Search code examples
javascripthtmlcssfilterwidth

Filtering a table by element's name


I'm trying to filter this HTML table by writing some text in a search form, which works great but whenever it filters, the elements try to fill the whole width of the table instead of keeping their original element width (25% of the table's width, without counting the spaces between the cells).

function searchFilter () {
   const input = document.getElementById('myInput');
   const filter = input.value.toUpperCase();
   const table = document.getElementById('tablaPiola');
   const articule = table.getElementsByTagName('td');

   for (i = 0; i < articule.length; i++) {
      a = articule[i].getElementsByTagName("a")[0];
      txtValue = a.textContent || a.innerText;
      if (txtValue.toUpperCase().indexOf(filter) > -1) {
         articule[i].style.display = "";
         } else {
         articule[i].style.display = "none";
         }
   }
}

document.getElementById("filter-btn").addEventListener("click", searchFilter);
body {
background-color: black;
}

table
{border-spacing: 20px;
table-layout: fixed;
width: 600px;
margin: auto;
margin-top: 10px;}

td
{text-align: center;
border-radius: 10px;}

td a
{width: 100%;
display: block;
line-height: 50px;
text-decoration: none;
font-family: "Poppins";
font-weight: 700;
font-size: 12px;
color: white;}

.automatizaciones
{background-image: url("https://via.placeholder.com/100/0000FF/0000FF");
background-size: cover;}

.bpm_a_ms
{background-image: url("https://via.placeholder.com/100/0000FF/0000FF");
background-size: cover;}

.compresion
{background-image: url("https://via.placeholder.com/100/0000FF/0000FF");
background-size: cover;}

.compresion_multibanda
{background-image: url("https://via.placeholder.com/100/0000FF/0000FF");
background-size: cover;}
<input type="text" id="myInput">
<input type="button" id="filter-btn" value="Apply">
<table class="tabla_basico" id="tablaPiola">
   <tr>
      <td class="automatizaciones">
         <div class="overlay_basico"><a href="/">AUTOMATIZACIONES</a></div>
      </td>
      <td class="bpm_a_ms">
         <div class="overlay_intermedio"><a href="/">BPM A MS</a></div>
      </td>
      <td class="compresion">
         <div class="overlay_basico"><a href="compresion.html">COMPRESIÓN</a></div>
      </td>
      <td class="compresion_multibanda">
         <div class="overlay_intermedio"><a href="/">COMPRESIÓN MULTIBANDA</a></div>
       </td>
   </tr>
</table>

This is my webpage

This is how it filters


Solution

  • I originally thought visibility: hidden but you pointed out that that means the ones that are showing aren't on the left anymore.

    My only other thought is adding padding cells at the end:

    function searchFilter () {
       const input = document.getElementById('myInput');
       const filter = input.value.toUpperCase();
       const table = document.getElementById('tablaPiola');
       // Remove any fillers we added last time
       table.querySelectorAll(".filler").forEach(filler => {
           filler.parentNode.removeChild(filler);
       });
       const articule = table.getElementsByTagName('td');
       // Remember how many are showing at the end
       let showing = 0;
    
       for (i = 0; i < articule.length; i++) {
          a = articule[i].getElementsByTagName("a")[0];
          txtValue = a.textContent || a.innerText;
          if (txtValue.toUpperCase().indexOf(filter) > -1) {
             articule[i].style.display = "";
             ++showing; // Remember we're showing this one
          } else {
             articule[i].style.display = "none";
          }
       }
       // Grab the length (since `articule` is a live list
       const max = articule.length;
       // Add blank cells to the end
       while (showing < max) {
          const filler = document.createElement("td");
          filler.className = "filler";
          table.appendChild(filler);
          ++showing;
       }
    }
    

    Live Example:

    function searchFilter () {
       const input = document.getElementById('myInput');
       const filter = input.value.toUpperCase();
       const table = document.getElementById('tablaPiola');
       // Remove any fillers we added last time
       table.querySelectorAll(".filler").forEach(filler => {
           filler.parentNode.removeChild(filler);
       });
       const articule = table.getElementsByTagName('td');
       // Remember how many are showing at the end
       let showing = 0;
       
       for (i = 0; i < articule.length; i++) {
          a = articule[i].getElementsByTagName("a")[0];
          txtValue = a.textContent || a.innerText;
          if (txtValue.toUpperCase().indexOf(filter) > -1) {
             articule[i].style.display = "";
             ++showing; // Remember we're showing this one
          } else {
             articule[i].style.display = "none";
          }
       }
       // Grab the length (since `articule` is a live list
       const max = articule.length;
       // Add blank cells to the end
       while (showing < max) {
          const filler = document.createElement("td");
          filler.className = "filler";
          table.appendChild(filler);
          ++showing;
       }
    }
    
    document.getElementById("filter-btn").addEventListener("click", searchFilter);
    body {
    background-color: black;
    }
    
    table
    {border-spacing: 20px;
    table-layout: fixed;
    width: 1200px;
    margin: auto;
    margin-top: 100px;}
    
    td
    {text-align: center;
    border-radius: 10px;}
    
    td a
    {width: 100%;
    display: block;
    line-height: 150px;
    text-decoration: none;
    font-family: "Poppins";
    font-weight: 700;
    font-size: 17.5px;
    color: white;}
    
    .automatizaciones
    {background-image: url(imagenes/basico/automatizaciones/articulo.jpg);
    background-size: cover;}
    
    .bpm_a_ms
    {background-image: url(imagenes/intermedio/bpm_a_ms/articulo.jpg);
    background-size: cover;}
    
    .compresion
    {background-image: url(imagenes/basico/compresion/articulo.jpg);
    background-size: cover;}
    
    .compresion_multibanda
    {background-image: url(imagenes/intermedio/compresion_multibanda/articulo.jpg);
    background-size: cover;}
    <input type="text" id="myInput">
    <input type="button" id="filter-btn" value="Apply">
    <table class="tabla_basico" id="tablaPiola">
       <tr>
          <td class="automatizaciones">
             <div class="overlay_basico"><a href="/">AUTOMATIZACIONES</a></div>
          </td>
          <td class="bpm_a_ms">
             <div class="overlay_intermedio"><a href="/">BPM A MS</a></div>
          </td>
          <td class="compresion">
             <div class="overlay_basico"><a href="compresion.html">COMPRESIÓN</a></div>
          </td>
          <td class="compresion_multibanda">
             <div class="overlay_intermedio"><a href="/">COMPRESIÓN MULTIBANDA</a></div>
           </td>
       </tr>
    </table>


    Some other notes not directly related to the main problem:

    • That code falls prey to what I call The Horror of Implicit Globals. You never declare i, a, or txtValue so you end up creating global variables when you assign to them. I recommend using strict mode in all code so you get an error when you try to do that.

    • FWIW, I see you're using ES2015+ features (const, for instance), so you could use a for-of loop instead of a for loop. for-of is a bit less typing. But you may need to polyfill iterability on some platforms. My answer here shows how to do that.