1. Use DocumentFragment or innerHTML to replace complex elements insertion
DOM operation on browser is expensive. Although browser performance is improved much, multiple DOM elements insertion is still expensive and will affect the page load speed.
Assume we have an ul element on our page, we now want to retrieve a JSON list using AJAX and then update the ul using JavaScript. Usually we may write it as :
var list = document.querySelector('ul'); ajaxResult.items.forEach(function(item) { // Create element var li = document.createElement('li'); li.innerHTML = item.text; // Manipulate li such as change class,change attribute,add event listener etc // Append to the list list.apppendChild(li); });
Above code is actually ineffective, every time when the li is appended, the DOM tree of the page will be refreshed and rebuilt. Instead we should use DocumentFragment. DocumentFragment is the virtual storage of a group of child nodes and it has no parent element. In our example, we can imagine DocumentFragment as an ul element. The child nodes will be injected into the DOM only once as a whole.
var frag = document.createDocumentFragment(); ajaxResult.items.forEach(function(item) { // Create li var li = document.createElement('li'); li.innerHTML = item.text; // Manipulate li such as change class, change attribute, add event listener etc // Add li to the fragment frag.appendChild(li); }); // Insert the fragment into the DOM document.querySelector('ul').appendChild(frag);
If you don't want to manipulate the DOM elements, you can directly insert HTML codes:
var htmlStr = ''; ajaxResult.items.forEach(function(item) { htmlStr += '<li>' + item.text + '</li>'; }); document.querySelector('ul').innerHTML = htmlStr;
2. Handle frequent triggered event
Usually developers may add event listeners to UI elements when users need to interact with the page and these events may be triggered frequently such as window resize and mouseover events. When they are triggered, they would be triggered many times in a short period of time and callbacks will be called many times as well. We need to limit the number of executions.
// Get from UnderscoreJS function debounce(func, wait, immediate) { var timeout; return function() { var context = this, args = arguments; var later = function() { timeout = null; if (!immediate) func.apply(context, args); }; var callNow = immediate && !timeout; clearTimeout(timeout); timeout = setTimeout(later, wait); if (callNow) func.apply(context, args); }; } // Add resize event listener,the callback will be executed every 300 milliseconds window.addEventListener('resize', debounce(function(event) { // DO SOMETHING }, 300));
debounce will return a function, this function will contain your callback function. The execution frequency can be limited.
3. Use Array.prototype.join to do string concatenation
Usually we concatenate strings using + operator, we can use Array.prototype.join as well.
For example:
htmlStr += '<li>' + item.text + '</li>';
With Array.prototype.join:
var items = []; ajaxResult.items.forEach(function(item) { items.push('<li>', item.text, '</li>'); }); document.querySelector('ul').innerHTML = items.join('');
4. Store static content
Web Storage API has a big improvement compared to Cookie API and it's sued by developers for many years. We can use it to store static content such as sidebar menus or contents loaded with AJAX.
define(function() { var cacheObj = window.sessionStorage || { getItem: function(key) { return this[key]; }, setItem: function(key, value) { this[key] = value; } }; return { get: function(key) { return this.isFresh(key); }, set: function(key, value, minutes) { var expDate = new Date(); expDate.setMinutes(expDate.getMinutes() + (minutes || 0)); try { cacheObj.setItem(key, JSON.stringify({ value: value, expires: expDate.getTime() })); } catch(e) { } }, isFresh: function(key) { var item; try { item = JSON.parse(cacheObj.getItem(key)); } catch(e) {} if(!item) return false; return new Date().getTime() > item.expires ? false : item.value; } } });
This tool provides some get and set methods, it's very simple to call these methods:
require(['storage'], function(storage) { var content = storage.get('sidebarContent'); if(!content) { // Do an AJAX request to get the sidebar content // ... and then store returned content for an hour storage.set('sidebarContent', content, 60); } });
The same content will not be loaded again. Go back and check your design and find out those content which are static and use Web Storage to store them.
5. Use CSS animation
Web design now needs large number of animations, jQuery, MooTools can help on this. Although browsers now support animation made with transformation and keyframe, many people are still using JavaScript to create animation effect. In fact using CSS is more efficient than JavaScript on creating animation and it needs less codes as well. Many CSS animations are processed by GPU, so the animation can play smoothly. You can use below codes to enforce hardware acceleration.
.myAnimation { animation: someAnimation 1s; transform: translate3d(0, 0, 0); }
If the browser doesn't support CSS animation(IE 8 and below), you can include JavaScript animation:
6. Use event delegation
Assume you have a list of links in an
- element, and you want to trigger a click event on each link, you may want to add event listener to each link,but this method is inefficient. Instead we can use event delegation in JavaScript, since event is bubbling, we just need to add an click event to the parent ul element and we then check which link is clicked in the event handler.
document.querySelector('#parent-list').addEventListener('click', function(e) { // e.target is the element being clicked // Check whether the target is a li element if(e.target && e.target.tagName == 'LI') { // Check which li element clicked and do something accordingly } });
7. Use Data URI to replace image src
To improve page load speed, we can do more beyond compressing codes. Reducing the request number to the server is also a considerable option. One way to reduce request number is to use Data URI to replace image src
<!-- Original method --> <img src="/images/logo.png" /> <!-- Use data URI --> <img src="data: image/jpeg;base64,/9j/4AAQSkZJRgABAgAAZABkAAD/7AARRHVja3kAAQAEAAAAPAAA/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoKDBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8fHx8fHx8fHx8fHx8fH ...." /> <-- Example: http://davidwalsh.name/demo/data-uri-php.php -->
The page size will increase a bit, but it reduces the numbers of request sent to the server. Most of browsers are now supporting Data URI, you can use Data URI to load background image as well in CSS.
There are many more optimization tips to come...