This presentation is an HTML5 website

Press key to advance.

Zoom in/out: Ctrl or Command + +/-

Browser
performance

Sorry; your browser does not support svg! Sorry; your browser does not support svg!

http://www.meetup.com/framsia/

< 100ms

"0.1 second [100ms] is about the limit for having the user feel that the system is reacting instantaneously,
meaning that no special feedback is necessary except to display the result."
Jakob Nielsen

What affects
performance

JavaScript - Rendering - Network

Not a JavaScript focus - for now...

Bad:

var i,
    items = ['a','b','c','d','e'];

for ( i = 0; i < items.length; i++ ) {
    // Do stuff
}

Good:

var i, li,
    items = ['a','b','c','d','e'];

for ( i = 0, il = items.length; i < il; i++ ) {
    // Do stuff faster
}
Use Strict Mode

Maybe good:

(function ( lib ) {

    "use strict";

    // Your code...

}( window.lib ));

Rendering

The HTML
<html>
    <head>
        <title>Awesomeness!</title>
    </head>
    <body>
        <p>
            This is
            awesome!
        </p>
        <img src="cool.png">
        <div style="display: none;">
            Hidden picaboo content
        </div>
    </body>
</html>
The DOM tree and render tree

DOM tree:

html
    head
        title
    body
        p
            [text node]
        img
        div
            [text node]

Render tree:

root
    body
        p
            text line 1
            text line 2
        img
DOM tree holds all nodes, hidden elements etc. Render tree is a visual part of the DOM tree.

Keep the number of elements to a minimum

Less time spent building the DOM and render tree

Repaint

Happens when something changes without altering the page layout.
Ex: Changing the background color of a element causes a repaint.

Repaint can also be referred to as a redraw.

Reflow

Happens when something changes so it does alter the page layout.
Ex: Changes to a elements width / height can cause a reflow.

Reflow can also be referred to as layout.
Both repaint and reflow are expensive. A reflow are the most expensive.
These triggers reflow

Element:

clientHeight, clientLeft, clientTop, clientWidth, focus(), getBoundingClientRect(), getClientRects(), innerText, offsetHeight, offsetLeft, offsetParent, offsetTop, offsetWidth, outerText, scrollByLines(), scrollByPages(), scrollHeight, scrollIntoView(), scrollIntoViewIfNeeded(), scrollLeft, scrollTop, scrollWidth

Frame, Image:

height, width

Range

getBoundingClientRect(), getClientRects()

SVGLocatable

computeCTM(), getBBox()

SVGTextContent

getCharNumAtPosition(), getComputedTextLength(), getEndPositionOfChar(), getExtentOfChar(), getNumberOfChars(), getRotationOfChar(), getStartPositionOfChar(), getSubStringLength(), selectSubString()

SVGUse

instanceRoot

window

getComputedStyle(), scrollBy(), scrollTo(), scrollX, scrollY, webkitConvertPointFromNodeToPage(), webkitConvertPointFromPageToNode()

Avoid
redraws and reflows!

Totally avoiding redraws and reflows are almost impossible. Keep them to a minimum!

Set width and height

Bad:

<img src="foo.png">

Good:

<img src="foo.png" width="400" height="400">
And don't scale the image in the browser...
Break out with position fixed / absolute
.fooLayer {
    position:   absolute;
    top:        400px;
    left:       200px;
    width:      200px;
    height:     200px;
}
Changes to a absolute / fixed element happens inside the element
Batch read and write operations

Bad:

var e1 = $('#e1').css('left');
$('#e3').css('left', e1).css('width', 100).css('height', 100);

var e2 = $('#e2').css('left');
$('#e4').css('left', e2).css('width', 100).css('height', 100);

Good:

var e1 = $('#e1').css('left');
var e2 = $('#e2').css('left');

$('#e3').css('left', e1).css('width', 100).css('height', 100);
$('#e4').css('left', e2).css('width', 100).css('height', 100);
Browsers try to optimize operations by batching them.
Work outside the document tree

Bad:

var i   = 100, li, txt,
    ul  = document.getElementById( 'ulElement' );

while ( i-- ) {
    li  = document.createElement( 'li' );
    txt = document.createTextNode( i );
    li.appendChild( txt );
    ul.appendChild( li );
}

Good - Build fragments:

var i   = 100, li, txt,
    ul  = document.createDocumentFragment( 'ul' );

while ( i-- ) {
    li  = document.createElement( 'li' );
    txt = document.createTextNode( i );
    li.appendChild( txt );
    ul.appendChild( li );
}

document.body.appendChild( ul );
Work outside the document tree

Good - Clone:

var i       = 100, li, txt,
    ul      = document.getElementById( 'ulElement' ),
    clone   = ul.cloneNode( true );

while ( i-- ) {
    li  = document.createElement( 'li' );
    txt = document.createTextNode( i );
    li.appendChild( txt );
    clone.appendChild( li );
}

ul.parentNode.replaceChild( clone, ul );
Work outside the document tree

Good - Invisible elements:

var i   = 100, li, txt,
    ul  = document.getElementById( 'ulElement' );

ul.style.display = 'none';

