skip to Main Content

I have a HTML table generated with a <asp:GridView/> object via a VB.Net call that populates the table from a database query.

The result is a table like this:

<table id="myTab">
<tr><td>val1</td></tr>
<tr><td>val2</td></tr>
.
.
<tr><td>valN</td></tr>

The javascript code is on a button :

function toggleHide(val) {
   tb = getElementById("myTab");
   if ( tb != null ) {
      trows = tb.rows;
      for ( var i = 0; i< trows.length; i++ ) {
         if ( trows[i].cells[0].innertText == val ) {
            trows[i].style.display = 'none';
         }
         else {
            trows[i].style.display = '';
         }
      }
   }
   return false;
}

This works well with smaller volumes of data, but once we get over 100 it simply stops processing/crashes, leaving the page blank.

I get it that accessing the DOM repeatedly like this might be slow. is there an alternative faster way?

3

Answers


  1. If the values inside the cells are known before-hand, and limited, @C3roe’s comment is the best solution.

    Otherwise, if you’re looking for something dynamic for any text, try this:

    • Add the cell’s contents as a data-attribute for the row during content generation.
    • Add a dynamic style to the page to hide the cells containing the given text:

    'tr[data-txt*="'+txt+'"]{ display: none; }'

    Full example and related fiddle for 10.000 rows. Try it out with any size you want:

    function initDemoData(limit=1000){
        var fragment = document.createDocumentFragment();
        var thetable=document.createElement('table');
        for (var i=0;i<limit;i++){
            var tr=document.createElement('tr');
            var td=document.createElement('td');
            var txt = ((i%2==0)?"some text ":"other ")+i;
            tr.setAttribute('data-txt',txt);
            td.innerText=txt;
            tr.appendChild(td);
            thetable.appendChild(tr);
        }
        fragment.appendChild(thetable);
        document.getElementById('container').appendChild(fragment);
    }
    
    function searchHandler(){
      if (window.searchTimeout){
        window.clearTimeout(window.searchTimeout);
      }
      window.searchTimeout=window.setTimeout(function(){    //to avoid rapid filtering as you type
        var txt = document.getElementById('filterTxt').value;
        var stylesheet=document.getElementById('dynamicStyle');
        if (stylesheet){
            stylesheet.remove();
        }
        stylesheet=document.createElement('style');
        stylesheet.setAttribute('id','dynamicStyle');
        stylesheet.appendChild(document.createTextNode('tr[data-txt*="'+txt+'"]{ display: none; }'));  //E[foo*=something] Element where foo attribute CONTAINS something
        document.body.appendChild(stylesheet);
      },500);
    }
    
    document.getElementById('filterTxt').addEventListener('input',searchHandler);
    initDemoData(10000);  //do your tests by changing this value
    
    Login or Signup to reply.
  2. You could use querySelectorAll() to get all the table rows first this will reduce your calls to DOM and make your code a lot faster.

    function toggleHide(val) {
        var tb = document.getElementById("myTab");
        if (tb !== null) {
            var trows = tb.querySelectorAll('tr');  // Use querySelectorAll to select all rows at once
            var rowCount = trows.length;
            
            // Cache the table rows to avoid repeated DOM access
            for (var i = 0; i < rowCount; i++) {
                var rowText = trows[i].cells[0].innerText || trows[i].cells[0].textContent;  // Get text content, handling both innerText and textContent
                
                // Check if the row matches the value we want to hide or show
                if (rowText === val) {
                    trows[i].style.display = 'none';  // Hide matching row
                } else {
                    trows[i].style.display = '';  // Show all other rows
                }
            }
        }
        return false;
    }
    Login or Signup to reply.
  3. It shouldn’t be that slow. I suspect that every button click iterates through all rows of the table. This is not very efficient.

    First: you can use event delegation to make handling more efficient.

    Second: use a css class for the hiding.

    Third: try limiting the number of iterations through the rows to a minimum (e.g. by using querySelectorAll and more precize css selectors).

    Here’s an example for a few scenario’s with the aformentioned in mind.

    document.addEventListener(`click`, handle);
    createDemoTable();
    
    function handle(evt) {
      const rowClicked = evt.target.closest(`tr`);
      const hideByValue = evt.target.dataset.value;
      
      // hide with explicit cell value
      if (hideByValue) {
        const allRows = document.querySelectorAll(`#myTable tr:not(.hidden) td`);
        // Array.find iterates until found, so may be more efficient
        const cellFound = [...allRows]
          .find(cell => cell.textContent === hideByValue);
          
        if (cellFound) {
          const row = cellFound.closest(`tr`);
          row.scrollIntoView();
          row.classList.add(`hidden`);
        }
        
        return;
      }
      
      // hide by clicking on a row
      if (rowClicked) {
        return rowClicked.classList.add(`hidden`);
      }
      
      // hide even rows
      if (evt.target.id === `hideEven`) {
        const rows = document.querySelectorAll(`#myTable tr`);
        return rows.forEach((row, i) => 
          row.classList[(i + 1) % 2 === 0 ? `add` : `remove`](`hidden`)
        );
      }
      
      // hide odd rows
      if (evt.target.id === `hideOdd`) {
        const rows = document.querySelectorAll(`#myTable tr`);
        return rows.forEach((row, i) => 
          row.classList[(i + 1) % 2 !== 0 ? `add` : `remove`](`hidden`)
        );
      }
      
      // show all
      if (evt.target.id === `showAll`) {
        return document.querySelectorAll(`#myTable tr.hidden`)
          .forEach(row => row.classList.remove(`hidden`) );
      }
      
    }
    
    function createDemoTable() {
      const table = Object.assign(
        document.createElement(`table`), 
        {id: `myTable`} );
    
      for (let i=0; i<500; i+=1) {
        const row = Object.assign(document.createElement(`tr`));
        row.append( 
          Object.assign(document.createElement(`td`), 
          {textContent: `row ${i+1}`}) );
        table.append(row);
      }
      
      document.body.append(table);
    }
    #myTable {
      tr.hidden {
        display: none;
      }
      tr {
        cursor: pointer;
      }
      
      tr:hover:after {
        content: 'remove this row';
        position: absolute;
        margin-left: -0.5rem;
        display: inline-block;
        padding: 3px;
        background-color: white;
        color: red;
        border: 1px solid #c0c0c0;
      }
    }
    <button id="hideEven">Hide even rows</button>
    <button id="hideOdd">Hide odd rows</button>
    <button id="showAll">Show all rows</button>
    <button data-value="row 42">Hide "row 42"</button>
    <button data-value="row 499">Hide "row 499"</button>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search