Escolar Documentos
Profissional Documentos
Cultura Documentos
JavaScript Libraries
(Left to Right)
Sam Stephenson (Prototype)
Alex Russell (Dojo)
Thomas Fuchs (Script.aculo.us)
Andrew Dupont (Prototype)
John Resig (jQuery)
What to Cover
✦ Topics:
✦ JavaScript Language
✦ Cross-Browser Code
✦ Events
✦ DOM Traversal
✦ Style
✦ Animations
✦ Distribution
✦ HTML Insertion
Secrets of the
JavaScript Language
// Set up a class and create an element
createElement: function() {
this.element = new Element("div");
}
});
// Display the time
createElement: function() {
this.element = new Element("div");
$(document.body).insert(new Clock().element);
// Add the timer
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));
}
});
// Add some options
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)
}
}, ...
// Use #toElement
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());
// Subclass it
getTime: function($super) {
var time = $super();
if (time.hour == 0) {
time.hour = 12;
} else if (time.hour > 12) {
time.hour -= 12;
}
return time;
}
});
$(document.body).insert(new AmPmClock());
// Or monkeypatch it
Object.extend(Clock.prototype, {
format: "#{hour}:#{minute}:#{second} #{ampm}",
getTime: Clock.prototype.getTime.wrap(function(original) {
var time = original();
if (time.hour == 0) {
time.hour = 12;
} else if (time.hour > 12) {
time.hour -= 12;
}
return time;
}
});
$(document.body).insert(new Clock());
Secrets of
Cross-Browser Code
Browser Sniffing
(wait, hear me out)
Conditionally evil
In order of desirability:
capabilities
&
quirks
Capabilities
are easy to sniff
Quirks
are... more troublesome
Object detection
(for capabilities)
if (document.evaluate) {
// ...
}
Distill to a boolean
(for stuff in the gray area)
if (Prototype.Browser.IE) {
element.style.filter =
"alpha(opacity=50);";
}
Try to do the right thing,
but don’t stand on principle
The social contract
Good faith from browser makers ➝
good faith from web authors
Secrets
of Quality
Assurance
1,500
750
0
1.5.0
1.5.1
1.6.0.2
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;
Secrets of
Events
Event handlers
are tricky
Don’t store them on the
element itself
circular references = sadness
Build a global hashtable
(like jQuery does it)
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(){...} ], ... }
};
Then clean up
on page unload
So that IE doesn’t keep them
in memory in perpetuum
Fixing memory leaks
Internet Explorer 6
red-headed stepchild
Don’t “prove” your code
has no leaks
(view the results!)
Drip
is awesome
Test page:
Create a bunch of elements, assign each an event
handler, then remove each one from the page
Demonstrating the Leak
Notice the stair-step effect
Plugging the leak
// In Prototype, will remove all event listeners from an element
Event.stopObserving(someElement);
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();
Drop-in fix
for Prototype 1.6
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?
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”]);
Secrets of
DOM Traversal
Selector Internals
• Optimized DOM
• Top-down vs. bottom-up
• Caching + winnowing
• XPath
• Native, aka: querySelectorAll()
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
Secrets of
Style
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
// 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;
}
width
clientWidth
offsetWidth
Computed styles
getting padding & border value
The fool-proof,
painful way
Take the offsetWidth,
then subtract computed padding & border
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"));
};
Secrets
of Animation
Old-School
“Effects”
setInterval
Events-based
Secrets
ofJavaScript
Deployment
CONTENT
EXPIRATION
Concatenation
GZIP
4-5x smaller
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
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
Packaging
✦ $(“<abbr/>”).html(“hello!”).appendTo(“#foo”);
Script Execution
✦ Execute Script in Global Scope
script.type = “text/javascript”;
if ( jQuery.browser.msie )
script.text = data;
else
script.appendChild( document.createTextNode( data ) );
head.appendChild( script );
head.removeChild( script );
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)