1. Secrets of
JavaScript Libraries
(Left to Right)
Sam Stephenson (Prototype)
Alex Russell (Dojo)
Thomas Fuchs (Script.aculo.us)
Andrew Dupont (Prototype)
John Resig (jQuery)
2. What to Cover
✦ Topics:
✦ JavaScript Language
✦ Cross-Browser Code
✦ Events
✦ DOM Traversal
✦ Style
✦ Animations
✦ Distribution
✦ HTML Insertion
4. // Set up a class and create an element
var Clock = Class.create({
initialize: function() {
this.createElement();
},
createElement: function() {
this.element = new Element(quot;divquot;);
}
});
5. // Display the time
var Clock = Class.create({
initialize: function() {
this.createElement();
},
createElement: function() {
this.element = new Element(quot;divquot;);
var date = new Date();
this.element.update(
date.getHours() + quot;:quot; +
date.getMinutes().toPaddedString(2) + quot;.quot; +
date.getSeconds().toPaddedString(2)
);
}
});
$(document.body).insert(new Clock().element);
6. // Add the timer
var Clock = Class.create({
initialize: function() {
this.createElement();
this.createTimer();
},
createElement: function() {
this.element = new Element(quot;divquot;);
},
updateElement: function() {
var date = new Date();
this.element.update(
date.getHours() + quot;:quot; +
date.getMinutes().toPaddedString(2) + quot;.quot; +
date.getSeconds().toPaddedString(2)
);
},
createTimer: function() {
window.setInterval(500, this.updateElement.bind(this));
}
});
19. Distill to a boolean
(for stuff in the gray area)
var thinksCommentsAreElements = false;
if ( document.createElement('!') ) {
thinksCommentsAreElements = true;
}
33. 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(){...} ], ... }
};
34. Then clean up
on page unload
So that IE doesn’t keep them
in memory in perpetuum
41. Plugging the leak
// In Prototype, will remove all event listeners from an element
Event.stopObserving(someElement);
Event.purgeObservers = function(element, includeParent) {
Element.select(element, quot;*quot;).each(Event.stopObserving);
if ( includeParent ) Event.stopObserving( element );
};
42. Redefining functions
add “before advice” to functions
that need to remove elements
45. Custom Events
• Everything is event based if you squint
• DOM is a good foundation
• Terrible for stitching together non-DOM
components and code
• Composition == good, inheritance == bad
• Custom events let us join loosely
• When to use them? Pitfalls?
48. Selector Internals
• Optimized DOM
• Top-down vs. bottom-up
• Caching + winnowing
• XPath
• Native, aka: querySelectorAll()
49. 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
51. Computed Style
✦ IE way vs. Everyone else
✦ IE returns “actual value”
✦ Everyone else returns “pixel value”
✦ font-size: 2em;
IE: “2em”
Other: 24
✦ Need to convert to common base
✦ Performance: Very costly, generally avoided
wherever possible
52. 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
53. 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”) == “”;
72. 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
74. Packaging
• The build-process divide
• Library support vs. server concat/shrink
• Can we strip “dead” code?
• Social artifacts of packaging systems
75. HTML Insertion
✦ $(“div”).append(“<b>foo</b>”);
✦ Convert HTML String
✦ innerHTML
✦ Range: .createContextualFragment()
✦ Must purify first:
✦ Fix <table>s for IE (<tbody> wrap)
✦ Handle <option>s (contain in <select>)
76. HTML Insertion
✦ // Fix “XHTML ”-style tags in all browsers
elem = elem.replace(/(<(w+)[^>]*?)/>/g, function(all, front, tag){
return tag.match(/^(abbr|br|col|img|input|link|meta|param|
hr|area|embed)$/i) ?
all :
front + “></” + tag + “>”;
});
✦ $(“<abbr/>”).html(“hello!”).appendTo(“#foo”);
77. 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 );
78. Questions
✦ Panelists:
✦ John Resig (ejohn.org)
✦ Sam Stephenson (conio.net)
✦ Alex Russell (alex.dojotoolkit.org)
✦ Thomas Fuchs (script.aculo.us/thomas)
✦ Andrew Dupont (andrewdupont.net)
✦ Frameworks:
✦ Prototype (prototypejs.org)
✦ jQuery (jquery.com)
✦ Dojo (dojotoolkit.org)
✦ Script.aculo.us (script.aculo.us)