Friday, April 13th, 2007

Advancing JavaScript with Libraries

Category: JavaScript, jQuery, Library, Presentation

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

  1. document.getElementById("obj").getElementsByTagName("param")
  2.  
  3. <object id="obj">
  4.   <param name="src" value="test.mov" />
  5.   <param name="title" value="My Video" />
  6. </object>

This fails in Safari 2

  1. document.getElementById("opt").selected
  2.  
  3. <div style="display: none;">
  4.  <select><option id="opt" alue="test"/></select>
  5. </div>

These are genuine bugs in a specific strange way.

These fail in Opera and IE (return the input where the name is q)

  1. document.getElementById("q")
  2.  
  3. <form action="" method="POST">
  4.   <input type="text" name="q" />
  5.   <span id="q">Search</span>
  6. </form>

And expandos mess you up if you use an id of length (in Opera and IE)

  1. var f = document.getElementsByTagName("input")
  2. for (var x = 0; x < f.length; f++) {
  3.  
  4. }
  5.  
  6. <form action="" method="POST">
  7.   <input type="text" id="length" value="22" />

And these fail in all browsers (for valid reasons, but Joe User doesn’t get it):

  1. document.getElementById("name").setAttribute("disabled", false)
  2.  
  3. <input type="text" id="name" disabled="disabled" />

Reason: DOM doesn’t grok booleans.

  1. document.body.setAttribute("className", "home");
  2.  
  3. <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:

  1. <input type="text" id="test" />
  2. <script>document.getElementById"test".docus();</script>

jQuery example:

  1. <head><script src="script.js"></script></head>
  2.  
  3. $(document).ready(Function() {
  4.   $("#test").focus();
  5. }

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:

javascript
< view plain text >
  1. $("#menu > li:not(:first-child)").hide(); // CSS 3
  2.  
  3. $("ul[ul]").show(); // XPath
  4.  
  5. $("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.

javascript
< view plain text >
  1. $("table tr").append("<td>test</td>");
  2. $("select").prepend("<option>test</option>");

Put them together and you get unobtrusive DOM scripting:

javascript
< view plain text >
  1. $(document).ready(function() {
  2.   $("select").append("<option>test</option>");
  3. })

and it sets up new expectations:

jQuery users have thought that this should work:

  1. $("ul > li").click(function(){
  2.   $(this).load("menu.html");
  3. });
  4.  
  5. <ul>
  6.   <li>item a</li>
  7.   <li>item b</li>
  8. </ul>

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:

javascript
< view plain text >
  1. 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:

  1. <script type="text/jquery">
  2. pre.run:
  3.   append '<input type="submit" value="Run"/>'
  4.   input: click
  5.     each this.parentNode.firstChild.nodeValue
  6.     remove 'foo'
  7.  
  8. #download > div: oneclick
  9.   #form + li: slideDown
  10.   remove
  11. </script>

Come on, time for:

  1. <script type="text/ruby">
  2.   document.ready do |dom|
  3.     dom["table tr"] < < "<td>test"
  4.   end
  5. </script>

Could get to this via JRuby and the JVM?

Related Content:

Posted by Dion Almaer at 5:04 pm
14 Comments

+++--
3.7 rating from 47 votes

14 Comments »

Comments feed TrackBack URI

You’re rock john! But I think jquery lacks of array extensions

Comment by Rizqi Ahmad — April 13, 2007

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?

Comment by remiss — April 14, 2007

@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’;”.

Comment by Andy — April 14, 2007

In “Waiting for the document to load”, I’m sure that “document.getElementById”test”.docus();” should be “document.getElementById(“test”).focus();”

Comment by Kevinin — April 14, 2007

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

ajaxian is JQuery2? the syntax is not JS-style, eh..

and whats this about LISP in firefox?? you mean crockfordgs scheme.js?

Comment by carmen — April 15, 2007

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.

Comment by carmen — April 15, 2007

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..

Comment by cdr — April 15, 2007

heres the link if you happen to have Flash:

http://video.yahoo.com/video/play?ei=UTF-8&b=0&vid=410472&gid=133414

Comment by cdr — April 15, 2007

Rizqi: how do you extend Array literals globally without polluting the ‘for..in’ and similar iteration? are they fixing that in JS2?

Comment by cdr — April 15, 2007

@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/

Comment by foriniswrong — April 15, 2007

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

Comment by cdr — April 15, 2007

Sho’ nuff, yo.

Comment by Giggle Platypus — April 15, 2007

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.

Comment by Kelly Storm — April 15, 2007

@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.

Comment by foriniswrong — April 16, 2007

Leave a comment

You must be logged in to post a comment.