Como executar uma pesquisa e filtro em tempo real em uma tabela HTML


139

Estou pesquisando e pesquisando no Stack Overflow há algum tempo, mas simplesmente não consigo contornar esse problema.

Eu tenho uma tabela HTML padrão, contendo, digamos, frutas. Igual a:

<table>
   <tr>
      <td>Apple</td>
      <td>Green</td>
   </tr>
   <tr>
      <td>Grapes</td>
      <td>Green</td>
   </tr>
   <tr>
      <td>Orange</td>
      <td>Orange</td>
   </tr>
</table>

Acima disso, tenho uma caixa de texto, que gostaria de pesquisar na tabela conforme o usuário digita. Portanto, se digitarem, Grepor exemplo, a linha laranja da tabela desaparecerá, deixando a Apple e o Grapes. Se eles continuassem digitando, Green Gra linha da Apple deveria desaparecer, deixando apenas uvas. Espero que isso esteja claro.

E, se o usuário excluir parte ou toda a sua consulta da caixa de texto, gostaria que todas as linhas que agora correspondem à consulta reapareçam.

Embora eu saiba como remover uma linha da tabela no jQuery, tenho pouca idéia de como proceder para pesquisar e remover linhas seletivamente com base nisso. Existe uma solução simples para isso? Ou um plugin?

Se alguém pudesse me apontar na direção certa, seria brilhante.

Obrigado.


Respostas:


307

Eu criei esses exemplos.

Simples indexOf pesquisa

var $rows = $('#table tr');
$('#search').keyup(function() {
    var val = $.trim($(this).val()).replace(/ +/g, ' ').toLowerCase();

    $rows.show().filter(function() {
        var text = $(this).text().replace(/\s+/g, ' ').toLowerCase();
        return !~text.indexOf(val);
    }).hide();
});

Demonstração : http://jsfiddle.net/7BUmG/2/

Pesquisa de expressão regular

Funcionalidades mais avançadas, usando expressões regulares, permitem pesquisar palavras em qualquer ordem na linha. Funcionará da mesma forma se você digitar apple greenou green apple:

var $rows = $('#table tr');
$('#search').keyup(function() {

    var val = '^(?=.*\\b' + $.trim($(this).val()).split(/\s+/).join('\\b)(?=.*\\b') + ').*$',
        reg = RegExp(val, 'i'),
        text;

    $rows.show().filter(function() {
        text = $(this).text().replace(/\s+/g, ' ');
        return !reg.test(text);
    }).hide();
});

Demonstração : http://jsfiddle.net/dfsq/7BUmG/1133/

Debounce

Ao implementar a filtragem de tabela com pesquisa em várias linhas e colunas, é muito importante considerar o desempenho e a velocidade / otimização da pesquisa. Simplesmente dizendo que você não deve executar a função de pesquisa a cada pressionamento de tecla, não é necessário. Para impedir que a filtragem seja executada com muita frequência, você deve devolvê-la. O exemplo de código acima se tornará:

$('#search').keyup(debounce(function() {
    var val = $.trim($(this).val()).replace(/ +/g, ' ').toLowerCase();
    // etc...
}, 300));

Você pode escolher qualquer implementação de debounce, por exemplo, no Lodash _.debounce , ou pode usar algo muito simples como eu uso nas próximas demos (debounce daqui ): http://jsfiddle.net/7BUmG/6230/ e http: / /jsfiddle.net/7BUmG/6231/ .


3
Eu sou muito verde com essas coisas, mas se eu quiser incorporar isso na minha mesa, eu só preciso mudar #tablepara a idda minha mesa? Precisaria haver alterações adicionais para trabalhar <thead>e <tbody>tags? Incluí o script e o html no link jsfiddle, alterando o #id, mas não recebo filtragem.
31713 JoshP

10
@JoshP Sctipt funciona com todas as linhas. Se você deseja filtrar apenas os que estão dentro, <tbody>deve alterar para var $rows = $('#id-of-your-table tbody tr');.
Dfsq 31/05

2
@ Josh Não, é necessário apenas o jQuery. Apenas certifique-se de executar seu código no DOMReady ou após o carregamento do HTML.
Dfsq 31/05

