Why Teams call analytics are critical to your entire business
Secrets of JavaScript Libraries
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)