lundi 8 juillet 2013

JQuery perfomance tip : visible and hidden selector



As client side components becomes smarter, Javascript performance becomes a real concern which can turn user experience to a dream or a nightmare.

JQuery is a mandatory tool you need for some use cases .

One of this use case is  about stripping table rows with even and odd colors.

You can do it using CSS 3 selector :

tr:nth-child(even) {background: #CCC}
tr:nth-child(odd) {background: #FFF} 

But sometimes you need to deal with more complex tables.

Imagine you have a table of data : some TRs are not data directly but some extra informations about data, let's say a detail view of data you want to hide and show when user click on some place on the data row.

So you will  get an HTML stream like this :


<tr data-isdata="true">
     <td>drtykRnZWhIFYkbTwuNkrBuZvBtJEwVDvLFC</td>
     <td>GGRpOQXnEUIUrLztepXPgkNQukEHOKNYoXmO</td>
     <td>fQzhJfjzVmkxGxQAKYVLbdjsLriosPWcNNhq</td>
     <td>pCeIsdthjvnfPtEVfuswGPZpCMbAneejMPbf</td>
     <td>iMzqEzOpecwHwNkoewhynidzUliwMhGEidOe</td>
     <td>XWteGfqPeAXHiNEGxTurkWuMagYeGXosiGdX</td>
</tr>
<tr style="display:none;">
     <td col="7">Some extra informations to hide or show.</td>
</tr>


Now, you are stucked with CSS 3 selectors because you can not select  odd or even nodes with metadata isdata :

tr[data-isdata]:nth-child(even) {background: #CCC}


Does not work since nth-child apply to the element tr and not to the whole tr[data-isdata].

You can actually check this with new HTML 5 API for querying the DOM :

document.querySelectorAll("tr[data-isdata^='true']:nth-child(odd)")

This returns all the nodes carrying data-isdata attribute and not the odd ones.

Plus, you need to strip only visible rows !

CSS 3 don't provide a convenient way to select the nodes we need here.

Visible


Now, you need JQuery : it's very easy to select the nodes we need but you can do it using different ways.

One of the obvious way is to use visible selector :

$("tbody > tr:visible:odd").addClass("odd");
$("tbody > tr:visible:even").addClass("even");

Quite easy isn't it ?

Yes, but quite slow too !

In certain cases, this selector can literally crush the browser, i've seen the browser for 30 seconds with some complex tables with 500 rows !

The visible selector is slow because it does not exist in CSS, it's computed using many other properties :


return elem.offsetWidth <= 0 && elem.offsetHeight <= 0 ||
                        (!jQuery.support.reliableHiddenOffsets && ((elem.style && elem.style.display) || jQuery.css( elem, "display" )) === "none");


This piece of code is the core of hidden method called by the visible one. The visible method apply a not operator after calling hidden method.

Metadata


The use of a dedicated metadata for visibility is much more faster than the visible or hidden selectors.


<tr data-isdata="true" data-isvisible="true">
     <td>drtykRnZWhIFYkbTwuNkrBuZvBtJEwVDvLFC</td>
     <td>GGRpOQXnEUIUrLztepXPgkNQukEHOKNYoXmO</td>
     <td>fQzhJfjzVmkxGxQAKYVLbdjsLriosPWcNNhq</td>
     <td>pCeIsdthjvnfPtEVfuswGPZpCMbAneejMPbf</td>
     <td>iMzqEzOpecwHwNkoewhynidzUliwMhGEidOe</td>
     <td>XWteGfqPeAXHiNEGxTurkWuMagYeGXosiGdX</td>
</tr>
<tr style="display:none;">
     <td col="7">Some extra informations to hide or show.</td>
</tr>

I've built a sample sample with the two ways : the metadata way is 4 times faster than the visible way.

Check the sample on Gist.

Here are the selectors :

var tbody = $("tbody");
tbody.find("tr[data-visible]:odd").addClass("odd");
tbody.find("tr[data-visible]:even").addClass("even");

With a profiler (use Google Chrome "Profile" for instance) you can see that the method strip2 based on metadata selector is many times faster than the visible one.

The main pitfall of the metadata approach is to put extra load on server side and network bandwidth, the metadata is a piece of data you have to generate and carry through the network.

As usual performance tuning is a matter of balance !







Aucun commentaire:

Enregistrer un commentaire