【问题标题】:Filter multiple table columns过滤多个表列
【发布时间】:2020-01-20 15:40:29
【问题描述】:

我正在使用来自Framework7data-table,它是使用 JSON 数据动态构建的,我正在尝试使 column filter input 正常工作。使用 Stack Overflow 上显示的其他示例,我已经能够成功过滤第一列:

HTML

<input type="text" id="brandFilter" onkeyup="filterTable()" placeholder="Filter">

JS

function filterTable() {  
  var input, filter, table, tr, td, i, txtValue;
  input = document.getElementById("brandFilter");
  filter = input.value.toUpperCase();
  table = document.getElementById("productList");
  tr = table.getElementsByTagName("tr");  
  for (i = 0; i < tr.length; i++) {
    td = tr[i].getElementsByTagName("td")[0];
    if (td) {
      txtValue = td.textContent || td.innerText;
      if (txtValue.toUpperCase().indexOf(filter) > -1) {
        tr[i].style.display = "";
      } else {
        tr[i].style.display = "none";
      }
    }
  }
}

我不知道如何改进此脚本,以便可以使用第 2 列和第 3 列的输入进一步过滤表格?

<input type="text" id="modelFilter" onkeyup="filterTable()" placeholder="Filter">
<input type="text" id="energyFilter" onkeyup="filterTable()" placeholder="Filter">

我还是个新手,有什么简单的解决方案吗?