2
Eu recomendaria aprimorar essa abordagem porque consome bastante recursos. Coloque todas as seqüências refinadas em uma matriz de objetos com dois campos: uma referência ao <tr>DOMElement e a sequência. Dessa forma, keyup()você pesquisa essas strings (que são muito mais rápidas) e tem as linhas correspondentes prontas para serem manipuladas. Esse primeiro procedimento de configuração caro deve ser executado apenas uma vez, logo após o carregamento. Todas essas mudanças são apenas pequenas correções, a parte central real ainda permanece como mostrado nesta resposta. Essa abordagem também é possível e muito fácil de implementar sem o jQuery.
pid

2
@confusedMind Use o $('#table tr:not(:first)')seletor.
dfsq

10

Eu tenho um plugin jquery para isso. Ele usa jquery-ui também. Você pode ver um exemplo aqui http://jsfiddle.net/tugrulorhan/fd8KB/1/

$("#searchContainer").gridSearch({
            primaryAction: "search",
            scrollDuration: 0,
            searchBarAtBottom: false,
            customScrollHeight: -35,
            visible: {
                before: true,
                next: true,
                filter: true,
                unfilter: true
            },
            textVisible: {
                before: true,
                next: true,
                filter: true,
                unfilter: true
            },
            minCount: 2
        });

8

Aqui está a melhor solução para pesquisar dentro da tabela HTML, cobrindo toda a tabela (todos td, tr na tabela), javascript puro e o mais curto possível:

<input id='myInput' onkeyup='searchTable()' type='text'>

<table id='myTable'>
   <tr>
      <td>Apple</td>
      <td>Green</td>
   </tr>
   <tr>
      <td>Grapes</td>
      <td>Green</td>
   </tr>
   <tr>
      <td>Orange</td>
      <td>Orange</td>
   </tr>
</table>

<script>
function searchTable() {
    var input, filter, found, table, tr, td, i, j;
    input = document.getElementById("myInput");
    filter = input.value.toUpperCase();
    table = document.getElementById("myTable");
    tr = table.getElementsByTagName("tr");
    for (i = 0; i < tr.length; i++) {
        td = tr[i].getElementsByTagName("td");
        for (j = 0; j < td.length; j++) {
            if (td[j].innerHTML.toUpperCase().indexOf(filter) > -1) {
                found = true;
            }
        }
        if (found) {
            tr[i].style.display = "";
            found = false;
        } else {
            tr[i].style.display = "none";
        }
    }
}
</script>

3
Para proteger a linha do cabeçalho da tabela de desaparecer, adicione id à linha como: <tr id = 'tableHeader'> e altere a instrução else else para: if (tr [i] .id! = 'TableHeader') {tr [i ] .style.display = "none";} Não é mencionado na pergunta, mas eu queria que ele cobrisse isso para torná-lo abrangente.
Tarik

