secrets of javascript libraries 1205311956392030 5

Upload: donny-kurnia

Post on 09-Apr-2018

220 views

Category:

Documents


0 download

TRANSCRIPT

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    1/78

    Secrets of

    JavaScript Libraries(Left to Right)Sam Stephenson (Prototype)

    Alex Russell (Dojo)

    Thomas Fuchs (Script.aculo.us)Andrew Dupont (Prototype)

    John Resig(jQuery)

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    2/78

    What to Cover Topics: JavaScript Language Cross-Browser Code

    Events DOM Traversal Style Animations

    Distribution HTML Insertion

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    3/78

    Secrets of theJavaScript Language

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    4/78

    // Set up a class and create an element

    var Clock = Class.create({

    initialize: function() {

    this.createElement();

    },

    createElement: function() {

    this.element =new Element("div");

    }

    });

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    5/78

    // Display the time

    var Clock = Class.create({

    initialize: function() {

    this.createElement();

    },

    createElement: function() {

    this.element =new Element("div");

    var date =new Date();

    this.element.update(date.getHours() +":"+

    date.getMinutes().toPaddedString(2) +"."+

    date.getSeconds().toPaddedString(2)

    );

    }

    });

    $(document.body).insert(new Clock().element);

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    6/78

    // Add the timer

    var Clock = Class.create({

    initialize: function() {

    this.createElement();

    this.createTimer();},

    createElement: function() {

    this.element =new Element("div");

    },

    updateElement: function() {

    var date =new Date();

    this.element.update(

    date.getHours() +":"+

    date.getMinutes().toPaddedString(2) +"."+

    date.getSeconds().toPaddedString(2));

    },

    createTimer: function() {

    window.setInterval(500, this.updateElement.bind(this));

    }});

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    7/78

    // Add some options

    var Clock = Class.create({

    color: "black",format: "#{hour}:#{minute}.#{second}",

    initialize: function(options) {

    Object.extend(this, options); this.createElement();

    this.createTimer();},

    createElement: function() {

    this.element =new Element("div");

    this.element.setStyle({ color: this.color });},

    updateElement: function() { this.element.update(this.format.interpolate(this.getTime()));

    },

    getTime: function() { var date =new Date();

    return {hour: date.getHours(),

    minute: date.getMinutes().toPaddedString(2),second: date.getSeconds().toPaddedString(2)

    }

    }, ...

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    8/78

    // Use #toElement

    var Clock = Class.create({

    ...

    toElement: function() { returnthis.element;

    }

    });

    $(document.body).insert(new Clock());$(document.body).down("div.clock > div").replace(new Clock());

    $(document.body).down("div.clock").update(new Clock());

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    9/78

    // Subclass it

    var AmPmClock = Class.create(Clock, {

    format: "#{hour}:#{minute}:#{second} #{ampm}",

    getTime: function($super) { var time =$super();

    time.ampm = time.hour 12) {

    time.hour -=12;

    }

    return time;

    }});

    $(document.body).insert(new AmPmClock());

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    10/78

    // Or monkeypatch it

    Object.extend(Clock.prototype, {

    format: "#{hour}:#{minute}:#{second} #{ampm}",

    getTime: Clock.prototype.getTime.wrap(function(original) {

    var time = original();

    time.ampm = time.hour 12) {

    time.hour -=12;

    }

    return time;

    }});

    $(document.body).insert(new Clock());

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    11/78

    Secrets ofCross-Browser Code

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    12/78

    Browser Sniffing(wait, hear me out)

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    13/78

    Conditionally evil

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    14/78

    In order of desirability:

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    15/78

    capabilities&

    quirks

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    16/78

    Capabilitiesare easy to sniff

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    17/78

    Quirksare... more troublesome

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    18/78

    if(document.evaluate) { // ...}

    Object detection(for capabilities)

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    19/78

    Distill to a booleanvar thinksCommentsAreElements = false;if( document.createElement('!') ) {thinksCommentsAreElements = true;

    }

    (for stuff in the gray area)

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    20/78

    ...then sniff(for outright bugs/quirks)if(Prototype.Browser.IE) {element.style.filter =

    "alpha(opacity=50);";}

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    21/78

    Try to do the right thing,but dont stand on principle

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    22/78

    The social contractGood faith from browser makersgood faith from web authors

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    23/78

    Secretsof Quality

    Assurance

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    24/78

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    25/78

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    26/78

    0

    750

    1,500

    1.5.01.5.1

    1.6.0.2

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    27/78

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    28/78

    Debugging Localize & Reproduce Find the smallest possible code that

    generates the problem

    Easy test-case generation ./gen.sh 1245 ajax

    ./gen.sh 1246 dom

    Simple logging, all browsers:document.getElementById(output)

    .innerHTML +=
    + msg;

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    29/78

    Secrets ofEvents

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    30/78

    Event handlersare tricky

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    31/78

    Dont store them on the

    element itselfcircular references = sadness

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    32/78

    Build a global hashtable(like jQuery does it)

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    33/78

    Element Data Store Each element gets a unique ID

    (bound w/ a unique property)elem.jQuery12345 = 1;

    Points back to large data structure:data[1] = { ... all element data here ... };

    Data(like event handlers) are stored here

    data[1] = {handlers: { click: [ function(){...}], ... }

    };

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    34/78

    Then clean up

    on page unloadSo that IE doesnt keep themin memory in perpetuum

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    35/78

    Fixing memory leaks

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    36/78

    Internet Explorer 6red-headed stepchild

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    37/78

    Dont prove your code

    has no leaks(view the results!)

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    38/78

    Dripis awesome

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    39/78

    Test page:Create a bunch of elements, assign each an eventhandler, then remove each one from the page

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    40/78

    Demonstrating the Leak

    Notice the stair-step effect

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    41/78

    Plugging the leak// In Prototype, will remove all event listeners from an elementEvent.stopObserving(someElement);

    Event.purgeObservers = function(element, includeParent) { Element.select(element, "*").each(Event.stopObserving);if ( includeParent ) Event.stopObserving( element );

    };

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    42/78

    Redefining functionsadd before advice to functionsthat need to remove elements

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    43/78

    Code sam leElement.Methods.update = Element.Methods.update.wrap(function(proceed, element, contents) {

    Event.purgeObservers(element); return proceed(element, contents);}

    );

    Element.Methods.replace = Element.Methods.replace.wrap(function(proceed, element, contents) {

    Event.purgeObservers(element, true); return proceed(element, contents);}

    );

    Element.Methods.remove = Element.Methods.remove.wrap(function(proceed, element) {

    Event.purgeObservers(element, true); return proceed(element);}

    );

    Element.addMethods();

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    44/78

    Drop-in fix

    for Prototype 1.6

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    45/78

    Custom Events

    Everything is event based if you squint

    DOM is a good foundation

    Terrible for stitching together non-DOMcomponents and code

    Composition == good, inheritance == bad Custom events let us join loosely

    When to use them? Pitfalls?

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    46/78

    Custom Events (contd.)// in Dojo:

    dojo.subscribe(/foo, function(e, arg){ ... });

    dojo.publish(/foo, [{ data: thinger}, second arg]);

    // in Prototype:

    document.observe(event:foo, function(e){ ... });

    $(nodeId).fire(event:foo, { data: thinger });

    // in jQuery:

    $(document).bind(foo, function(e, data, arg){ ... });

    $(document).trigger(foo, [{ data: thinger}, second]);

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    47/78

    Secrets ofDOM Traversal

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    48/78

    Selector Internals

    Optimized DOM

    Top-down vs. bottom-up Caching + winnowing

    XPath

    Native, aka: querySelectorAll()

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    49/78

    Selector Internals Tradeoffs: size vs. speed vs. complexity

    Prototype: XPath when possible, else DOM

    Good perf, lower complexity, hackable Dojo: all 3 methods, picks best available

    Best perf, biggest, highest complexity

    JQuery: DOM

    Good perf, small size, extensiblity vs.forward-compat tradeoff

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    50/78

    Secrets ofStyle

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    51/78

    Computed Style IE way vs. Everyone else IE returns actual value Everyone else returns pixel value font-size: 2em;

    IE: 2emOther: 24

    Need to convert to common base

    Performance: Very costly, generally avoidedwherever possible

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    52/78

    Pixel Values in IE

    if( !/ \d+(px)?$/i.test(ret ) && /\d/.test(ret )){ // Remember the original values var style = elem.style.left, runtimeStyle = elem.runtimeStyle.left;

    // Put in the new values to get a computed value out elem.runtimeStyle.left = elem.currentStyle.left;

    elem.style.left = ret || 0; ret = elem.style.pixelLeft + px;

    // Revert the changed values elem.style.left = style; elem.runtimeStyle.left = runtimeStyle;

    }

    From Dean Edwards:http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291

    http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    53/78

    Computed Style Safari 2 & 3:

    Giant mess for display: none elements Safari 2 getComputedStyle() returns undefined

    Safari 3 Always return empty strings for value Much harder to detect

    var ret = document.defaultView.getComputedStyle(elem, null );return !ret || ret.getPropertyValue(color) == ;

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    54/78

    Finding dimensions

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    55/78

    Dimensions of what?content box, border box, margin box...

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    56/78

    DHTML propertiesclientWidth, offsetWidth

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    57/78

    The value you want is not captured by any property

    Lorem ipsum dolor sit

    amet.

    width

    clientWidth

    offsetWidth

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    58/78

    Computed stylesgetting padding & border value

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    59/78

    The fool-proof,

    painful wayTake the offsetWidth,then subtract computed padding & border

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    60/78

    Code exampleElement.getCSSWidth = function(element) { element = $(element); returnelement.ofsetWidth -

    parseFloat(element.getStyle("borderLeft")) -

    parseFloat(element.getStyle("paddingLeft")) - parseFloat(element.getStyle("paddingRight")) - parseFloat(element.getStyle("borderRight"));};

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    61/78

    Secretsof Animation

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    62/78

    Old-School

    Effects

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    63/78

    setInterval

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    64/78

    Events-based

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    65/78

    SecretsofJavaScript

    Deployment

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    66/78

    CONTENT

    EXPIRATION

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    67/78

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    68/78

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    69/78

    Concatenation

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    70/78

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    71/78

    GZIP4-5x smaller

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    72/78

    Packaging

    Dev vs. deployment constraints

    No library a single file, but all ship that way

    # of requests largest constraint

    Sync vs. async

    Static resource servers + CDNs Dependency management matters!

    Runtime vs. deployment

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    73/78

    Packaging// in Dojo:dojo.provide(foo.bar.Baz);

    dojo.require(dojox.dtl);

    // in GWT:

    package com.foo.bar;

    import com.foo.bar.Blah;

    // in JSAN:

    JSAN.use(foo.bar.Blah);

    // exports handled by build tools

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    74/78

    Packaging

    The build-process divide

    Library support vs. server concat/shrink

    Can we strip dead code?

    Social artifacts of packaging systems

    HTML I i

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    75/78

    HTML Insertion $(div).append(foo); Convert HTML String innerHTML

    Range: .createContextualFragment() Must purifyfirst: Fix s for IE ( wrap)

    Handle s (contain in )

    HTML I i

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    76/78

    HTML Insertion

    // FixXHTML-style tags in all browserselem = elem.replace(/(]*?)\/>/g, function(all, front, tag){ return tag.match(/ (abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ? all : front + >;});

    $().html(hello!).appendTo(#foo);

    S i t E ti

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    77/78

    Script Execution

    Execute Script in Global Scope var head = document.getElementsByTagName(head)[0]||

    document.documentElement, script = document.createElement(script);

    script.type = text/javascript;if( jQuery.browser.msie) script.text = data;else script.appendChild(document.createTextNode(data));

    head.appendChild(script );head.removeChild(script );

  • 8/8/2019 Secrets of Javascript Libraries 1205311956392030 5

    78/78