【问题讨论】:

    标签: javascript filter html-table datatable html-framework-7


    【解决方案1】:

    您可以根据列位置构建术语列表并存储输入类型和值。

    然后,您可以遍历每一行,并根据每个单元格的文本内容及其对应的术语来确定是否要显示它。

    function onFilterChange(e) {
      let table = findParentBySelector(e.target, 'table');
      var terms = [].slice.call(table.querySelectorAll('.input')).map(el => {
        let item = el.querySelector('input, select');
        return { type : item.tagName, value : item.value };
      });
      table.querySelectorAll('tbody tr').forEach(tr => {
        let visible = [].slice.call(tr.querySelectorAll('td')).every((td, i) => {
          if (terms[i].type === 'SELECT' && terms[i].value === 'All') return true;
          let text = td.innerHTML.trim();
          return text != '' ? text.indexOf(terms[i].value) > -1 : true;
        });
        tr.classList[visible ? 'remove' : 'add']('hidden-row');
      });
    }
    

    演示

    var data = `2,Jane Doe,jane.doe@com,Female
    3,Vladimir Kharlampidi,vladimir@google.com,Male
    4,Jennifer Doe,jennifer@doe.com,Female`.split('\n');
    
    let tbody = document.querySelector('.data-table table tbody')
    data.forEach(r => {
      let tr = document.createElement('TR');
      r.split(/,\s*?/g).forEach(c => {
        let td = document.createElement('TD');
        td.innerHTML = c;
        tr.appendChild(td);
      });
      tbody.appendChild(tr);
    });
    
    document.querySelectorAll('.input').forEach(el => {
      el.querySelectorAll('input').forEach(x => x.addEventListener('keyup', onFilterChange));
      el.querySelectorAll('*').forEach(x => x.addEventListener('change', onFilterChange));
    });
    
    function onFilterChange(e) {
      let table = findParentBySelector(e.target, 'table'),
        caseInsensitive = table.getAttribute('data-ignore-case') === 'true',
        terms = [].slice.call(table.querySelectorAll('.input')).map(el => {
          let item = el.querySelector('input, select');
          return {
            type : item.tagName,
            value : (v => caseInsensitive ? v.toLowerCase() : v)(item.value)
          };
        });
      table.querySelectorAll('tbody tr').forEach(tr => {
        let visible = [].slice.call(tr.querySelectorAll('td')).every((td, i) => {
          let text = (t => caseInsensitive ? t.toLowerCase() : t)(td.textContent.trim());
          if (terms[i].type === 'SELECT') {
           if (terms[i].value.toUpperCase() === 'ALL') return true;
           return terms[i].value === text;
          }
          return text != '' ? text.indexOf(terms[i].value) > -1 : true;
        });
        tr.classList.toggle('hidden-row', !visible);
      });
    }
    
    // https://stackoverflow.com/a/14234618/1762224
    // Note: Modified as a recursive routine
    function findParentBySelector(el, target) {
      return el != null ? el && ![].slice.call(typeof target === 'string' ? (target = document.querySelectorAll(target)) : target).some(c => c == el) ? findParentBySelector(el.parentNode, target) : el : null;
    }
    body {
      background: #444 !important;
    }
    
    .data-table {
      width: 96% !important;
      margin: 1em auto !important;
      padding: 0.5em;
    }
    
    /** Toggled */
    .hidden-row {
      display: none;
    }
    <link href="https://cdnjs.cloudflare.com/ajax/libs/framework7/4.5.0/css/framework7.bundle.min.css" rel="stylesheet" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/framework7/4.5.0/js/framework7.bundle.min.js"></script>
    <div class="card data-table">
      <table data-ignore-case="true">
        <thead>
          <tr>
            <th class="input-cell">
              <span class="table-head-label">ID</span>
              <div class="input" style="width: 50px">
                <input type="number" placeholder="Filter">
              </div>
            </th>
            <th class="input-cell">
              <span class="table-head-label">Name</span>
              <div class="input">
                <input type="text" placeholder="Filter">
              </div>
            </th>
            <th class="input-cell">
              <span class="table-head-label">Email</span>
              <div class="input">
                <input type="text" placeholder="Filter">
              </div>
            </th>
            <th class="input-cell">
              <span class="table-head-label">Gender</span>
              <div class="input input-dropdown">
                <select>
                  <option value="All">All</option>
                  <option value="Male">Male</option>
                  <option value="Female">Female</option>
                </select>
              </div>
            </th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>1</td>
            <td>John Doe</td>
            <td>john@doe.com</td>
            <td>Male</td>
          </tr>
        </tbody>
      </table>
    </div>

    原始回复

    1. 为您的搜索字段添加一个带有数据属性的类:

      $('<input>').addClass('search-field').data('field', field))
      
    2. 在表格单元格中添加字段名称。

      $('<td>').text(rec[field]).data('field-name', field)
      
    3. 如果所有条件都满足每个单元格的文本内容,则使用术语实现搜索映射并过滤每一行。如果该列的术语为空,则假定为 true

      var $table = $.tableFromJson(data).addClass('stylized').appendTo('body');
      $table.on('keyup', '.search-field', function(e) {
        // Map fields to pairs and then reduce into a map.
        let terms = $('.search-field').map(function(i, field) {
          return { field : $(field).data('field'), value : $(field).val().trim() };
        }).toArray().reduce(function(terms, pair) {
          return Object.assign(terms, { [pair.field] : pair.value });
        }, {});
      
        let len = Object.keys(terms).length; // Store the length of the keys
      
        // Find the parent (table) of the search field that you modified and locate the rows
        $(e.target).closest('table').find('tbody tr').each(function(i, tr) {
          // Toggle the row visibility
          $(tr).toggle($(tr).find('td').filter(function(j, td) {
            let term =  terms[$(td).data('field-name')];
      
            // If not empty, locate the term within the text of the cell
            return term != '' ? $(td).text().trim().indexOf(term) > -1 : true;
          }).length === len);
        });
      });
      

    演示

    (function($) {
      $.reduce = function(arr, fnReduce, valueInitial) {
        if (Array.prototype.reduce) {
          return Array.prototype.reduce.call(arr, fnReduce, valueInitial);
        }
        $.each(arr, function(i, value) {
          valueInitial = fnReduce.call(null, valueInitial, value, i, arr);
        });
        return valueInitial;
      };
      $.fn.reduce = function(fnReduce, valueInitial) {
        return $.reduce(this, fnReduce, valueInitial);
      };
      $.fn.renderTable = function(data, options) {
        options = options || {};
        let ignoreCase = options.ignoreCase, fields = Object.keys(data[0]);
        return this.renderHeaders(fields).renderRows(fields, data)
          .on('keyup', '.search-field', function(e) {
            let terms = $('.search-field').reduce(function(m, field) {
                return Object.assign(m, {
                  [$(field).data('field')]: (function(val) {
                    return ignoreCase && val ? val.toLowerCase() : val;
                  })($(field).val().trim())
                });
              }, {}), len = Object.keys(terms).length;
            $(e.target).closest('table').find('tbody tr').each(function(i, tr) {
              $(tr).toggle($(tr).find('td').filter(function(j, td) {
                let term = terms[$(td).data('field-name')];
                return term != '' ? (function(text) {
                  return ignoreCase && text ? text.toLowerCase() : text;
                })($(td).text().trim()).indexOf(term) > -1 : true;
              }).length === len);
            });
          });
      };
      $.fn.renderHeaders = function(fields) {
        return this.append($.renderHeaders(fields));
      }
      $.fn.renderRows = function(fields, data) {
        return this.append($.renderRows(fields, data));
      };
      $.tableFromJson = function(data, options) {
        return $('<table>').renderTable(data, options)
          .toggleClass('stylized', (options || {}).stylized);
      };
      $.renderHeaders = function(fields) {
        return $('<thead>').append($('<tr>').append(fields
          .map(field => $('<th>')
            .append($('<div>').text(field))
            .append($('<input>').addClass('search-field').data('field', field)))));
      };
      $.renderRows = function(fields, data) {
        return $('<tbody>').append(data
          .map((rec, row) => $('<tr>').append(fields
            .map((field, col) => $('<td>').text(rec[field]).data('field-name', field)))));
      };
    })(jQuery);
    
    var data = [
      { "brand" : "Bosch", "model" : 333, "energyUse" : "A"   },
      { "brand" : "Bosch", "model" : 456, "energyUse" : "A++" }
    ];
    
    $.tableFromJson(data, { ignoreCase: true, stylized: true }).appendTo('body');
    body {
      padding: 0.25em;
    }
    
    h1 {
      font-weight: bold;
      margin-top: 0.75em;
      margin-bottom: 0.33em;
    }
    
    table.stylized {
      font-family: "Lucida Sans Unicode", "Lucida Grande", Sans-Serif;
      font-size: 12px;
      text-align: left;
      border-collapse: collapse;
      margin: 4px;
      width: 600px;
    }
    
    table.stylized thead th {
      text-transform: capitalize;
      font-size: 13px;
      color: #039;
      background: #b9c9fe;
      padding: 6px;
      cursor: pointer;
    }
    
    table.stylized thead th input {
      background: #f2f5ff;
      color: #039;
      font-size: smaller;
    }
    
    table.stylized tbody tr:nth-child(odd) {
      background: #f2f5ff;
    }
    
    table.stylized tbody tr:nth-child(even) {
      background: #e8edff;
    }
    
    table.stylized tbody td {
      border-top: 1px solid #fff;
      color: #669;
      padding: 6px;
    }
    
    table.stylized tbody tr:hover td {
      background: #d0dafd;
    }
    &lt;script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"&gt;&lt;/script&gt;

    注意:这个demo中的表格生成jQuery插件是基于我之前实现的一个sortable table plugin

    【讨论】:

      【解决方案2】:

      如果您想将过滤器更改为每一列,只需更改以下内容:

      function filterTable(column) {  // add a parameter
        var input, filter, table, tr, td, i, txtValue;
        input = document.getElementById("brandFilter");
        filter = input.value.toUpperCase();
        table = document.getElementById("productList");
        tr = table.getElementsByTagName("tr");  
        for (i = 0; i < tr.length; i++) {
        td = tr[i].getElementsByTagName("td")[0];// change this
        td = tr[i].getElementsByTagName("td")[column];// to this
          if (td) {
            txtValue = td.textContent || td.innerText;
            if (txtValue.toUpperCase().indexOf(filter) > -1) {
              tr[i].style.display = "";
            } else {
              tr[i].style.display = "none";
            }
          }
        }
      }
      

      这样你将列索引作为值传递给每列过滤

      希望对你有帮助

      【讨论】:

        【解决方案3】:

        @Mr. Polywhirl answer 的小修正 在他的回答中,如果名称列中的数据全部为空,则脚本将不起作用,因为所有数据都是空的
        示例:

        var data = `2,,jane.doe@com,Female
        3,,vladimir@google.com,Male
        4,,jennifer@doe.com,Female`.split('\n');
        
        let tbody = document.querySelector('.data-table table tbody')
        data.forEach(r => {
          let tr = document.createElement('TR');
          r.split(/,\s*?/g).forEach(c => {
            let td = document.createElement('TD');
            td.innerHTML = c;
            tr.appendChild(td);
          });
          tbody.appendChild(tr);
        });
        
        document.querySelectorAll('.input').forEach(el => {
          el.querySelectorAll('input').forEach(x => x.addEventListener('keyup', onFilterChange));
          el.querySelectorAll('*').forEach(x => x.addEventListener('change', onFilterChange));
        });
        
        function onFilterChange(e) {
          let table = findParentBySelector(e.target, 'table'),
            caseInsensitive = table.getAttribute('data-ignore-case') === 'true',
            terms = [].slice.call(table.querySelectorAll('.input')).map(el => {
              let item = el.querySelector('input, select');
              return {
                type : item.tagName,
                value : (v => caseInsensitive ? v.toLowerCase() : v)(item.value)
              };
            });
          table.querySelectorAll('tbody tr').forEach(tr => {
            let visible = [].slice.call(tr.querySelectorAll('td')).every((td, i) => {
              let text = (t => caseInsensitive ? t.toLowerCase() : t)(td.textContent.trim());
              if (terms[i].type === 'SELECT') {
               if (terms[i].value.toUpperCase() === 'ALL') return true;
               return terms[i].value === text;
              }
              return text != '' ? text.indexOf(terms[i].value) > -1 : true;
            });
            tr.classList.toggle('hidden-row', !visible);
          });
        }
        
        // https://stackoverflow.com/a/14234618/1762224
        // Note: Modified as a recursive routine
        function findParentBySelector(el, target) {
          return el != null ? el && ![].slice.call(typeof target === 'string' ? (target = document.querySelectorAll(target)) : target).some(c => c == el) ? findParentBySelector(el.parentNode, target) : el : null;
        }
        body {
          background: #444 !important;
        }
        
        .data-table {
          width: 96% !important;
          margin: 1em auto !important;
          padding: 0.5em;
        }
        
        /** Toggled */
        .hidden-row {
          display: none;
        }
        <link href="https://cdnjs.cloudflare.com/ajax/libs/framework7/4.5.0/css/framework7.bundle.min.css" rel="stylesheet" />
        <script src="https://cdnjs.cloudflare.com/ajax/libs/framework7/4.5.0/js/framework7.bundle.min.js"></script>
        <div class="card data-table">
          <table data-ignore-case="true">
            <thead>
              <tr>
                <th class="input-cell">
                  <span class="table-head-label">ID</span>
                  <div class="input" style="width: 50px">
                    <input type="number" placeholder="Filter">
                  </div>
                </th>
                <th class="input-cell">
                  <span class="table-head-label">Name</span>
                  <div class="input">
                    <input type="text" placeholder="Filter">
                  </div>
                </th>
                <th class="input-cell">
                  <span class="table-head-label">Email</span>
                  <div class="input">
                    <input type="text" placeholder="Filter">
                  </div>
                </th>
                <th class="input-cell">
                  <span class="table-head-label">Gender</span>
                  <div class="input input-dropdown">
                    <select>
                      <option value="All">All</option>
                      <option value="Male">Male</option>
                      <option value="Female">Female</option>
                    </select>
                  </div>
                </th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td>1</td>
                <td></td>
                <td>john@doe.com</td>
                <td>Male</td>
              </tr>
            </tbody>
          </table>
        </div>

        将这一行 return text != '' ? text.indexOf(terms[i].value) &gt; -1 : true; 改为 return text.indexOf(terms[i].value) &gt; -1; 即可。
        示例:

        var data = `2,,jane.doe@com,Female
        3,,vladimir@google.com,Male
        4,,jennifer@doe.com,Female`.split('\n');
        
        let tbody = document.querySelector('.data-table table tbody')
        data.forEach(r => {
          let tr = document.createElement('TR');
          r.split(/,\s*?/g).forEach(c => {
            let td = document.createElement('TD');
            td.innerHTML = c;
            tr.appendChild(td);
          });
          tbody.appendChild(tr);
        });
        
        document.querySelectorAll('.input').forEach(el => {
          el.querySelectorAll('input').forEach(x => x.addEventListener('keyup', onFilterChange));
          el.querySelectorAll('*').forEach(x => x.addEventListener('change', onFilterChange));
        });
        
        function onFilterChange(e) {
          let table = findParentBySelector(e.target, 'table'),
            caseInsensitive = table.getAttribute('data-ignore-case') === 'true',
            terms = [].slice.call(table.querySelectorAll('.input')).map(el => {
              let item = el.querySelector('input, select');
              return {
                type : item.tagName,
                value : (v => caseInsensitive ? v.toLowerCase() : v)(item.value)
              };
            });
          table.querySelectorAll('tbody tr').forEach(tr => {
            let visible = [].slice.call(tr.querySelectorAll('td')).every((td, i) => {
              let text = (t => caseInsensitive ? t.toLowerCase() : t)(td.textContent.trim());
              if (terms[i].type === 'SELECT') {
               if (terms[i].value.toUpperCase() === 'ALL') return true;
               return terms[i].value === text;
              }
              return text.indexOf(terms[i].value) > -1;
            });
            tr.classList.toggle('hidden-row', !visible);
          });
        }
        
        // https://stackoverflow.com/a/14234618/1762224
        // Note: Modified as a recursive routine
        function findParentBySelector(el, target) {
          return el != null ? el && ![].slice.call(typeof target === 'string' ? (target = document.querySelectorAll(target)) : target).some(c => c == el) ? findParentBySelector(el.parentNode, target) : el : null;
        }
        body {
          background: #444 !important;
        }
        
        .data-table {
          width: 96% !important;
          margin: 1em auto !important;
          padding: 0.5em;
        }
        
        /** Toggled */
        .hidden-row {
          display: none;
        }
        <link href="https://cdnjs.cloudflare.com/ajax/libs/framework7/4.5.0/css/framework7.bundle.min.css" rel="stylesheet" />
        <script src="https://cdnjs.cloudflare.com/ajax/libs/framework7/4.5.0/js/framework7.bundle.min.js"></script>
        <div class="card data-table">
          <table data-ignore-case="true">
            <thead>
              <tr>
                <th class="input-cell">
                  <span class="table-head-label">ID</span>
                  <div class="input" style="width: 50px">
                    <input type="number" placeholder="Filter">
                  </div>
                </th>
                <th class="input-cell">
                  <span class="table-head-label">Name</span>
                  <div class="input">
                    <input type="text" placeholder="Filter">
                  </div>
                </th>
                <th class="input-cell">
                  <span class="table-head-label">Email</span>
                  <div class="input">
                    <input type="text" placeholder="Filter">
                  </div>
                </th>
                <th class="input-cell">
                  <span class="table-head-label">Gender</span>
                  <div class="input input-dropdown">
                    <select>
                      <option value="All">All</option>
                      <option value="Male">Male</option>
                      <option value="Female">Female</option>
                    </select>
                  </div>
                </th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td>1</td>
                <td></td>
                <td>john@doe.com</td>
                <td>Male</td>
              </tr>
            </tbody>
          </table>
        </div>

        我想强调这是一个主观的变化,我更喜欢看到结果,即使是空字段

        【讨论】:

          猜你喜欢
          • 2018-01-25
          • 2019-01-28
          • 2019-05-04
          • 2017-09-23
          • 2013-07-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多