Em vez de comparar o ID usando! =, Sugiro alterar o final else para este:} else if (! Tr [i] .id.match ('^ tableHeader')) {Isso permite que você tenha mais de uma tabela, cada com seu próprio cabeçalho. É necessário mais trabalho para parametrizar a função searchTable, passando o ID da tabela.
Tom Ekberg #

3

Obrigado @dfsq pelo código muito útil!

Fiz alguns ajustes e talvez alguns outros também. Garanto que você pode procurar várias palavras, sem ter uma correspondência estrita.

Linhas de exemplo:

  • Maçãs e peras
  • Maçãs e Bananas
  • Maçãs e laranjas
  • ...

Você poderia procurar por 'ap pe' e ele reconheceria a primeira linha.
Você poderia procurar por 'banana apple' e reconheceria a segunda linha.

Demo: http://jsfiddle.net/JeroenSormani/xhpkfwgd/1/

var $rows = $('#table tr');
$('#search').keyup(function() {
  var val = $.trim($(this).val()).replace(/ +/g, ' ').toLowerCase().split(' ');

  $rows.hide().filter(function() {
    var text = $(this).text().replace(/\s+/g, ' ').toLowerCase();
    var matchesSearch = true;
    $(val).each(function(index, value) {
      matchesSearch = (!matchesSearch) ? false : ~text.indexOf(value);
    });
    return matchesSearch;
  }).show();
});

Pesquisa sólida - tive que modificá-lo um pouco para evitar que os cabeçalhos e rodapés da minha tabela desaparecessem, alterando: var $rows = $('#WorldPlayersTable tr'); para - var $rows = $('#WorldPlayersTable tbody tr');
Drefetr 29/06/17

2

Achei a resposta do dfsq extremamente útil. Fiz algumas pequenas modificações aplicáveis ​​a mim (e estou postando aqui, caso seja de alguma utilidade para outras pessoas).

  1. Usando classcomo ganchos, em vez de elementos da tabelatr
  2. Pesquisando / comparando texto em uma criança classenquanto mostra / oculta os pais
  3. Tornando-o mais eficiente, armazenando os $rowselementos de texto em uma matriz apenas uma vez (e evitando o $rows.lengthcálculo de tempos)

var $rows = $('.wrapper');
var rowsTextArray = [];

var i = 0;
$.each($rows, function() {
  rowsTextArray[i] = $(this).find('.fruit').text().replace(/\s+/g, ' ').toLowerCase();
  i++;
});

$('#search').keyup(function() {
  var val = $.trim($(this).val()).replace(/ +/g, ' ').toLowerCase();
  $rows.show().filter(function(index) {
    return (rowsTextArray[index].indexOf(val) === -1);
  }).hide();
});
span {
  margin-right: 0.2em;
}
<input type="text" id="search" placeholder="type to search" />

<div class="wrapper"><span class="number">one</span><span class="fruit">apple</span></div>
<div class="wrapper"><span class="number">two</span><span class="fruit">banana</span></div>
<div class="wrapper"><span class="number">three</span><span class="fruit">cherry</span></div>
<div class="wrapper"><span class="number">four</span><span class="fruit">date</span></div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>


2

Solução Javascript Pura:

Funciona para TODAS as colunas e não diferencia maiúsculas de minúsculas:

function search_table(){
  // Declare variables 
  var input, filter, table, tr, td, i;
  input = document.getElementById("search_field_input");
  filter = input.value.toUpperCase();
  table = document.getElementById("table_id");
  tr = table.getElementsByTagName("tr");

  // Loop through all table rows, and hide those who don't match the search query
  for (i = 0; i < tr.length; i++) {
    td = tr[i].getElementsByTagName("td") ; 
    for(j=0 ; j<td.length ; j++)
    {
      let tdata = td[j] ;
      if (tdata) {
        if (tdata.innerHTML.toUpperCase().indexOf(filter) > -1) {
          tr[i].style.display = "";
          break ; 
        } else {
          tr[i].style.display = "none";
        }
      } 
    }
  }
}

1

você pode usar javascript nativo como este

<script>
function myFunction() {
  var input, filter, table, tr, td, i;
  input = document.getElementById("myInput");
  filter = input.value.toUpperCase();
  table = document.getElementById("myTable");
  tr = table.getElementsByTagName("tr");
  for (i = 0; i < tr.length; i++) {
    td = tr[i].getElementsByTagName("td")[0];
    if (td) {
      if (td.innerHTML.toUpperCase().indexOf(filter) > -1) {
        tr[i].style.display = "";
      } else {
        tr[i].style.display = "none";
      }
    }       
  }
}
</script>



-1

Se você pode separar html e dados, pode usar bibliotecas externas, como tabelas de dados ou a que criei. https://github.com/thehitechpanky/js-bootstrap-tables

Esta biblioteca usa a função keyup para recarregar os dados tabled e, portanto, parece funcionar como pesquisa.

function _addTableDataRows(paramObjectTDR) {
    let { filterNode, limitNode, bodyNode, countNode, paramObject } = paramObjectTDR;
    let { dataRows, functionArray } = paramObject;
    _clearNode(bodyNode);
    if (typeof dataRows === `string`) {
        bodyNode.insertAdjacentHTML(`beforeend`, dataRows);
    } else {
        let filterTerm;
        if (filterNode) {
            filterTerm = filterNode.value.toLowerCase();
        }
        let serialNumber = 0;
        let limitNumber = 0;
        let rowNode;
        dataRows.forEach(currentRow => {
            if (!filterNode || _filterData(filterTerm, currentRow)) {
                serialNumber++;
                if (!limitNode || limitNode.value === `all` || limitNode.value >= serialNumber) {
                    limitNumber++;
                    rowNode = _getNode(`tr`);
                    bodyNode.appendChild(rowNode);
                    _addData(rowNode, serialNumber, currentRow, `td`);
                }
            }
        });
        _clearNode(countNode);
        countNode.insertAdjacentText(`beforeend`, `Showing 1 to ${limitNumber} of ${serialNumber} entries`);
    }
    if (functionArray) {
        functionArray.forEach(currentObject => {
            let { className, eventName, functionName } = currentObject;
            _attachFunctionToClassNodes(className, eventName, functionName);
        });
    }
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.