Slideshare.net (beta)

 
Post to TwitterPost to Twitter
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 40 (more)

Secrets of JavaScript Libraries

From jeresig, 7 months ago

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

21727 views  |  0 comments  |  37 favorites  |  632 downloads  |  65 embeds (Stats)
 

Categories

Add Category
 
 

Tags

panel libraries jquery dojo prototype javascript sxsw js resig john

more

 
 
 
Embed
options

More Info

This slideshow is Public
Total Views: 21727
on Slideshare: 10998
from embeds: 10729

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)