If you only stay at the level of using jQuery without knowing its specific implementation, it is really easy to cause problems. This is why I haven't been very fond of using jQuery recently. The API settings of this framework are suspected of misleading people into going astray.
$.fn.beautifyTable = function(options) {
//Define the default configuration items, and then override them with options
return this.each(function() {
var table = $(this),
tbody = table.children('tbody'),
tr = tbody.children('tr'),
th = tbody.children('th'),
td = tbody.children('td');
//Single content class
table.addClass(option.tableClass);
th.addClass(options.headerClass); //1
td.addClass(options.cellClass); //2
//Odd and even rows class
tbody.children('tr:even').addClass(options.evenRowClass); //3
tbody.children('tr:odd').addClass(options.oddRowClass); //4
//Alignment
tr.children('th,td').css('text-align', options.align); //5
//Add mouse hover
tr. bind('mouseover', addActiveClass); //6
tr.bind('mouseout', removeActiveClass); //7
//Click to change color
tr.bind('click', toggleClickClass); //8
});
};
In general, this code is good, the ideas are clear, the logic is clear, and what you want to do is clearly explained through comments. But according to the author, when there are 120 rows in the table, IE has already reflected that the script running time is too long. Obviously from the performance point of view, the efficiency of this function is not high, or even extremely low.
So, we started to analyze from the code level. This is a standard jQuery plug-in function. There is a typical code in the form of return this.each(function( ) { .. };); If the author didn't just follow the script without thinking when he wrote this code, he should have realized what a function of jQuery did.
To put it simply, most of the functions under jQuery.fn are a call to each. The so-called each naturally traverses the selected elements and performs a specified operation on an element. . So take a look at the above code and see how many traversals have been performed. Let’s assume that only 120 rows are selected, each row has 6 columns, plus 1 row of headers:
Traverse th, add headerClass, the number of elements is 6.
Traverse td, add cellClass, the number of elements is 6*120=720.
To find the odd number from all tr, you need to traverse all tr once, and the number of elements is 120.
Traverse the odd number of tr, add evenRowClass, the number of elements is 120/2=60.
To find the even number from all tr, you need to traverse all tr once, and the number of elements is 120.
Traverse the even number of tr, add oddRowClass, the number of elements is 120/2=60.
Traverse all th and td, add text-align, the number of elements is 120*6 6=726.
Traverse all tr, add mouseover event, the number of elements is 120.
Traverse all tr, add mouseout event, the number of elements is 120.
Traverse all tr, add click event, the number of elements is 120.
For convenience, we simply assume that accessing an element during traversal takes 10ms. So how much time does this function take in total? This function encountered a total of 2172 elements and took 21720ms, or 21 seconds. Obviously IE should indeed report that the script took too long.
Now that we know the reason for the low efficiency, we need to fundamentally solve it. Naturally, we have to find ways to merge the loops. At first glance, according to the numbers in the comments in the code above, at least the following points can be merged:
3 and 4 can be combined into one cycle, from 120 60 120 60 to 120, a reduction of 240. 1, 2 and 5 can be combined into one cycle, from 6 720 726 to 726, a reduction of 726. 6. 7 and 8 can be combined into one cycle, changing from 120 120 120 to 120, which is a reduction of 240. Further, 3, 4 and 6, 7, 8 can be combined into one cycle, which continues to reduce by 120. Cumulatively, we have reduced a total of 240 726 240 120 = 1326 element operations, totaling 13260ms. After optimization, our function time consumption becomes 21720-13260=8460ms, which is 8s.
You may have a question here. From the structure of the table, all th and td elements must be within tr, so why not put the three-step cycle of 1, 2, and 5 into tr as well? In the loop, forming a nested loop, wouldn't it be faster?
There are two main reasons why this is not done here:
First, no matter where you place 1, 2, and 5, it will not reduce one access to all th and td elements.
On the other hand, the selector $('th, td') will be translated into two calls to the getElementsByTagName function in sizzle, the first time to get all th, the second time to get all td, and then collect them of merger. Since getElementsByTagName is a built-in function, it can be considered that this function does not have a loop, that is, the complexity is O(1). The merging of the same collection uses the related function of Array, which is an operation on memory, and the complexity is also O(1). ).
On the contrary, if the selector $('th, 'td) is used in the loop of tr elements, getElementsByTagName will be called twice on the tr element, because no matter which element the function is called on , the function execution time is the same, so when used when looping tr, there are 119*2 more function calls, and the efficiency does not increase but decreases.
It can be seen that the basic knowledge of sizzle selector is also a very important aspect to help optimize jQuery code.
Don’t let javascript do everything.
According to the previous basic optimization, the time has been reduced from 21 seconds to 8 seconds, but the number of 8 seconds is obviously unacceptable.
Further analysis of our code shows that in fact, loop traversal is at the language level, and its speed should be quite fast. The operations performed on each element are functions provided by jQuery. Compared with traversal, they take up most of the resources. If it takes 10ms to access elements during traversal, to put it bluntly, executing an addClass consumes at least 100ms.
Therefore, in order to further optimize efficiency, we have to start by reducing the operations on elements. After carefully reviewing the code, I found that this function has a lot of modifications to the style, at least including:
Add class to all th.
Add class to all td.
Add class to the odd and even lines of tr.
Add a text-align style to all th and td.
In fact, we know that CSS itself has a descendant selector, and the browser's native parsing of CSS is much more efficient than letting JavaScript add classes to elements one by one.
So, if CSS is controllable, then this function should not have the two configuration items headerClass and cellClass, but should be configured in CSS as much as possible:
.beautiful-table th { /* content of headerClass*/ }
.beautiful -table td { /* content of cellClass*/ }
Furthermore, for the odd-even row style of tr, you can use the nth-child pseudo-class to implement it in some browsers. In this regard, you can Use feature detection to add styles using addClass only in browsers that don't support this pseudo-class. Of course, if you just want to optimize the IE series, you can ignore this item.
For the detection of nth-child pseudo-class, you can use the following idea: create a stylesheet, and then create a rule, such as #test span:nth-child (odd) { display: block; } . Create the corresponding HTML structure, a div with the id test, and place 3 spans inside.
Add stylesheet and div to the DOM tree. Check the runtime display style of the 1st and 3rd span. If it is block, it indicates that the pseudo-class is supported. Delete the stylesheet and div you created, and don’t forget to cache the results of the probe. Finally, adding text-align styles to all th and td elements can also be optimized through CSS. Since you don’t know which align is being added, write a few more styles:
/* CSS styles*/
.beautiful-table-center th,.beautiful-table-center td { text-align: center !important; }
.beautiful-table-rightright th, .beautiful-table-rightright td { text-align: rightright !important; }
.beautiful-table-left th,.beautiful-table-left td { text-align: left !important; }
/* javascript */
table.addClass('beautiful-table-' options.align);
Of course, the optimization mentioned above is based on having control over CSS. , what should we do if we have no access to CSS styles, for example, this is a general plug-in function that will be used by a third party that is completely uncontrollable? It’s not completely impossible:
Find all the CSS rules in the page, such as document.styleSheets. Traverse all the rules and take out the headerClass, cellClass, etc. in the configuration items. Extract all the styles in the several classes you need, and then assemble them into a new selector yourself, such as beautiful-table th. Use the created selector to generate a new stylesheet and add it to the DOM tree. Then just add the beautiful-table class to the table and you're done.
Of course, the above approach is actually quite time-consuming. After all, you have to traverse the stylesheet and create the stylesheet. Whether it is of great help to improve efficiency will have different effects depending on the size of the page. Whether to use it depends on the specific needs of the function designer. Here is a strategy.
In general, by executing as little javascript as possible and handing more styling tasks to CSS, the browser's rendering engine will complete it, and this function can be further optimized. Assume that for If the call to addClass and css takes 100ms, this optimization directly eliminates the original 120 726 = 846 operations and saves 84600ms of time (of course it is an exaggeration, but in terms of the consumption of the entire function, this is really a lot big piece).
This article only wants to optimize the various implementation levels of jQuery. It only involves the analysis, detailed introduction and optimization direction of the entire running process of jQuery, and does not mention some basic basics. Optimization methods, such as: first remove the entire table from the DOM tree, and then put it back into the DOM after completing all operations to reduce repaint. Change mouseover and mouseout to mouseenter and mouseleave to reduce the execution of repeated event functions caused by the correct event bubbling model. For the selection of simple elements such as th and td, priority is given to using the native getElementsByTagName to eliminate the time of sizzle analyzing the selector.
Finally, this article just wants to explain that for front-end developers, although the browser may be a black box, many frameworks, tools, and libraries are open. If you can understand it to a certain extent before using it, , will definitely help improve personal technology and optimize the quality of the final product. "Knowing it but not knowing why" is a very taboo situation.