steve
steve

Reputation: 362

HTML Table search on each column Individually using JavaScript

I have a table with 5 columns. the following code will filter the data on basis of all columns. I want to filter the data for each column. for ex: if there are 10 columns then 10 search fields and Also how can I make HTML part Dynamic so that I don't have to add one more search text field whenever a new column is added.

<input id="myInput" type="text"  />

<script>
   function filterTable(event) {
    var filter = event.target.value.toUpperCase();
    var rows = document.querySelector("#myTable tbody").rows;

    for (var i = 1; i < rows.length; i++) {
        var Col1 = rows[i].cells[0].textContent.toUpperCase();
        var Col2 = rows[i].cells[1].textContent.toUpperCase();
        var Col3 = rows[i].cells[2].textContent.toUpperCase();
        var Col4 = rows[i].cells[3].textContent.toUpperCase();
        var Col5 = rows[i].cells[4].textContent.toUpperCase();
        if (Col1.indexOf(filter) > -1 || Col2.indexOf(filter) > -1 || Col3.indexOf(filter) > -1
          || Col4.indexOf(filter) > -1 || Col5.indexOf(filter) > -1) {
            rows[i].style.display = "";
        } else {
            rows[i].style.display = "none";
        }      
    }
}

document.querySelector(''#myInput'').addEventListener(''keyup'', filterTable, false);
</script>

I want to have this kind of functionality:

Upvotes: 1

Views: 2240

Answers (3)

Mr. Polywhirl
Mr. Polywhirl

Reputation: 48751

You can loop over the rows and then the columns. The Array.from method allows you to cast an element list to an array so that you can iterate over the children with Array.prototype.forEach.

All you need to do is have a show initialized to true for each row. Then if any DOES NOT column meets the filter criteria, you set show to false. After looping through all columns you display the row based on the final value of show.

Edit: Make sure you are using a browser that supports ES6+. There is a polyfill available for Array.from on the MDN site.

function filterTable(event) {
  let filter = event.target.value.trim().toLowerCase();
  let rows = document.querySelector('#myTable tbody').rows;
  for (let i = 0; i < rows.length; i++) {
    let row = rows[i], show = false;
    if (filter.length > 0) {
      for (let j = 0; j < row.children.length; j++) {
        let col = row.children[j], text = col.textContent.toLowerCase();
        if (text.indexOf(filter) > -1) {
          show = true;
          continue;
        }
      }
    } else {
      show = true;
    }
    // Avoid using 'row.styledisplay' - https://stackoverflow.com/a/28028656/1762224
    // Avoid using 'row.visibility'   - rows do not collapse
    toggleClass(row, 'hidden-row', !show);
  }
}

function toggleClass(el, className, state) {
  if (el.classList) el.classList.toggle(className, state);
  else {
    var classes = el.className.split(' ');
    var existingIndex = classes.indexOf(className);
    if (state === undefined) {
      if (existingIndex > -1) classes.splice(existingIndex, 1)
      else classes.push(existingIndex);
    } else {
      if (!state) classes.splice(existingIndex, 1)
      else classes.push(existingIndex);
    }
    el.className = classes.join(' ');
  }
}

document.querySelector('#myInput').addEventListener('keyup', filterTable, false);
body {
  padding: 8px;
}
.field label {
  font-weight: bold;
  margin-right: 0.25em;
}
#myTable {
  margin-top: 0.667em;
  width: 100%;
}
#myTable th {
  text-transform: capitalize;
}
.hidden-row {
  display: none;
}
<link href="https://unpkg.com/[email protected]/build/pure-min.css" rel="stylesheet" />
<div class="field"><label for="myInput">Filter:</label><input id="myInput" type="text" /></div>
<table id="myTable" class="pure-table pure-table-horizontal">
  <thead>
    <tr>
      <th>name</th>
      <th>drink</th>
      <th>pizza</th>
      <th>movie</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Homer</td>
      <td>Squishie</td>
      <td>Magheritta</td>
      <td>The Avengers</td>
    </tr>
    <tr>
      <td>Marge</td>
      <td>Squishie</td>
      <td>Magheritta</td>
      <td>The Avengers</td>
    </tr>
    <tr>
      <td>Bart</td>
      <td>Squishie</td>
      <td>Pepperoni</td>
      <td>Black Dynamite</td>
    </tr>
    <tr>
      <td>Lisa</td>
      <td>Buzz Cola</td>
      <td>Pepperoni</td>
      <td>Iron Man</td>
    </tr>
    <tr>
      <td>Maggie</td>
      <td>Duff Beer</td>
      <td>Magheritta</td>
      <td>The Avengers</td>
    </tr>
    <tr>
      <td>Kent</td>
      <td>Duff Beer</td>
      <td>Hawaiian</td>
      <td>The Avengers</td>
    </tr>
  </tbody>
</table>


Search fields example

populateTable(document.getElementById('simpsons'), getData());

function dataFields(data) {
  return data.reduce((r, x) => Object.keys(x).reduce((s, k) => s.indexOf(k) === -1 ? s.concat(k) : s, r), []);
}

/* Can be useful if working with raw JSON data */
function searchCriteria(fields) {
  return Array.from(fields).reduce((o, field) => {
    return Object.assign(o, { [field.getAttribute('placeholder')] : field.value });
  }, {});
}

