Friday, April 13th, 2007
Advancing JavaScript with Libraries
The Yahoo! UI team kindly invited me to listen to John Resig, of Mozilla and jQuery, talk to the team on "Advancing JavaScript with Libraries".
John wanted to talk about his experiences developing a number of JavaScript libraries,
At Mozilla John is working on FUEL, a JavaScript library that should be shipping in Firefox 3, that will reduce the complexity for developers to create add-ons.
What is a library?
- An abstraction away from an existing API
- Give us new APIs to interface with
APIs breed patterns based on how the API is constructed. Libraries will develop new patterns on top of the existing APIs.
For example, compare the DOM API versus a library that makes the DOM easier to work with. This enables library authors to enhance The Way.
Why create a library?
There are many, many libraries out there. Why write a library?
- Avoid repetition
- In JavaScript: Distance from browser differences
- In C: stdlib distances itself from the platform differences
What about the DOM API?
DOM is implemented in every modern browser, and is very stable and web documented. Even here libraries are useful.
This breaks in IE 7, and always returns an empty set:
This fails in Safari 2
These are genuine bugs in a specific strange way.
These fail in Opera and IE (return the input where the name is q)
And expandos mess you up if you use an id of length (in Opera and IE)
And these fail in all browsers (for valid reasons, but Joe User doesn't get it):
-
-
document.getElementById("name").setAttribute("disabled", false)
-
-
<input type="text" id="name" disabled="disabled" />
-
Reason: DOM doesn't grok booleans.
-
-
document.body.setAttribute("className", "home");
-
-
<body> ... </body>
-
Reason: class is reserved in JavaScript. Use className.
All of these differences cause libraries to be created.
- IE has lots of well documented bugs.
- Safari has less, but more obscure, bugs
Libraries break down issues into meta-problems
E.g.
- Waiting for the document to load
- Traversing the DOM
- Injecting HTML into a page
Waiting for the document to load
A DOM document must be fully loaded before you can work with it, and most wait for the window to load which causes flashes of un-effected content.
What people often do:
jQuery example:
Traversing the DOM
We all know it is ugly, tricky, and long-winded.
We are moving to DOM Selectors to improve the DOM Traversal API. We have looked to standards such as XPath selectors (//div/span[2]), and CSS 3 selectors (div > span:nth-of-type(2)).
Most web developers grok CSS more than XPath, however they are stuck in CSS 1-ish land due to IE.
Here are some one line solutions that would take a lot more in DOM:
-
-
$("#menu> li:not(:first-child)").hide(); // CSS 3
-
-
$("ul[ul]").show(); // XPath
-
-
$("tr:even").addClass("even"); // Users want it
-
Injecting HTML
Many issues here, such as injecting table rows, select options, etc.
jQuery and others allow you to create HTML and insert into the DOM directly.
-
-
$("table tr").append("<td>test</td>");
-
$("select").prepend("<option>test</option>");
-
Put them together and you get unobtrusive DOM scripting:
-
-
$(document).ready(function() {
-
$("select").append("<option>test</option>");
-
})
-
and it sets up new expectations:
jQuery users have thought that this should work:
The users expect these behaviours to not be running once on page load. It should be like CSS. You don't re-run CSS rules.
function handleClick() {
$("li", this).click(loadMenu);
}
function loadMenu() {
$(this).load("menu.html", handleClick);
}
$(document).ready(function() {
$(document).each(handleClick);
});
and becomes the behaviour pattern:
$(document).ready(function() {
$("li").behaviour("click", function(){
$(this).load("menu.html", handleClick);
})
FUEL
JavaScript library for Firefox Extension development, designed to be used by Web Developers (not C++ centric folk).
XPCOM is very daunting, and John shows the code for setting a preference. Lots of lines, lots of ugly XPCOM.
With FUEL:
-
-
Application.prefs.setValue("some.pref", "some non-ascii text");
-
This isn't new to JavaScript
John compares our library development to other worlds.
E.g. SQL
SELECT * FROM users WHERE id = 5;
...
to Ruby on Rails ActiveRecord::Base
Conclusion
- Libraries build new patterns on top of existing APIs
- New library patterns advance development in meaningful considerable ways
And there is more... Meta-Libraries
John is excited about the DSL movement and what it can mean to the web.
He has been playing with jquery2 which isn't version 2 of jquery, but rather a DSL for JavaScript.
Browsers ignore a script tag where the type isn't know, which means that you can have your code go back and grok and run it.
This is how the LISP in Firefox works.
Here is how jquery2 is plugged in:
Come on, time for:
-
-
<script type="text/ruby">
-
document.ready do |dom|
-
dom["table tr"] <<"<td>test"
-
end
-
</script>
-
Could get to this via JRuby and the JVM?












You’re rock john! But I think jquery lacks of array extensions
Hmm..
“document.body.setAttribute(”className”, “home”);”
Sure you don’t mean document.body.class = “home”; ?
The last one would break, the first one just doesn’t make sense..
Another thing, what about browsers like konqueror that doesn’t support xpath?
@remiss, Actually both of you are wrong.
Technically, the syntax should be:
“document.body.setAttribute(”class”,”home”);”. HOWEVER, this doesn’t work properly in IE. The content gets added to the body, however it doesn’t render. Therefore you should use “document.body.className = ‘home’;”.
In “Waiting for the document to load”, I’m sure that “document.getElementById”test”.docus();” should be “document.getElementById(”test”).focus();”
ok..the first half of the article is pretty straightforward ‘abstraction away from buggy/broken implementations is good, presents common API’, etc.
then it jumps off the deep end (is this summarizing another article?)
wtf is JQuery2? the syntax is not JS-style, eh..
and whats this about LISP in firefox?? you mean crockfordgs scheme.js?
with apologizes for mispelling crockford,
as for the Ruby code. youd need a browser running on the JVM - afaik netscape was working on it at one point - it would be nice, for sure.
if any of the folks from Yahoo! are reading this, any chance you are planning to put a mp4 version of this talk on the YUI Theatre website? flash-impaired on 64bit OSes out here in the dev jungle..
heres the link if you happen to have Flash:
http://video.yahoo.com/video/play?ei=UTF-8&b=0&vid=410472&gid=133414
Rizqi: how do you extend Array literals globally without polluting the ‘for..in’ and similar iteration? are they fixing that in JS2?
@cdr - you should know good and well by now that using a for-in loop on an array is bad practice. A for-in loop iterates over all properties and methods in an object and the Array object is no different. Just because some people do it doesn’t make it good practice or correct. For Arrays you should stick with regular for loops iterating over the indexes of the array.
Frameworks are meant to be used from the ground up in a project. The code you make shouldn’t contain the for-in loop array pit-fall. If you are concerned with 3rd party JavaScript code using the for-in loop wrong, most text editors allow you to do a search through files for a ‘for-in’ loop (At least I know Zend IDE allows it).
Some interesting information on array sandboxing can be found here:
http://dean.edwards.name/weblog/2006/11/hooray/
im pretty sure theres a concensus that its not kosher for libraries to modify basic object type prototypes, especially the ones with literal syntax help, for precisely that kind of issue.
not being able to use for..in and extended Array/Object prototypes at the same time sounds like something that could be fixed. i’m interested in your thoughts on how to do that (introducing the unwritten, hidden ‘archetype’ vs ‘prototype’ distinctino into the syntax would make the inheritance system even more crazy than it already is..)
Sho’ nuff, yo.
It does not cease to amaze me that, while covering a variety of fresh web applications and frameworks, you can still write up a viciously thorough, yet strikingly simplistic, article like this one.
Keep up the *great* work.
@cdr - Extending the ‘Object object’s prototype is bad news because loads of things use it. For-in loops ARE supposed to be used with ‘Object objects’. That is not the case with Arrays. For example, Prototype Framework does NOT extend the ‘object Object’ prototype but is cool extending the Array prototype. If you don’t dig that don’t use frameworks that support doing that. What is your hand up on wanting to use a for-in loop with an Array? That is mis-use! I think your consensus is wrong. Extending basic data types can be a great thing. myString.capitalize() or myString.toJSON(), myArray.toJSON(), myArray.each() and other really cool extensions are great and offer a quick and easy to read programming solution.