- 浏览: 46538 次
- 性别:
- 来自: 湖南
-
最新评论
-
laj12347:
javascript 闭包真是牛逼呀。。看了好久,终于有点 ...
JavaScript: The Good Parts 读书笔记(二) -
mu0001:
赞一个,很不错。以现在的知识来看,先看了第一部分。
JavaScript 数据访问(翻译自High Performance Javascript 第二章) -
e421083458:
得买本书好好看看了!
JavaScript 数据访问(翻译自High Performance Javascript 第二章) -
adamed:
这本书已经有中文版出版了。。到处都可以买到。高性能 javas ...
JavaScript 数据访问(翻译自High Performance Javascript 第二章) -
jiyanliang:
elvishehai 写道来源于那里的呀,写的很不错,学习了. ...
JavaScript 数据访问(翻译自High Performance Javascript 第二章)
三.DOM Scripting
DOM scripting is expensive, and it’s a common performance bottleneck in rich web applications. This chapter discusses the areas of DOM scripting that can have a negative effect on an application’s responsiveness and gives recommendations on how to improve response time. The three categories of problems discussed in the chapter include:
1. Accessing and modifying DOM elements
2. Modifying the styles of DOM elements and causing repaints and reflows
3.Handling user interaction through DOM events
- DOM in the Browser World
The Document Object Model (DOM) is a language-independent application interface (API) for working with XML and HTML documents. In the browser, you mostly work with HTML documents, although it’s not uncommon for web applications to retrieve XML documents and use the DOM APIs to access data from those documents.
Even though the DOM is a language-independent API, in the browser the interface is implemented in JavaScript. Since most of the work in client-side scripting has to do with the underlying document, DOM is an important part of everyday JavaScript coding.
It’s common across browsers to keep DOM and JavaScript implementations independent of each other. In Internet Explorer, for example, the JavaScript implementation is called JScript and lives in a library file called jscript.dll, while the DOM implementation lives in another library, mshtml.dll (internally called Trident). This separation allows other technologies and languages, such as VBScript, to benefit from the DOM and the rendering functionality Trident has to offer. Safari uses WebKit’s WebCore for DOM and rendering and has a separate JavaScriptCore engine (dubbed SquirrelFish in its latest version). Google Chrome also uses WebCore libraries from WebKit for rendering pages but implements its own JavaScript engine called V8. In Firefox, SpiderMonkey (the latest version is called TraceMonkey) is the JavaScript implementation, a
separate part of the Gecko rendering engine.
Inherently Slow
What does that mean for performance? Simply having two separate pieces of functionality interfacing with each other will always come at a cost. An excellent analogy is to think of DOM as a piece of land and JavaScript (meaning ECMAScript) as another piece of land, both connected with a toll bridge (see John Hrvatin, Microsoft, MIX09, http://videos.visitmix.com/MIX09/T53F). Every time your ECMAScript needs access to the DOM, you have to cross this bridge and pay the performance toll fee. The more you work with the DOM, the more you pay. So the general recommendation is to cross that bridge as few times as possible and strive to stay in ECMAScript land. The rest of the chapter focuses on what this means exactly and where to look in order to make user interactions faster.
DOM Access and Modification
Simply accessing a DOM element comes at a price—the “toll fee” discussed earlier.Modifying elements is even more expensive because it often causes the browser to recalculate changes in the page geometry.
Naturally, the worst case of accessing or modifying elements is when you do it in loops, and especially in loops over HTML collections.
Just to give you an idea of the scale of the problems with DOM scripting, consider this simple example:function innerHTMLLoop() { for (var count = 0; count < 15000; count++) { document.getElementById('here').innerHTML += 'a'; } }
This is a function that updates the contents of a page element in a loop. The problem with this code is that for every loop iteration, the element is accessed twice: once to read the value of the innerHTML property and once to write it.
A more efficient version of this function would use a local variable to store the updated contents and then write the value only once at the end of the loop:function innerHTMLLoop2() { var content = ''; for (var count = 0; count < 15000; count++) { content += 'a'; } document.getElementById('here').innerHTML += content; }
This new version of the function will run much faster across all browsers. Figure 3-1 shows the results of measuring the time improvement in different browsers. The y-axis in the figure (as with all the figures in this chapter) shows execution time improvement, i.e., how much faster it is to use one approach versus another. In this case, for example,using innerHTMLLoop2() is 155 times faster than innerHTMLLoop() in IE6.
As these results clearly show, the more you access the DOM, the slower your code executes. Therefore, the general rule of thumb is this: touch the DOM lightly, and stay within ECMAScript as much as possible.
a.innerHTML Versus DOM methods
Over the years, there have been many discussions in the web development community over this question: is it better to use the nonstandard but well-supported innerHTML property to update a section of a page, or is it best to use only the pure DOM methods, such as document.createElement()? Leaving the web standards discussion aside, does it matter for performance? The answer is: it matters increasingly less, but still,innerHTML is faster in all browsers except the latest WebKit-based ones (Chrome and Safari). The benefits of innerHTML are more obvious in older browser versions (innerHTML is 3.6 times faster in IE6), but the benefits are less pronounced in newer versions. And in newer WebKit-based browsers it’s the opposite: using DOM methods is slightly faster. So the decision about which approach to take will depend on the browsers your users are commonly using, as well as your coding preferences.
As a side note, keep in mind that this example used string concatenation, which is not optimal in older IE versions. Using an array to concatenate large strings will make innerHTML even faster in those browsers.
Using innerHTML will give you faster execution in most browsers in performance-critical operations that require updating a large part of the HTML page. But for most everyday cases there isn’t a big difference, and so you should consider readability, maintenance, team preferences, and coding conventions when deciding on your approach.
b.Cloning Nodes
Another way of updating page contents using DOM methods is to clone existing DOM elements instead of creating new ones—in other words, using element.cloneNode() (where element is an existing node) instead of document.createElement().
Cloning nodes is more efficient in most browsers, but not by a big margin. Regenerating the table from the previous example by creating the repeating elements only once and then copying them results in slightly faster execution times:
• 2% in IE8, but no change in IE6 and IE7
• Up to 5.5% in Firefox 3.5 and Safari 4
• 6% in Opera (but no savings in Opera 10)
• 10% in Chrome 2 and 3% in Chrome 3
c.HTML Collections
HTML collections are array-like objects containing DOM node references. Examples of collections are the values returned by the following methods:
• document.getElementsByName()
• document.getElementsByClassName()
• document.getElementsByTagName()
The following properties also return HTML collections:
document.images
All img elements on the page
document.links
All a elements
document.forms
All forms
document.forms[0].elements
All fields in the first form on the page
These methods and properties return HTMLCollection objects, which are array-like lists.They are not arrays (because they don’t have methods such as push() or slice()), but provide a length property just like arrays and allow indexed access to the elements in the list. For example, document.images[1] returns the second element in the collection. As defined in the DOM standard, HTML collections are “assumed to be live, meaning that they are automatically updated when the underlying document is updated” (see http://www.w3.org/TR/DOM-Level-2-HTML/html.html#ID-75708506).
The HTML collections are in fact queries against the document, and these queries are being reexecuted every time you need up-to-date information, such as the number of elements in the collection (i.e., the collection’s length). This could be a source of inefficiencies.
To demonstrate that the collections are live, consider the following snippet:
// an accidentally infinite loop var alldivs = document.getElementsByTagName('div'); for (var i = 0; i < alldivs.length; i++) { document.body.appendChild(document.createElement('div')) }
This code looks like it simply doubles the number of div elements on the page. It loops through the existing divs and creates a new div every time, appending it to the body. But this is in fact an infinite loop because the loop’s exit condition, alldivs.length, increases by one with every iteration, reflecting the current state of the underlying document.
Looping through HTML collections like this may lead to logic mistakes, but it’s also slower, due to the fact that the query needs to run on every iteration. When the length of the collection is accessed on every iteration, it causes the collection to be updated and has a significant performance penalty across all browsers. The way to optimize this is to simply cache the length of the collection into a variable and use this variable to compare in the loop’s exit condition:
function loopCacheLengthCollection() { var coll = document.getElementsByTagName('div'), len = coll.length; for (var count = 0; count < len; count++) { // ... } }
The previous example used just an empty loop, but what happens when the elements of the collection are accessed within the loop?
In general, for any type of DOM access it’s best to use a local variable when the same DOM property or method is accessed more than once. When looping over a collection, the first optimization is to store the collection in a local variable and cache the length outside the loop, and then use a local variable inside the loop for elements that are accessed more than once.
In the next example, three properties of each element are accessed within the loop. The lowest version accesses the global document every time, an optimized version caches a reference to the collection, and the fastest version also stores the current element of the collection into a variable. All three versions cache the length of the collection.
// slow function collectionGlobal() { var coll = document.getElementsByTagName('div'), len = coll.length, name = ''; for (var count = 0; count < len; count++) { name = document.getElementsByTagName('div')[count].nodeName; name = document.getElementsByTagName('div')[count].nodeType; name = document.getElementsByTagName('div')[count].tagName; } return name; }; // faster function collectionLocal() { var coll = document.getElementsByTagName('div'), len = coll.length, name = ''; for (var count = 0; count < len; count++) { name = coll[count].nodeName; name = coll[count].nodeType; name = coll[count].tagName; } return name; }; // fastest function collectionNodesLocal() { var coll = document.getElementsByTagName('div'), len = coll.length, name = '', el = null; for (var count = 0; count < len; count++) { el = coll[count]; name = el.nodeName; name = el.nodeType; name = el.tagName; } return name; };
- Walking the DOM
The DOM API provides multiple avenues to access specific parts of the overall document structure. In cases when you can choose between approaches, it’s beneficial to use the most efficient API for a specific job.
a.Crawling the DOM
Often you need to start from a DOM element and work with the surrounding elements,maybe recursively iterating over all children. You can do so by using the childNodes collection or by getting each element’s sibling using nextSibling.
Consider these two equivalent approaches to a nonrecursive visit of an element’s children:function testNextSibling() { var el = document.getElementById('mydiv'), ch = el.firstChild, name = ''; do { name = ch.nodeName; } while (ch = ch.nextSibling); return name; }; function testChildNodes() { var el = document.getElementById('mydiv'), ch = el.childNodes, len = ch.length, name = ''; for (var count = 0; count < len; count++) { name = ch[count].nodeName; } return name; };
Bear in mind that childNodes is a collection and should be approached carefully, caching the length in loops so it’s not updated on every iteration.
The two approaches are mostly equal in terms of execution time across browsers. But in IE, nextSibling performs much better than childNodes. In IE6, nextSibling is 16 times faster, and in IE7 it’s 105 times faster. Given these results, using nextSibling is the preferred method of crawling the DOM in older IE versions in performance-critical cases. In all other cases, it’s mostly a question of personal and team preference.
b.Element nodes
DOM properties such as childNodes, firstChild, and nextSibling don’t distinguish between element nodes and other node types, such as comments and text nodes (which are often just spaces between two tags). In many cases, only the element nodes need to be accessed, so in a loop it’s likely that the code needs to check the type of node returned and filter out nonelement nodes. This type checking and filtering is unnecessary DOM work. Many modern browsers offer APIs that only return element nodes. It’s better to use those when available, because they’ll be faster than if you do the filtering yourself in JavaScript. Table 3-1 lists those convenient DOM properties.
Table 3-1. DOM properties that distinguish element nodes (HTML tags) versus all nodesProperty Use as a replacement for children childNodes childElementCount childNodes.length firstElementChild firstChild lastElementChild lastChild nextElementSibling nextSibling previousElementSibling previousSibling
All of the properties listed in Table 3-1 are supported as of Firefox 3.5, Safari 4, Chrome 2, and Opera 9.62. Of these properties, IE versions 6, 7, and 8 only support children.
Looping over children instead of childNodes is faster because there are usually less items to loop over. Whitespaces in the HTML source code are actually text nodes, and they are not included in the children collection. children is faster than childNodes across all browsers, although usually not by a big margin—1.5 to 3 times faster. One notable exception is IE, where iterating over the children collection is significantly faster than iterating over childNodes—24 times faster in IE6 and 124 times faster in IE7.
c.Element nodes
When identifying the elements in the DOM to work with, developers often need finer control than methods such as getElementById() and getElementsByTagName() can provide. Sometimes you combine these calls and iterate over the returned nodes in order to get to the list of elements you need, but this refinement process can become inefficient.
On the other hand, using CSS selectors is a convenient way to identify nodes because developers are already familiar with CSS. Many JavaScript libraries have provided APIs for that purpose, and now recent browser versions provide a method called querySelectorAll() as a native browser DOM method. Naturally this approach is faster than using JavaScript and DOM to iterate and narrow down a list of elements. Consider the following:var elements = document.querySelectorAll('#menu a');
The value of elements will contain a list of references to all a elements found inside an element with id="menu". The method querySelectorAll() takes a CSS selector string as an argument and returns a NodeList—an array-like object containing matching nodes. The method doesn’t return an HTML collection, so the returned nodes do not represent the live structure of the document. This avoids the performance (and potentially logic) issues with HTML collection discussed previously in this chapter.
To achieve the same goal as the preceding code without using querySelectorAll(), you will need the more verbose:var elements = document.getElementById('menu').getElementsByTagName('a');
In this case elements will be an HTML collection, so you’ll also need to copy it into an array if you want the exact same type of static list as returned by querySelectorAll(). Using querySelectorAll() is even more convenient when you need to work with a union of several queries. For example, if the page has some div elements with a class name of “warning” and some with a class of “notice”, to get a list of all of them you can use querySelectorAll():var errs = document.querySelectorAll('div.warning, div.notice');
Getting the same list without querySelectorAll() is considerably more work. One way is to select all div elements and iterate through them to filter out the ones you don’t need.var errs = [], divs = document.getElementsByTagName('div'), classname = ''; for (var i = 0, len = divs.length; i < len; i++) { classname = divs[i].className; if (classname === 'notice' || classname === 'warning') { errs.push(divs[i]); } }
The Selectors API is supported natively in browsers as of these versions: Internet Explorer 8, Firefox 3.5, Safari 3.1, Chrome 1, and Opera 10.
- Repaints and Reflows
Once the browser has downloaded all the components of a page—HTML markup,JavaScript, CSS, images—it parses through the files and creates two internal data structures:A DOM tree A representation of the page structure A render tree A representation of how the DOM nodes will be displayed
The render tree has at least one node for every node of the DOM tree that needs to be displayed (hidden DOM elements don’t have a corresponding node in the render tree). Nodes in the render tree are called frames or boxes in accordance with the CSS model that treats page elements as boxes with padding, margins, borders, and position. Once the DOM and the render trees are constructed, the browser can display (“paint”) the elements on the page.
When a DOM change affects the geometry of an element (width and height)—such as a change in the thickness of the border or adding more text to a paragraph, resulting in an additional line—the browser needs to recalculate the geometry of the element as well as the geometry and position of other elements that could have been affected by the change. The browser invalidates the part of the render tree that was affected by the change and reconstructs the render tree. This process is known as a reflow. Once the reflow is complete, the browser redraws the affected parts of the screen in a process called repaint.
Not all DOM changes affect the geometry. For example, changing the background color of an element won’t change its width or height. In this case, there is a repaint only (no reflow), because the layout of the element hasn’t changed. Repaints and reflows are expensive operations and can make the UI of a web application less responsive. As such, it’s important to reduce their occurrences whenever possible.
a.When Does a Reflow Happen?
As mentioned earlier, a reflow is needed whenever layout and geometry change. This happens when:
• Visible DOM elements are added or removed
• Elements change position
• Elements change size (because of a change in margin, padding, border thickness, width, height, etc.)
• Content is changed, e.g., text changes or an image is replaced with one of a different size.
• Page renders initially
• Browser window is resized
Depending on the nature of the change, a smaller or bigger part of the render tree needs to be recalculated. Some changes may cause a reflow of the whole page: for example, when a scroll bar appears.
b.Queuing and Flushing Render Tree Changes
Because of the computation costs associated with each reflow, most browsers optimize the reflow process by queuing changes and performing them in batches. However, you may (often involuntarily) force the queue to be flushed and require that all scheduled changes be applied right away. Flushing the queue happens when you want to retrieve layout information, which means using any of the following:
• offsetTop, offsetLeft, offsetWidth, offsetHeight
• scrollTop, scrollLeft, scrollWidth, scrollHeight
• clientTop, clientLeft, clientWidth, clientHeight
• getComputedStyle() (currentStyle in IE)
The layout information returned by these properties and methods needs to be up to date, and so the browser has to execute the pending changes in the rendering queue and reflow in order to return the correct values.
During the process of changing styles, it’s best not to use any of the properties shown in the preceding list. All of these will flush the render queue, even in cases where you’re retrieving layout information that wasn’t recently changed or isn’t even relevant to the latest changes.
Consider the following example of changing the same style property three times (this is probably not something you’ll see in real code, but is an isolated illustration of an important topic):// setting and retrieving styles in succession var computed, tmp = '', bodystyle = document.body.style; if (document.body.currentStyle) { // IE, Opera computed = document.body.currentStyle; } else { // W3C computed = document.defaultView.getComputedStyle(document.body, ''); } // inefficient way of modifying the same property // and retrieving style information right after bodystyle.color = 'red'; tmp = computed.backgroundColor; bodystyle.color = 'white'; tmp = computed.backgroundImage; bodystyle.color = 'green'; tmp = computed.backgroundAttachment;
In this example, the foreground color of the body element is being changed three times,and after every change, a computed style property is retrieved. The retrieved properties—backgroundColor, backgroundImage, and backgroundAttachment—are unrelated to the color being changed. Yet the browser needs to flush the render queue and reflow due to the fact that a computed style property was requested.
A better approach than this inefficient example is to never request layout information while it’s being changed. If the computed style retrieval is moved to the end, the code looks like this:bodystyle.color = 'red'; bodystyle.color = 'white'; bodystyle.color = 'green'; tmp = computed.backgroundColor; tmp = computed.backgroundImage; tmp = computed.backgroundAttachment;
c.Minimizing Repaints and Reflows
Reflows and repaints can be expensive, and therefore a good strategy for responsive applications is to reduce their number. In order to minimize this number, you should combine multiple DOM and style changes into a batch and apply them once.
Style changes
Consider this example:var el = document.getElementById('mydiv'); el.style.borderLeft = '1px'; el.style.borderRight = '2px'; el.style.padding = '5px';
Here there are three style properties being changed, each of them affecting the geometry of the element. In the worst case, this will cause the browser to reflow three times. Most modern browsers optimize for such cases and reflow only once, but it can still be inefficient in older browsers or if there’s a separate asynchronous process happening at the same time (i.e., using a timer). If other code is requesting layout information while this code is running, it could cause up to three reflows. Also, the code is touching the DOM four times and can be optimized.
A more efficient way to achieve the same result is to combine all the changes and apply them at once, modifying the DOM only once. This can be done using the cssText property:var el = document.getElementById('mydiv'); el.style.cssText = 'border-left: 1px; border-right: 2px; padding: 5px;';
Modifying the cssText property as shown in the example overwrites existing style nformation,
so if you want to keep the existing styles, you can append this to the cssText string:el.style.cssText += '; border-left: 1px;';
Another way to apply style changes only once is to change the CSS class name instead of changing the inline styles. This approach is applicable in cases when the styles do not depend on runtime logic and calculations. Changing the CSS class name is cleaner and more maintainable; it helps keep your scripts free of presentation code, although it might come with a slight performance hit because the cascade needs to be checked when changing classes.
var el = document.getElementById('mydiv'); el.className = 'active';
Batching DOM changes
When you have a number of changes to apply to a DOM element, you can reduce the number of repaints and reflows by following these steps:
1. Take the element off of the document flow.
2. Apply multiple changes.
3. Bring the element back to the document.
This process causes two reflows—one at step 1 and one at step 3. If you omit those steps, every change you make in step 2 could cause its own reflows.There are three basic ways to modify the DOM off the document:
• Hide the element, apply changes, and show it again.
• Use a document fragment to build a subtree outside of the live DOM and then copy it to the document.
• Copy the original element into an off-document node, modify the copy, and then
replace the original element once you’re done.
To illustrate the off-document manipulations, consider a list of links that must be updated with more information:<ul id="mylist"> <li><a href="http://phpied.com">Stoyan</a></li> <li><a href="http://julienlecomte.com">Julien</a></li> </ul>
Suppose additional data, already contained in an object, needs to be inserted into this list. The data is defined as:
var data = [ { "name": "Nicholas", "url": "http://nczonline.net" }, { "name": "Ross", "url": "http://techfoolery.com" } ];
The following is a generic function to update a given node with new data:function appendDataToElement(appendToElement, data) { var a, li; for (var i = 0, max = data.length; i < max; i++) { a = document.createElement('a'); a.href = data[i].url; a.appendChild(document.createTextNode(data[i].name)); li = document.createElement('li'); li.appendChild(a); appendToElement.appendChild(li); } };
The most obvious way to update the list with the data without worrying about reflows would be the following:var ul = document.getElementById('mylist'); appendDataToElement(ul, data);
Using this approach, however, every new entry from the data array will be appended to the live DOM tree and cause a reflow. As discussed previously, one way to reduce reflows is to temporarily remove the <ul> element from the document flow by changing the display property and then revert it:var ul = document.getElementById('mylist'); ul.style.display = 'none'; appendDataToElement(ul, data); ul.style.display = 'block';
Another way to minimize the number of reflows is to create and update a document fragment, completely off the document, and then append it to the original list. A document fragment is a lightweight version of the document object, and it’s designed to help with exactly this type of task—updating and moving nodes around. One syntactically convenient feature of the document fragments is that when you append a fragment to a node, the fragment’s children actually get appended, not the fragment itself. The following solution takes one less line of code, causes only one reflow, and touches the live DOM only once:var fragment = document.createDocumentFragment(); appendDataToElement(fragment, data); document.getElementById('mylist').appendChild(fragment);
A third solution would be to create a copy of the node you want to update, work on the copy, and then, once you’re done, replace the old node with the newly updated copy:var old = document.getElementById('mylist'); var clone = old.cloneNode(true); appendDataToElement(clone, data); old.parentNode.replaceChild(clone, old);
The recommendation is to use document fragments (the second solution) whenever possible because they involve the least amount of DOM manipulations and reflows. The only potential drawback is that the practice of using document fragments is currently underused and some team members may not be familiar with the technique.
- Caching Layout Information
As already mentioned, browsers try to minimize the number of reflows by queuing changes and executing them in batches. But when you request layout information such as offsets, scroll values, or computed style values, the browser flushes the queue and applies all the changes in order to return the updated value. It is best to minimize the number of requests for layout information, and when you do request it, assign it to local variables and work with the local values.
Consider an example of moving an element myElement diagonally, one pixel at a time, starting from position 100 × 100px and ending at 500 × 500px. In the body of a timeout loop you could use:// inefficient myElement.style.left = 1 + myElement.offsetLeft + 'px'; myElement.style.top = 1 + myElement.offsetTop + 'px'; if (myElement.offsetLeft >= 500) { stopAnimation(); }
This is not efficient, though, because every time the element moves, the code requests the offset values, causing the browser to flush the rendering queue and not benefit from its optimizations. A better way to do the same thing is to take the start value position once and assign it to a variable such as var current = myElement.offsetLeft;. Then, inside of the animation loop, work with the current variable and don’t request offsets:current++ myElement.style.left = current + 'px'; myElement.style.top = current + 'px'; if (current >= 500) { stopAnimation(); }
- Take Elements Out of the Flow for Animations
Showing and hiding parts of a page in an expand/collapse manner is a common interaction pattern. It often includes geometry animation of the area being expanded, which pushes down the rest of the content on the page.
Reflows sometimes affect only a small part of the render tree, but they can affect a larger portion, or even the whole tree. The less the browser needs to reflow, the more responsive your application will be. So when an animation at the top of the page pushes down almost the whole page, this will cause a big reflow and can be expensive, appearing choppy to the user. The more nodes in the render tree that need recalculation, the worse it becomes.
A technique to avoid a reflow of a big part of the page is to use the following steps:
1. Use absolute positioning for the element you want to animate on the page, taking it out of the layout flow of the page.
2. Animate the element. When it expands, it will temporarily cover part of the page. This is a repaint, but only of a small part of the page instead of a reflow and repaint of a big page chunk.
3. When the animation is done, restore the positioning, thereby pushing down the rest of the document only once.
- IE and :hover
Since version 7, IE can apply the :hover CSS pseudo-selector on any element (in strict mode). However, if you have a significant number of elements with a :hover, the responsiveness degrades. The problem is even more visible in IE 8.
For example, if you create a table with 500–1000 rows and 5 columns and use tr:hover to change the background color and highlight the row the user is on, the performance degrades as the user moves over the table. The highlight is slow to apply, and the CPU usage increases to 80%–90%. So avoid this effect when you work with a large number of elements, such as big tables or long item lists.
- Event Delegation
When there are a large number of elements on a page and each of them has one or more event handlers attached (such as onclick), this may affect performance. Attaching every handler comes at a price—either in the form of heavier pages (more markup or JavaScript code) or in the form of runtime execution time. The more DOM nodes you need to touch and modify, the slower your application, especially because the event attaching phase usually happens at the onload (or DOMContentReady) event, which is a busy time for every interaction-rich web page. Attaching events takes processing time, and, in addition, the browser needs to keep track of each handler, which takes up memory. And at the end of it, a great number of these event handlers might never be needed (because the user clicked one button or link, not all 100 of them, for example), so a lot of the work might not be necessary.
A simple and elegant technique for handling DOM events is event delegation. It’s based on the fact that events bubble up and can be handled by a parent element. With event delegation, you attach only one handler on a wrapper element to handle all events that happen to the children descendant of that parent wrapper.
According to the DOM standard, each event has three phases:
• Capturing
• At target
• Bubbling
Capturing is not supported by IE, but bubbling is good enough for the purposes of delegation.
- Summary:
DOM access and manipulation are an important part of modern web applications. But every time you cross the bridge from ECMAScript to DOM-land, it comes at a cost. To reduce the performance costs related to DOM scripting, keep the following in mind:
• Minimize DOM access, and try to work as much as possible in JavaScript.
• Use local variables to store DOM references you’ll access repeatedly.
• Be careful when dealing with HTML collections because they represent the live,underlying document. Cache the collection length into a variable and use it when iterating, and make a copy of the collection into an array for heavy work on collections.
• Use faster APIs when available, such as querySelectorAll() and firstElementChild.
• Be mindful of repaints and reflows; batch style changes, manipulate the DOM tree “offline,” and cache and minimize access to layout information.
• Position absolutely during animations, and use drag and drop proxies.
• Use event delegation to minimize the number of event handlers.
发表评论
-
High Performance JavaScript 读书笔记(五)
2011-02-27 21:24 2342五.Strings and Regular Expressio ... -
High Performance JavaScript 读书笔记(六)
2011-02-27 20:49 2689六.Responsive Interfaces There ... -
High Performance JavaScript 读书笔记(四)
2011-02-27 19:50 1250四.Algorithms and Flow Control ... -
High Performance JavaScript 读书笔记(一)
2011-02-27 13:44 1642一.Loading and Execution JavaS ... -
JavaScript 数据访问(翻译自High Performance Javascript 第二章)
2011-02-27 13:21 1961计算机科学中一 ... -
JavaScript Patterns 读书笔记(七)
2011-02-15 20:32 14817.Client Pattern DOM Access ... -
JavaScript Patterns 读书笔记(六)
2011-02-15 19:38 13006.Design Pattern Singleton ... -
JavaScript Patterns 读书笔记(五)
2011-02-15 19:08 12725.Inheritance Pattern Classic ... -
JavaScript Patterns 读书笔记(四)
2011-02-15 18:19 12304.Function Pattern Namespace ... -
JavaScript Patterns 读书笔记(二)
2011-02-14 21:20 1191二.Object Object Constructor C ... -
JavaScript Patterns 读书笔记(三)
2011-02-14 21:19 1490三.Function Background The ... -
JavaScript Patterns 读书笔记(一)
2011-02-14 20:09 1695一.Global 全局域中的this = window.m ... -
JavaScript: The Good Parts 读书笔记(五)
2011-01-27 12:56 2118五.Javascript 总结 语 ... -
JavaScript: The Good Parts 读书笔记(四)
2011-01-27 11:37 1029四.数组与正则表达式 ... -
JavaScript: The Good Parts 读书笔记(三)
2011-01-26 23:38 1431三.继承 概述 Javascr ... -
JavaScript: The Good Parts 读书笔记(二)
2011-01-26 23:01 1862二.函数 JS 中函数亦是对象 使用字面变量表面 ... -
JavaScript: The Good Parts 读书笔记(一)
2011-01-26 21:44 1315一.基础特性 在浏览器中,每个<script> ...
相关推荐
《High Performance JavaScript》是Nicholas C. Zakas所著的一本专注于提高JavaScript代码效率的书籍。这本书涵盖了许多使***ript运行更高效的技巧和方法,为开发人员提供了优化代码的策略。以下是根据提供的文件...
但现在,我们可以实现高效流式传输、双向实时通信、自定义应用程序协议的能力,甚至包括点对点视频会议和直接在对等方之间传递数据的能力——这全都通过寥寥数行JavaScript代码便可实现。结果就是,数十亿的联网设备...
我想学习建站技术,从哪里开始呢? XHTML基础教程 ...《High Performance Web Sites》读书笔记 单片机笔记 C51笔记 51单片机实验 51单片机的Proteus实验 单片机小工具 ARM的Proteus实验 全站打包资源
前段时间花时间看了大半的《High Performance JavaScript》这本书啊,然后就开始忙项目了,庆幸最忙的一周已经熬过去了。由于空不出时间,这个月写的学习笔记也不多,忙完最苦X的一周,这两天晚上也算是挑灯夜读了…...
基于Python的天气预测和天气可视化项目源码+文档说明(高分毕设/大作业),个人经导师指导并认可通过的高分设计项目,评审分99分,代码完整确保可以运行,小白也可以亲自搞定,主要针对计算机相关专业的正在做大作业的学生和需要项目实战练习的学习者,可作为毕业设计、课程设计、期末大作业,代码资料完整,下载可用。 基于Python的天气预测和天气可视化项目源码+文档说明(高分毕设/大作业)基于Python的天气预测和天气可视化项目源码+文档说明(高分毕设/大作业)基于Python的天气预测和天气可视化项目源码+文档说明(高分毕设/大作业)基于Python的天气预测和天气可视化项目源码+文档说明(高分毕设/大作业)基于Python的天气预测和天气可视化项目源码+文档说明(高分毕设/大作业)基于Python的天气预测和天气可视化项目源码+文档说明(高分毕设/大作业)基于Python的天气预测和天气可视化项目源码+文档说明(高分毕设/大作业)基于Python的天气预测和天气可视化项目源码+文档说明(高分毕设/大作业)基于Python的天气预测和天气可视化项目源码+文档说明(高分毕设/大作业)基于Python的天气预测和天气可视化项目源码+文档说明(高分毕设/大作业)基于Python的天气预测和天气可视化项目源码+文档说明(高分毕设/大作业)基于Python的天气预测和天气可视化项目源码+文档说明(高分毕设/大作业)基于Python的天气预测和天气可视化项目源码+文档说明(高分毕设/大作业)基于Python的天气预测和天气可视化项目源码+文档说明(高分毕设/大作业)基于Python的天气预测和天气可视化项目源码+文档说明(高分毕设/大作业)基于Python的天气预测和天气可视化项目源码+文档说明(高分毕设/大作业)基于Python的天气预测和天气可视化项目源码+文档说明(高分毕设/大作业
2025工业5G终端设备发展报告.pdf
内容概要:本文介绍了一段基于分布式ADMM算法的MATLAB代码,用于电力系统优化调度,尤其关注碳排放交易的影响。代码首先对电力系统进行分区,接着构建DC-DOPF最优潮流问题,考虑碳排放交易的成本,并利用ADMM算法求解。文中详细解释了各个关键步骤,如系统分区、目标函数设计、碳排放交易成本计算以及ADMM算法的具体实现。此外,代码还包括了多种优化技术和实用技巧,如自适应惩罚因子调整、边界条件处理等,确保算法的有效性和实用性。 适用人群:适用于对电力系统优化调度感兴趣的科研人员、工程师和技术爱好者,尤其是希望深入了解分布式算法和碳排放交易机制的人群。 使用场景及目标:①研究电力系统优化调度的新方法和技术;②探讨碳排放交易对电力系统调度策略的影响;③提高电力系统运行效率和环保性能。 其他说明:代码不仅提供了详细的注释和模块化设计,还展示了丰富的可视化结果,便于理解和进一步研究。同时,文中提到了一些实际应用案例,证明了该方法的有效性和优越性。
适配于jdk8版本
自动化生成全套教程
内容概要:本文档《GRP_U8软件近期常见问题85例.docx》详细列出了GRP_U8软件在实际使用过程中遇到的85个常见问题及其解决方案。这些问题涵盖了账务处理、电子报表、工资模块、资产管理、物资管理、成本模块、网上报销、预算编制、学生收费、安装配置以及基础数据管理等多个方面。每个问题不仅描述了现象,还提供了具体的解决步骤或SQL语句。文档强调在执行任何脚本前务必进行整库备份,并提供了维护问题的联系方式。 适合人群:适用于GRP_U8软件的管理员、技术支持人员及有一定数据库操作基础的用户。 使用场景及目标:①帮助用户快速定位并解决GRP_U8软件在账务处理、报表生成、工资管理、资产管理等模块中遇到的具体问题;②提供详细的SQL语句和操作指南,确保用户能够独立解决问题,减少对技术支持的依赖;③指导用户在遇到软件安装、配置及升级相关问题时采取正确的措施。 其他说明:文档内容正在不断完善中,用户可以通过私信反馈意见和建议。此外,文档中多次强调了数据安全的重要性,提醒用户在执行任何操作前做好备份工作。针对某些特定问题,文档还提供了多种解决方案供用户选择,以适应不同的环境和需求。
少儿编程scratch项目源代码文件案例素材-scratch RPG 战斗.zip
内容概要:本文详细介绍了利用模型预测控制(MPC)实现无人艇分布式编队协同控制的方法和技术。首先,通过简化的动力学模型和MATLAB代码展示了无人艇的基本行为预测。接着,深入探讨了编队协同控制的关键要素,包括代价函数的设计、信息交换机制以及分布式MPC的具体实现步骤。文中还提供了具体的Python代码示例,涵盖了从单个无人艇的动力学建模到多智能体之间的协作控制。此外,作者分享了一些实用技巧,如如何处理通信延迟、传感器噪声等问题,并展示了仿真效果,证明了所提出方法的有效性和鲁棒性。 适合人群:对无人艇编队控制、模型预测控制(MPC)、分布式系统感兴趣的科研人员、工程师及高校学生。 使用场景及目标:适用于研究和开发无人艇编队控制系统,特别是希望通过分布式控制实现高效、灵活的编队任务。目标是在复杂的海洋环境中,使无人艇能够自主完成编队、跟踪指定路径并应对各种干扰因素。 其他说明:文中提供的代码片段和理论解释有助于理解和实现无人艇编队控制的实际应用。建议读者在实验过程中结合实际情况进行参数调整和优化。
(3)编写程序验证FIFO和Stack LRU页面置换算法 (4)分别用FIFO和Stack LRU页置换算法,自己设定一个页面引用序列,绘制页错误次数和可用页帧总数的曲线并对比(可用Excel绘制或手绘);能否重现FIFO导致的Belady异常; (5)[选做]编程实现最优页置换算法,用课件上的序列验证。
一个用于骨折分类的医学图像数据集,旨在通过计算机视觉技术帮助研究人员和医疗专业人员准确识别和分类骨折类型。以下是关于该数据集的详细介绍。该数据集包含了多种类型的骨折X光图像,涵盖了常见的骨折类别,如撕脱性骨折(Avulsion Fractures)、粉碎性骨折(Comminuted Fractures)、骨折脱位(Fracture-Dislocations)、青枝骨折(Greenstick Fractures)、发际线骨折(Hairline Fractures)、嵌插性骨折(Impacted Fractures)、纵向骨折(Longitudinal Fractures)、斜行骨折(Oblique Fractures)、病理性骨折(Pathological Fractures)和螺旋形骨折(Spiral Fractures)等。多样性:数据集中的图像来自不同的骨折类型,能够为模型训练提供丰富的样本。高质量标注:数据由专业放射科医生手动标记,确保了数据的准确性和可靠性。适用性:该数据集适用于机器学习和深度学习项目,可用于开发自动化骨折分类系统。该数据集主要用于训练和验证计算机视觉模型,以实现从X光图像中自动识别和分类骨折类型。通过自动化骨折分类,可以提高医疗诊断的效率和准确性,减少人为误判,并帮助医疗专业人员更快地做出决策。是一个极具价值的医学图像数据集,能够为医疗领域的研究人员和从业者提供有力支持,推动医学影像分析技术的发展。
本书《互联网的历史与数字未来》由约翰尼·瑞安撰写,探讨了互联网从诞生到成为全球性现象的历程。书中分为三个阶段:分布式网络与离心思想的兴起、互联网的扩展以及新兴环境下的互联网。第一阶段追溯了互联网概念的起源,包括冷战背景下的军事实验和计算机技术的普及。第二阶段描述了互联网如何从军事网络演变为全球互联网,并催生了万维网。第三阶段则探讨了Web 2.0的出现、网络社会的形成以及互联网对政治、文化和商业的深远影响。瑞安强调了互联网作为离心力、用户驱动和开放性的三个核心特征,并指出这些特征正在重塑我们的世界。
进程封包截取神器,支持TCP和UDP协议封包拦截
最新版kibana-9.0.0-linux-x86_64.tar.gz
子查询练习题,多练习总没有坏处,不知道凑没凑够十一个字
内容概要:本文详细介绍了如何利用Matlab计算二氧化钒(VO2)在可见光到近红外波段的介电常数,并将其应用于COMSOL多物理场仿真软件进行光学性能仿真。主要内容包括:VO2在不同温度下的相变特性及其对折射率的影响;基于Lorentz和Drude模型的介电常数计算方法;Matlab代码实现步骤;COMSOL中材料参数的导入与设置;以及常见错误提示和解决方案。文中还附带了一个详细的30分钟教学视频,帮助读者更好地理解和掌握整个流程。 适合人群:对光学材料、相变材料感兴趣的科研工作者和技术人员,尤其是从事智能窗户、光学开关等领域研究的人士。 使用场景及目标:① 学习并掌握VO2在不同温度下的光学特性和相变机制;② 利用Matlab和COMSOL进行材料参数计算和仿真,为实际应用提供理论支持;③ 解决仿真过程中可能出现的问题,提高仿真精度。 阅读建议:建议读者跟随文中的代码示例逐步操作,结合提供的教学视频加深理解。对于初学者来说,可以先熟悉Matlab的基本语法和COMSOL的操作界面,再尝试完成完整的仿真流程。
内容概要:本文详细介绍了利用COMSOL Multiphysics进行激光打孔过程中热应力耦合仿真的具体步骤和技术要点。首先,通过建立波动光学和固体力学两个物理场,精确模拟了1064nm激光与材料相互作用产生的温度场变化及其引起的热膨胀效应。接着,针对热源加载、网格划分、求解器配置等方面进行了深入探讨,提出了多项创新性的解决方案,如采用移动高斯热源实现精准加热、引入时间条件判断调整热膨胀系数以及优化网格布局等措施。此外,还讨论了材料参数设置中的注意事项,尤其是对于高温合金材料,在不同温度区间内的导热系数和弹性模量的变化规律,并强调了相变潜热的影响。最后,通过对温度场和应力场的综合分析,揭示了激光移动速度对孔洞边缘应力分布的影响机制。 适用人群:从事激光加工、材料科学、热力学研究的专业人士,以及对多物理场耦合仿真感兴趣的科研工作者。 使用场景及目标:适用于希望深入了解激光打孔过程中热应力形成机理的研究人员;旨在提高加工精度、减少缺陷发生的工程技术人员;希望通过理论模型指导实际生产的制造业从业者。 其他说明:文中提供了大量MATLAB代码片段用于辅助理解和实施相关操作,同时分享了许多实用的经验技巧,帮助读者更好地掌握COMSOL软件的应用。