Performance Guide for Tizen Web Applications(2): Rendering

Optimizing Rendering Performance

There are several ways to improve the rendering performance, including initializing the application early, keeping the UI responsive, minimizing document reflow, and updating the page only partially.

1 Starting Initialization as Early as Possible

The initialization code of a Web application is typically placed in the event handler of the window load (onload) event, which is triggered when a resource and its dependent resources have finished loading. However, earlier initialization makes the Web application load and display faster. To initialize the Web application earlier, use the DOMContentLoaded event of HTML5. Like the onload event, the DOMContentLoaded event is triggered only once while loading HTML.

For more information, see: https://developer.mozilla.org/en-US/docs/Web/Events

 

Figure 6: DOMContentLoaded and window load events in the Web Inspector

As shown in [Figure 6], the DOMContentLoaded event is always triggered before the onload event. You can start initializing your Web application earlier than the onload event by implementing the initialization code in the DOMContentLoaded event handler. However, be careful in using this event, since not all resources are fully loaded when the DOMContentLoaded event is triggered.

The following table shows how to implement handlers for the DOMContentLoaded and onload events.

 

Table 2: Implementing DOMContentLoaded and onload event handlers

DOMContentLoaded
Window load (onload)
//EventListner
document.addEventListener("DOMContentLoaded", function(event) {});

//use readyState attribute (in case of IE<9)
document.onreadystatechange = function () {
    if (document.readyState == "complete") {}
}

//JQuery
$(document).ready(function(){})
//EventListner
windowt.addEventListener(“load", function(event) {});   


//window object event
window.onload=function(){}


//JQuery
$(window).load(function(){})

2 Keeping the UI Responsive

JavaScript execution and UI rendering share a single processing thread. This means that long-running scripts block UI updates as well as the execution of other scripts. To make your Web application responsive, use web workers to run scripts in the background. Web workers create worker threads and execute scripts without affecting UI updates and the execution of other scripts. However, note that web workers are generally expected to be long-lived, since they have a high start-up performance cost and a high per-instance memory cost [3].

The following example shows how to use web workers effectively:

var request = new XMLHttpRequest();

// Set synchronous request
request.open('GET', '/bar/foo.txt', false); 
request.send(null);

// Blocked
if (request.status === 200) { 
    console.log(request.responseText); 
}
// main.js
var worker = new Worker("myTask.js"); 
worker.onmessage = function(event) { 
    alert("Worker said: " + event.data); 
}; 
worker.postMessage(“RUN");

// Continue without blocking

// myTask.js
self.onmessage = function (event) { 
    if (event.data === “RUN") { 
        var xhr = new XMLHttpRequest(); 
        // Set synchronous request
        xhr.open('GET', '/bar/foo.txt', false); 
        xhr.send(null); 
        self.postMessage(xhr.responseText); 
    } 
};

3 Minimizing Document Reflow

Reflow is the sequence of processes for re-calculating the positions and geometry of elements in the document, for the purpose of re-rendering a part or all of the document. Reflow is triggered when you: [5][6]

- Load the HTML document for the first time.

- Add or remove a DOM node.

- Animate or move a DOM node.

- Apply a style dynamically.

- Retrieve a measurement that must be calculated.

Since reflow is a costly operation that blocks user interaction, improving reflow time is the key to optimizing the performance of your Web application.

This section describes how to minimize reflow. [1]

 

  • Minimizing Trigger Reflow [1]

Since reflow carries a relatively high cost, modern Web browsers try to minimize the number of reflows by queuing changes to the render tree and executing them in batches. However, there are some properties and methods that are related to current layout information and that always trigger a reflow:

- offsetTop, offsetLeft, offsetWidth, offsetHeight    

- scrollTop, scrollLeft, scrollWidth, scrollHeight 

- clientTop, clientLeft, clientWidth, clientHeight   

- getComputedStyle()

Thus, to improve performance, either minimize the number of requests for layout information or cache the layout information to custom variables when you change a style.

 

The following example shows how to reduce reflow by changing the order in which you request layout information:

var newWidth = aDiv.offsetWidth + 10; // Read
aDiv.style.width = newWidth + 'px'; // Write
var newHeight = aDiv.offsetHeight + 10; // Read
aDiv.style.height = newHeight + 'px'; // Write
var newWidth = aDiv.offsetWidth + 10; // Read
var newHeight = aDiv.offsetHeight + 10; // Read
aDiv.style.width = newWidth + 'px'; // Write
aDiv.style.height = newHeight + 'px'; // Write!

 

The following example shows how to reduce reflow by caching layout information:

// inefficient
myElement.style.left = 1 + myElement.offsetLeft + 'px';
if (myElement.offsetLeft >= 500) {
    stopAnimation();
}
var current = myElement.offsetLeft +1;
myElement.style.left = current + 'px';
myElement.style.top = current + 'px';
if (current >= 500) {
    stopAnimation();
}

 

  • Batching Multiple DOM Changes [10]

To reduce the number of reflows:

  1. Take the element out of the document flow.
  2. Apply multiple changes.
  3. Bring the element back to the document flow.

 

This process causes 2 reflows: one at step 1 and another at step 3. If you omit those steps, every change you make in step 2 can cause its own reflow.

The following example shows how to update a list with data. The last 2 lines of this code cause reflows and requires optimization.

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);
    }
};

