5

I am currently building a site that allows searching for elements, with results added to a big table (think hundreds of results). Each element in the result table can be manipulated in various ways (custom javascript 0-5 star rating, toggling a fold-out panel for additional options etc.).

If I use the site on my android tablet, performance of the javascript part is very sluggish, so I am wondering how I can improve performance.

One option I have considered is to not bind any event listeners on the result rows except for a single mouse-enter event, and then binding the actual event listeners only when the mouse is over a given element.

Any other ideas to improve performance would be greatly appreciated.

Most of my javascript code is jquery based, so if you have any jquery specific optimizations I would appreciate that also.

8
  • 1
    Binding events on current or future elements using the .on() Method should not cost you in performance, cause there's no event until your element is the initiator of such event. What is sluggish? the toggling? Commented Mar 12, 2015 at 7:45
  • Try using css selectors for better performance. Also when you are using jquery then you should know jsperf to check which selectors are better to use. Commented Mar 12, 2015 at 7:46
  • @Jai "Try using css selectors for better performance" Can you explain this? Commented Mar 12, 2015 at 7:47
  • 1
    Consider paginating those "hundreds of results". And there is no magical recipe for improving performance. There are lots of factors involved. Commented Mar 12, 2015 at 7:49
  • @RokoC.Buljan As OP mentioned about jquery, so it might be possible OP might have used some custom selectors which jquery provides like :last, [id^="someid"] etc. which i guess might be there. Commented Mar 12, 2015 at 7:50

1 Answer 1

13

You might look into javaScript event delegation. There are many answers on SO (an example here) and many good articles ( here or here ).

Basically the idea you had is actually a good solution. So, instead of binding - let's say - one hundred rows with their own event handlers, you bind only their common parent which will fire an event when any of its child will receive a mouse input.

Roughly speaking instead of this:

$('tr').on("click", function() {});

You will do this:

$('table').on('click', 'tr', function() {});

This is obviously a very simplified example, but it should be enough to build a good pattern upon.

As a side note, it's a very interesting thing (well, at least for me...) to inspect the event that is fired doing something like:

$('table').on('click', 'tr', function(evt) {
   console.log(evt);
});

And see how much information an event carries, and how much useful information you get out of the box with a simple click or mouse enter.

VanillaJs

Of course the same result can be achieved without any library.

A simple implementation using Vanilla JS can be taken from David Walsh's article linked at the beginning of the answer, it goes roughly like this:

// Get the element, add a click listener...
document.getElementById("#myTable").addEventListener("click", function(e) {
    // e.target is the clicked element.
    // Check if it was a <tr>...
    if(e.target && e.target.nodeName == "TR") {
        // Element has been found, do something with it
    }
});

If you try this code though chances are that the actual target.element is the <td>, and not the <tr> we want. This means we have to be a bit more clever and walk the DOM upwards to see if the clicked element (<td>) is contained in the one we really want (<tr>).

A rough implementation to get you started would look like this:

function walk(startEl, stopEl, targetNodeName) {
  if (!startEl || startEl === stopEl || startEl.nodeName === 'BODY') {
    return false;
  }

  if (startEl.nodeName === targetNodeName) {
    // Found it, return the element
    return startEl;
  }

  // Keep on looking...
  return walk(startEl.parentNode, stopEl, targetNodeName);
}

const container = document.getElementById('myTable');

container.addEventListener('click', (e) => {
  const clickedElm = walk(e.target, container, 'TR');
  console.log(clickedElm);
  if (clickedElm) {
    clickedElm.classList.add('clicked');
  }
})

See this fiddle or the snippet below:

function walk(startEl, stopEl, targetNodeName) {
  if (!startEl || startEl === stopEl || startEl.nodeName === 'BODY') {
    return false;
  }

  if (startEl.nodeName === targetNodeName) {
    return startEl;
  }

  return walk(startEl.parentNode, stopEl, targetNodeName);
}

const elem = document.getElementById('myTable');

elem.addEventListener('click', (e) => {
  const clickedElm = walk(e.target, elem, 'TR');
  console.log(clickedElm);
  if (clickedElm) {
    clickedElm.classList.add('clicked');
  }
})
table {
  width: 100%;
}
tr {
  background: #ddd;
}
.clicked {
  background: teal;
}
 <table id="myTable">
   <tr>
     <td>one</td>
   </tr>
   <tr>
     <td>two</td>
   </tr> 
   <tr>
     <td>three</td>
   </tr> 
   <tr>
     <td>four</td>
   </tr> 
   <tr>
     <td>five</td>
   </tr> 
   <tr>
     <td>six</td>
   </tr>    
 </table>

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.