Slideshare.net (beta)

 
Post: 
Myspace Hi5 Friendster Xanga LiveJournal Facebook Blogger Tagged Typepad Freewebs BlackPlanet gigya icons



All comments

Add a comment on Slide 1

If you have a SlideShare account, login to comment; else you can comment as a guest


Showing 1-50 of 34 (more)

Secrets of JavaScript Libraries

From jeresig, 4 months ago

This is the presentation from the "Secrets of JavaScript Libraries more

19232 views  |  0 comments  |  31 favorites  |  530 downloads  |  65 embeds (Stats)
 

Tags

panel libraries jquery dojo prototype javascript sxsw js resig john

more

 
 
 
 

Privacy InfoNew!

This slideshow is Public

 
Embed in your blog
Embed (wordpress.com)
custom

Slideshow Statistics
Total Views: 19232
on Slideshare: 9060
from embeds: 10172* * Views from embeds since 21 Aug, 07

Slideshow transcript

Slide 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)

Slide 2: What to Cover ✦ Topics: ✦ JavaScript Language ✦ Cross-Browser Code ✦ Events ✦ DOM Traversal ✦ Style ✦ Animations ✦ Distribution ✦ HTML Insertion

Slide 3: Secrets of the JavaScript Language

Slide 4: // Set up a class and create an element var Clock = Class.create({ initialize: function() { this.createElement(); }, createElement: function() { this.element = new Element("div"); } });

Slide 5: // 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);

Slide 6: // 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)); } });

Slide 7: // 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: Clock().element); $(document.body).insert(new this.color }); }, $(document.body).insert(new Clock({ color: "red" }).element); $(document.body).insert(new Clock({ format: "#{hour}:#{minute}" }).element); 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) } }, ...

Slide 8: // Use #toElement var Clock = Class.create({ ... toElement: function() { return this.element; } }); $(document.body).insert(new Clock()); $(document.body).down("div.clock > div").replace(new Clock()); $(document.body).down("div.clock").update(new Clock());

Slide 9: // Subclass it var AmPmClock = Class.create(Clock, { format: "#{hour}:#{minute}:#{second} #{ampm}", getTime: function($super) { var time = $super(); time.ampm = time.hour < 12 ? "am" : "pm"; if (time.hour == 0) { time.hour = 12; } else if (time.hour > 12) { time.hour -= 12; } return time; } }); $(document.body).insert(new AmPmClock());

Slide 10: // 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 ? "am" : "pm"; if (time.hour == 0) { time.hour = 12; } else if (time.hour > 12) { time.hour -= 12; } return time; } }); $(document.body).insert(new Clock());

Slide 11: Secrets of Cross-Browser Code

Slide 12: Browser Sniffing (wait, hear me out)

Slide 13: Conditionally evil

Slide 14: In order of desirability:

Slide 15: capabilities & quirks

Slide 16: Capabilities are easy to sniff

Slide 17: Quirks are... more troublesome

Slide 18: Object detection (for capabilities) if (document.evaluate) { // ... }

Slide 19: Distill to a boolean (for stuff in the gray area) var thinksCommentsAreElements = false; if ( document.createElement('!') ) { thinksCommentsAreElements = true; }

Slide 20: ...then sniff (for outright bugs/quirks) if (Prototype.Browser.IE) { element.style.filter = "alpha(opacity=50);"; }

Slide 21: Try to do the right thing, but don’t stand on principle

Slide 22: The social contract Good faith from browser makers ➝ good faith from web authors

Slide 23: Secrets of Quality Assurance

Slide 26: 1,500 750 0 1.5.0 1.5.1 1.6.0.2

Slide 28: 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 += “<br>” + msg;

Slide 29: Secrets of Events

Slide 30: Event handlers are tricky

Slide 31: Don’t store them on the element itself circular references = sadness

Slide 32: Build a global hashtable (like jQuery does it)

Slide 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(){...} ], ... } };

Slide 34: Then clean up on page unload So that IE doesn’t keep them in memory in perpetuum

Slide 35: Fixing memory leaks

Slide 36: Internet Explorer 6 red-headed stepchild

Slide 37: Don’t “prove” your code has no leaks (view the results!)

Slide 38: Drip is awesome

Slide 39: Test page: Create a bunch of elements, assign each an event handler, then remove each one from the page

Slide 40: Demonstrating the Leak Notice the stair-step effect

Slide 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, "*").each(Event.stopObserving); if ( includeParent ) Event.stopObserving( element ); };

Slide 42: Redefining functions add “before advice” to functions that need to remove elements

Slide 43: Code sample Element.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();

Slide 44: Drop-in fix for Prototype 1.6

Slide 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?

Slide 46: 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”]);

Slide 47: Secrets of DOM Traversal

Slide 48: Selector Internals • Optimized DOM • Top-down vs. bottom-up • Caching + winnowing • XPath • Native, aka: querySelectorAll()

Slide 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

Slide 50: Secrets of Style

Slide 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

Slide 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

Slide 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”) == “”;

Slide 54: Finding dimensions

Slide 55: Dimensions of what? content box, border box, margin box...

Slide 56: DHTML properties clientWidth, offsetWidth

Slide 57: The value you want is not captured by any property Lorem ipsum dolor sit amet. width clientWidth offsetWidth

Slide 58: Computed styles getting padding & border value

Slide 59: The fool-proof, painful way Take the offsetWidth, then subtract computed padding & border

Slide 60: Code example Element.getCSSWidth = function(element) { element = $(element); return element.offsetWidth - parseFloat(element.getStyle("borderLeft")) - parseFloat(element.getStyle("paddingLeft")) - parseFloat(element.getStyle("paddingRight")) - parseFloat(element.getStyle("borderRight")); };

Slide 61: Secrets of Animation

Slide 62: Old-School “Effects”

Slide 63: setInterval

Slide 64: Events-based

Slide 65: Secrets ofJavaScript Deployment

Slide 66: CONTENT EXPIRATION

Slide 69: Concatenation

Slide 71: GZIP 4-5x smaller

Slide 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

Slide 73: 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

Slide 74: Packaging • The build-process divide • Library support vs. server concat/shrink • Can we strip “dead” code? • Social artifacts of packaging systems

Slide 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>)

Slide 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”);

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

Slide 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)