//Causes refolws
var ul = document.getElementById('mylist');
appendDataToElement(ul, data);

The following examples show alternative ways to modifying the red part in the above example to effectively apply multiple DOM changes.

 

Changing Element Visibility: style.display=’none’

Hide the element by using the display attribute, update the element, and then show the element:

var ul = document.getElementById('mylist');
ul.style.display = 'none';
appendDataToElement(ul, data);
ul.style.display = 'block';

 

Using a Cloned Node: cloneNode()

Clone the node you want to update, update the clone, and then replace the original node with the updated clone:

var old = document.getElementById('mylist');
var clone = old.cloneNode(true);
appendDataToElement(clone, data);
old.parentNode.replaceChild(clone, old);

 

Creating a Document Fragment: createDocumentFragment()

Create a DOM fragment, update it outside the document, and then append it to the document:

var fragment = document.createDocumentFragment();
appendDataToElement(fragment, data);
document.getElementById('mylist').appendChild(fragment);

 

  • Batching Multiple Style Changes

The DOM is a very slow structure. Using inline styles generates multiple reflows across the document, whereas applying an external style class that combines multiple style definitions generates only one reflow [1].

The following example shows how to change an element's style by using inline style definitions. This approach is inefficient, as it causes overlapping reflows and repaints.

var toChange = document.getElementById('elem');
toChange.style.background = '#333';
toChange.style.color = '#fff';
toChange.style.border = '1px solid #ccc';

The following example shows how to make the same changes efficiently by using an external style class:

/* CSS */
#elem { border:1px solid #000; color:#000; background:#ddd; }
.highlight { border-color:#00f; color:#fff; background:#333; }
/* js */
document.getElementById('elem').className = 'highlight';

 

  • Configuring an Animation Element position Attribute

Dimension and position animations with JavaScript (especially jQuery) and CSS3 normally generate considerable reflow. You can make animations less costly by setting an animation element’s position property to ‘fixed’ or ‘absolute’, which causes that element to have no effect on the layout of other elements [1].

 

  • Rearranging Low Selectors of CSS

CSS recalculation causes reflow. You can use CSS rule matching, the performance improves, as there are less rules to handle [1].

The following examples show what happens when the btn_more class of the list_service property is unique.

.section_service .list_service li .box_name .btn_more
{display:block;width:100px;height:30px;}
.section_service .list_service .btn_more 
{display:block;width:100px;height:30px;}

Example 1 is more easily redable from the maintenance point of view, but the performance suffers when the rules are on level 5. When the length of CSS code is 500 to 1000 lines, the effect is significant. Because of this, CSS rules must be defined like in example 2 to diminish the number of low selectors.

4 Updating Page Content Only Partially Using AJAX

Loading a new HTML document carries the additional cost of building a new DOM and render tree as well as requesting new resources, as shown in [Figure 7]. Therefore, for best performance, update only a part of a page rather than the whole page. AJAX is a collection of Web development techniques that apply this concept to Web applications.

 

Figure 7: Comparison between whole page update and partial page update

 

AJAX allows Web pages to be updated asynchronously by exchanging small amounts of data with the server in the background. AJAX makes it possible to update only parts of a web page without reloading the whole page.

For more information, see: https://developer.mozilla.org/en-US/docs/AJAX/Getting_Started

 

 

 

References

[1] Performance Guide for Web App: http://www.samsungdforum.com/Guide/d60/index.html

[2] Best Practices for Speeding Up Your Web Site: https://developer.yahoo.com/performance/rules.html

[3] Web workers: http://www.whatwg.org/specs/web-apps/current-work/multipage/workers.html#workers

[4] Minify Resources (HTML, CSS, and JavaScript): https://developers.google.com/speed/docs/insights/MinifyResources

[5] Notes on HTML Reflow: http://www-archive.mozilla.org/newlayout/doc/reflow.html

[6] Minimizing browser reflow: https://developers.google.com/speed/articles/reflow

[7] 10 JavaScript Performance Boosting Tips from Nicholas Zakas: http://jonraasch.com/blog/10-javascript-performance-boosting-tips-from-nicholas-zakas

[8] JavaScript Performance Tips, http://www.codeproject.com/Tips/623082/JavaScript-Performance-Tips

[9] Writing Efficient JavaScript: Chapter 7 - Even Faster Websites, http://archive.oreilly.com/pub/a/server-administration/excerpts/even-faster-websites/writing-efficient-javascript.html

[10] High Performance JavaScript, Nicholas C. Zakas