while ( i-- ) {
    li  = document.createElement( 'li' );
    txt = document.createTextNode( i );
    li.appendChild( txt );
    ul.appendChild( li );
}

ul.style.display = 'block';
Cache DOM values

Bad:

document.getElementById('foo').title = 'Foo element';
document.getElementById('foo').style.height = '200px';
document.getElementById('foo').style.width = '200px';
document.getElementById('foo').style.backgroundColor = 'orange';

Good:

var fooEl = getElementById('foo');
fooEl.title = 'Foo element';
fooEl.style.height = '200px';
fooEl.style.width = '200px';
fooEl.style.backgroundColor = 'orange';
Cache measurements values

Bad:

var fooEl = getElementById('foo');

fooEl.style.fontSize = ( fooEl.offsetWidth / 10 ) + 'px';
fooEl.firstChild.style.marginLeft = ( fooEl.offsetWidth / 20 ) + 'px';
fooEl.style.left = ( ( -1 * fooEl.offsetWidth ) / 2 ) + 'px';

Good:

var fooEl = getElementById('foo');

var width = fooEl.offsetWidth;
fooEl.style.fontSize = ( width / 10 ) + 'px';
fooEl.firstChild.style.marginLeft = ( width / 20 ) + 'px';
fooEl.style.left = ( ( -1 * width ) / 2 ) + 'px';
Avoid setting inline styles

Bad:

var fooEl = getElementById('foo');

fooEl.style.backgroundColor = '#333';
fooEl.style.color           = '#999';
fooEl.style.border          = '1px solid #999';

Better:

var fooEl = getElementById('foo');
fooEl.setAttribute('style', 'background-color: #333; color: #999; ... ');

Good:

.fooStyle {
    background-color:   #333;
    color:              #999;
    border:             1px solid #999;
}
var fooEl = getElementById('foo');
fooEl.className = 'fooStyle';
Don't modify what you traverse

Bad:

var pEls = document.getElementsByTagName('p'),
    txt;

for ( var i = 0; i < pEls.length; i++ ) {
    txt = document.createTextNode( i );
    pEls[ i ].appendChild( txt );
}

Good:

var pEls = document.getElementsByTagName('p'),
    txt,
    cache = [];

for ( var i = 0; i < pEls.length; i++ ) {
    cache[ cache.length ] = pEls[ i ];
}

for( i = 0; i < cache.length; i++ ) {
    txt = document.createTextNode( i );
    cache[ i ].appendChild( txt );
}
Some DOM collections are live!

Separate
responsibility

Animations

Old fashion - JavaScript:

$("#fooEl").animate({ left: 300 }, 200 )

New fashion - CSS Animations:

.animateLinear {
    left:                       300px;
    -webkit-transition:         all 200ms linear;
    -moz-transition:            all 200ms linear;
    -o-transition:              all 200ms linear;
    transition:                 all 200ms linear;
}
B/W approach for older browsers?
Hardware acceleration

CSS:

.animateLinear {
    left:                       300px;
    -webkit-transition:         all 200ms linear;
    -moz-transition:            all 200ms linear;
    -o-transition:              all 200ms linear;
    transition:                 all 200ms linear;

    -webkit-transform:          translate3d(0, 0, 0);
    transform:                  translate3d(0, 0, 0);
}
Pushes the element as a layer into the GPU
Event delegation
<html>
    <head>
        <title>Awesomeness!</title>
    </head>
    <body>
        <div>
            <div>
                <-- Some content -->
            </div>
            <div>
                <div id="foo">
                    Click foo!
                </div>
                <div id="bar">
                    Click bar!
                </div>
            </div>
        </div>
    </body>
</html>
Events does bubble. Use that!
Event delegation

Bad:

var fooEl = document.getElementById('foo'),
    barEl = document.getElementById('bar');

fooEl.addEventListener('click', function () {
    // Do something foo'ish
})

barEl.addEventListener('click', function () {
    // Do something bar'ish
})

Good:

window.addEventListener('click', function ( ev ) {
    var id = ev.target.getAttribute('id');

    if ( id === 'foo' ) {
        // Do something foo'ish
    } else if ( id === 'bar') {
        // Do something bar'ish
    }
})
We can even 'block' all events at once

Remove unused
CSS styles

The browser needs to evaluate them

CSS - Avoid

Redundant stuff:

div#foo { }

Descendant universal selectors:

body * { }
.fooElement * { }

Child or adjacent universal selectors:

body > * { }
.fooBox > * { }
CSS - Avoid

Descendant tag selectors:

ul li a { }
#head h1 { }
* html #fooBox ul li a { }

Child or adjacent tag selectors:

ul > li > a { }
#header > h3 { }

<script> and <link>
blocks rendering

Inline is king. CSS in header, scripts in the bottom.

Specify a character set
in the HTTP response
headers

Browser can't correctly render a page without knowing how to construct the page's characters

Know your tools

"After running CSS Lint on our code base and fixing the reported problems,
we improved page rendering by about 200 milliseconds"
eBay
http://www.ebaytechblog.com/2011/10/14/the-new-ebay-motors-homepage-is-2x-faster/

Thnx!

@trygve_lie

http://www.trygve-lie.com/