function onFilter(e) {
  let table = e.target.parentElement.parentElement.parentElement.parentElement;
  let fields = table.querySelectorAll('thead tr th input');
  let criteria = searchCriteria(fields); // Unused, but useful if filtering bindable data
  let searchText = Array.from(fields).map(field => field.value.trim());
  Array.from(table.querySelectorAll('tbody tr')).forEach(row => {
    let hideRow = false;
    Array.from(row.children).forEach((col, index) => {
      var value = col.innerHTML.trim().toLowerCase();
      var search = searchText[index].toLowerCase();
      if (search.length > 0) {
        if (!value.startsWith(search)) { /* or value.indexOf(search) === -1 */
          hideRow = true;
          return;
        }
      }
    });
    row.classList.toggle('hidden-row', hideRow);
  });
}

function populateTable(table, data) {
  let fields = dataFields(data);
  let thead = document.createElement('THEAD');
  let tr = document.createElement('TR');
  fields.forEach(field => {
    let th = document.createElement('TH');
    th.innerHTML = field;
    tr.appendChild(th);
  });
  thead.appendChild(tr);
  let tbody = document.createElement('TBODY');
  tr = document.createElement('TR');
  fields.forEach(field => {
    let th = document.createElement('TH');
    let input = document.createElement('INPUT');
    input.setAttribute('placeholder', field);
    input.addEventListener('keyup', onFilter);
    th.append(input);
    tr.appendChild(th);
  });
  thead.appendChild(tr);
  data.forEach(record => {
    let tr = document.createElement('TR');
    fields.forEach(field => {
      let td = document.createElement('TD');
      td.innerHTML = record[field];
      tr.appendChild(td);
    });
    tbody.append(tr);
  });
  table.appendChild(thead);
  table.appendChild(tbody);
}

function getData() {
  return [{
    "name": "Homer",
    "drink": "Squishie",
    "pizza": "Magheritta",
    "movie": "The Avengers"
  }, {
    "name": "Marge",
    "drink": "Squishie",
    "pizza": "Magheritta",
    "movie": "The Avengers"
  }, {
    "name": "Bart",
    "drink": "Squishie",
    "pizza": "Pepperoni",
    "movie": "Black Dynamite"
  }, {
    "name": "Lisa",
    "drink": "Buzz Cola",
    "pizza": "Pepperoni",
    "movie": "Iron Man"
  }, {
    "name": "Maggie",
    "drink": "Duff Beer",
    "pizza": "Magheritta",
    "movie": "The Avengers"
  }, {
    "name": "Kent",
    "drink": "Duff Beer",
    "pizza": "Hawaiian",
    "movie": "The Avengers"
  }];
}
table {
  border-collapse: collapse;
  width: 100%;
}
table thead tr th {
  text-transform: capitalize;
}
table thead tr:last-child {
  background: #eaeaea;
  border-bottom: 4px double #cbcbcb;
}
table thead tr th input {
  width: 100%;
}
table tbody tr:nth-child(even) {
  background-color: #f2f2f2;
}
table tbody tr.hidden-row {
  display: none;
}
<link href="https://unpkg.com/[email protected]/build/pure-min.css" rel="stylesheet" />
<table id="simpsons" class="pure-table pure-table-horizontal"></table>


Browser Compatibility

function onFilter(e) {
  var table = e.target.parentElement.parentElement.parentElement.parentElement;
  console.log(table);
  var fields = table.querySelectorAll('thead tr th input');
  console.log(fields);
  var searchText = Array.from(fields).map(function (field) {
    return field.value.trim();
  });
  console.log(searchText);
  Array.from(table.querySelectorAll('tbody tr')).forEach(function (row) {
    var hideRow = false;
    Array.from(row.children).forEach(function (col, index) {
      var value = col.innerHTML.trim().toLowerCase();
      console.log(value);
      var search = searchText[index].toLowerCase();
      console.log(search);
      if (search.length > 0) {
        if (value.indexOf(search) === -1) {
          hideRow = true;
          return;
        }
      }
    });
    row.classList.toggle('hidden-row', hideRow);
  });
}

Upvotes: 1

Code Maniac
Code Maniac

Reputation: 37775

You can use array. First map col to an array colArr and than using some you can match you can match filter content with content of cols .

for (var i = 1; i < rows.length; i++) {
  let colArr = new Array(5).fill(0).map((e,index)=> rows[i].cells[index].textContent.toUpperCase();) 
     if (colArr.some(e=> e.indexOf(filter) > -1 ) {
         rows[i].style.display = "";
        } else {
          rows[i].style.display = "none";
        }
    }) 
}

Upvotes: 0

S. Walker
S. Walker

Reputation: 2162

All you have to do is iterate the .cells array with a for loop:

For this example, I used a variable to determine if the row should be shown.

function filterTable(event) {
  var filter = event.target.value.toUpperCase();
  var rows = document.querySelector("#myTable tbody").rows;

  for (var i = 1; i < rows.length; i++) {
    // Placeholder to indicate if a row matches the given query.
    var shouldShowRow = false;
    // Loop over all the cells in this row.
    for (var k = 0; k < rows[i].cells.length) {
      // Check to see if this cell in this row matches the query.
      if (rows[i].cells[k].textContent.toUpperCase().indexOf(filter) > -1) {
        // Yes, this cell matches, therefore this entire row matches.
        // Flip the show row boolean to indicate that we need to show.
        shouldShowRow = true;
        // Because this cell matches, we do not need to check any more cells.
        break;
      }
    }
    // Change the display on the row if we need to.
    if (shouldShowRow) {
      rows[i].style.display = "";
    } else rows[i].style.display = "none";
  }
}

Upvotes: 1

Related Questions