[image]

Blog


querySelectorAll in Firefox 3.1

A brand-new implementation of the Selectors API has landed in the latest Firefox nightlies (and in Firefox 3.1a1) - on track to head your way in the upcoming Firefox 3.1 release.

I've talked about this API before (1, 2) and while I do have some misgivings about the current API (which will be remedied in upcoming revisions of the spec) there is one thing that is undeniable about it: It is extraordinarily fast.

Thankfully, implementations haven't scarified specification compatibility for performance and we can see both the Firefox and WebKit implementations coming in at 99.3% passing the Selectors API test suite. Opera is working on their implementations, slated for Opera 10, and Microsoft has an implementation in beta 1 of Internet Explorer 8. This means that by late this year all browsers will have an implementation of the Selectors API in the market.

JavaScript libraries have already been working to utilize this new API, preparing for when it'll eventually be ready for all to use. The current score is:

Dojo has querySelectorAll support in Dojo 1.1.1, although support for Safari 3.1 is disabled (there were troublesome crashing bugs in early versions of Safari 3.1 that have since been resolved). Prototype has querySelectorAll support in their Git repository (presumably to be rolled into their next release). jQuery has querySelectorAll support in an experimental plugin (to land in the next release).

This has lead to some interesting numbers (utilizing the same testing techniques employed by the WebKit team):

[image]
Library Time (ms)
Prototype 1.6.0.2 44677
Prototype Git 9914 (123% slower than native, 351% faster than DOM)
jQuery 1.2.7 Plugin 35045
jQuery Plugin 4731 (7% slower than native, 641% faster than DOM)
Dojo 1.0.2 20782
Dojo 1.1.1 5669 (28% slower than native, 267% faster than DOM)
Native 4441

That means that libraries that utilize querySelectorAll will be running 2-6x faster than their previous versions. This is already quite impressive.

There are two points to consider when using this API:

That you need to try and keep the overhead on top of the querySelectorAll method as low as possible. That it becomes advantageous to avoid the querySelectorAll API in some extreme cases (for example, jQuery avoids it for #id queries, allowing it to go over 10x faster than querySelectorAll).

A lot of bare-bones selectors library implementations are going to look something like this:

function querySelectorAll(selector){
  try {
    return Array.prototype.slice.call(
      document.querySelectorAll( selector ) );
  } catch(e){}
 
  return myOtherLibrary( selector );
}

Note two points: There's a try-catch block there to capture any syntax errors that are generated by querySelectorAll (syntax errors could be generated by APIs that the implementation doesn't understand - like jQuery's div:first, for example). If no exception is thrown while retrieving the results we need to convert it into an array (most libraries convert result sets into arrays - or bless them in some manner).

Tackling both of these points will introduce some level of overhead in a library (on top of the native querySelectorAll implementation). Of course it's never as simple as it should be, many libraries extend these return sets with additional functionality so the overhead will be that much greater.

Regardless it's readily apparent that this API will be quite instrumental in trivializing one of the most difficult parts of implementing a new JavaScript library. Everything after this is just gravy.

Tags: firefox, selectors, jquery, javascript, mozilla

[image]

Object.getPrototypeOf

Historically one JavaScript property that's seen a lot of use (mostly due to its convenience) is that of __proto__. It's a quick-and-dirty way of accessing the original prototype property of the object's constructor function. For example, the following is true:

"test".__proto__ === String.prototype

// Another alternative, not using __proto__
// Only works when constructor isn't changed
"test".constructor.prototype === String.prototype

This feature has been codified in the upcoming ECMAScript 3.1 specification as a new method: Object.getPrototypeOf(object) (and is implemented in the latest Firefox nightlies).

So how can we make use of this, now standardized, functionality?

instanceOf

If you wish you could implement your own version of the instanceof operator, using pure JavaScript.

function instanceOf(object, constructor) {
  while (object != null) {
    if (object == constructor.prototype)
      return true;
    object = Object.getPrototypeOf(object);
  }
  return false;
}

instanceOf("test", String);
instanceOf(true, Boolean);

In this example we traverse up the prototype chain, checking each constructor along the way. It's an effective method and allows for great expressiveness in our code.

Super Methods

We could use this function to call a super method, while doing some inheritance.

function Person(){}

Person.prototype.kick = function(type){
  alert(type + " kick!");
}

function Norris(){}

// Inherit properties from Person
Norris.prototype = new Person();

Norris.prototype.kick = function(){
  Object.getPrototypeOf(this).kick("Roundhouse");
};

In the above code we use Object.getPrototypeOf(this) to tap in to the original, inherited, kick method. Since we have since overridden the method we don't have direct access to it, but using getPrototypeOf we can capture, and utilize, it again.

Cross-Browser Implementation

The obvious question now becomes: How do we begin using Object.getPrototypeOf today if most browsers don't have it implemented yet? In the meantime we can use something like the following code for some form of compatibility:

if ( typeof Object.getPrototypeOf !== "function" ) {
  if ( typeof "test".__proto__ === "object" ) {
    Object.getPrototypeOf = function(object){
      return object.__proto__;
    };
  } else {
    Object.getPrototypeOf = function(object){
      // May break if the constructor has been tampered with
      return object.constructor.prototype;
    };
  }
}

While it's not 100% spot-on (since the .constructor property is mutable on any object - it's fully possible that it could've been manipulated by the user at some point) the above code should serve as a "good enough" solution to tide you over until browsers have good ECMAScript 3.1 compatibility.

Why Object.getPrototypeOf?

A common question at this point (and one that's sure to come up often as new features start to trickle in from ECMAScript 3.1): Why is the method Object.getPrototypeOf("test") and not "test".getPrototypeOf() - or even just a property, like __proto__? While having a method, or property, on every object would certainly be more convenient to use it ends up being impractical for generalized use.

For example, take the following case into consideration:

var obj = { getPrototypeOf: "blah" };

Any attempt to call its getPrototypeOf method would end in failure, forcing the developer to always have to fall back to using the generalized Object.getPrototypeOf. Since most uses of getPrototypeOf would be required to work in the general case the fallback would always have to be used. Thus it's not necessary to include it as an extra property of every object.

Additionally, it makes it far easier to backport to old implementations since you no longer have to extend the Object prototype (Object.prototype.getPrototypeOf = ...;) which would've been bad in any case.

Tags: ecmascript, mozilla, javascript

[image]

DOM DocumentFragments

I was playing around with DOM DocumentFragments recently, in JavaScript, seeing what I could make with them. Roughly speaking, a DocumentFragment is a lightweight container that can hold DOM nodes. It's part of the DOM 1 specification and is supported in all modern browsers (it was added to Internet Explorer in version 6).

In reading up on them I came across an interesting point, from the specification:

Furthermore, various operations -- such as inserting nodes as children of another Node -- may take DocumentFragment objects as arguments; this results in all the child nodes of the DocumentFragment being moved to the child list of this node.

This means that if you take a bunch of DOM nodes and append them to a fragment then you can simply append the fragment to the document, instead (and achieve the same result - as if you had appended each node individually). I instantly smelled a possible performance improvement here. I investigated a little bit further and noticed that DocumentFragments support the cloneNode method, as well. This provides all the functionality that you need to highly-optimize your DOM insertion code.

I set up a simple demo to test the theory.

Let's take the situation where you have a bunch of DOM nodes that you need to append into the document (in the demo it's 12 nodes - 8 at the top level - against a whole mess of divs).

var elems = [
        document.createElement("hr"),
        text( document.createElement("b"), "Links:" ),
        document.createTextNode(" "),
        text( document.createElement("a"), "Link A" ),
        document.createTextNode(" | "),
        text( document.createElement("a"), "Link B" ),
        document.createTextNode(" | "),
        text( document.createElement("a"), "Link C" )
];

function text(node, txt){
        node.appendChild( document.createTextNode(txt) );
        return node;
}

Normal Append

If we wanted to append these nodes into the document we would probably do it in this traditional manner: Looping through the nodes and cloning them individually (so that we can continue to append them all throughout the document).

var div = document.getElementsByTagName("div");

for ( var i = 0; i < div.length; i++ ) {
        for ( var e = 0; e < elems.length; e++ ) {
                div[i].appendChild( elems[e].cloneNode(true) );
        }
}

DocumentFragment Append

However, when we bring DocumentFragments into the picture we can immediately see a different structure. To start we append all our nodes into the fragment itself (built using the createDocumentFragment method).

But the interesting point comes when it's time to actually insert the nodes into the document: We only have to call appendChild and cloneNode once for all the nodes!

var div = document.getElementsByTagName("div");

var fragment = document.createDocumentFragment();
for ( var e = 0; e < elems.length; e++ ) {
        fragment.appendChild( elems[e] );
}

for ( var i = 0; i < div.length; i++ ) {
        div[i].appendChild( fragment.cloneNode(true) );
}

Setting some time stamps we can see our results pay off in spades:

Browser Normal (ms) Fragment (ms)
Firefox 3.0.1 90 47
Safari 3.1.2 156 44
Opera 9.51 208 95
IE 6 401 140
IE 7 230 61
IE 8b1 120 40

As it turns out: A method that is largely ignored in modern web development can provide some serious (2-3x) performance improvements to your DOM manipulation.

Tags: javascript, dom

[image]

JavaScript Micro-Templating

I've had a little utility that I've been kicking around for some time now that I've found to be quite useful in my JavaScript application-building endeavors. It's a super-simple templating function that is fast, caches quickly, and is easy to use. I have a couple tricks that I use to make it real fun to mess with.

Here's the source code to the templating function (a more-refined version of this code will be in my upcoming book Secrets of the JavaScript Ninja):

// Simple JavaScript Templating
// John Resig - http://ejohn.org/ - MIT Licensed
(function(){
  var cache = {};
 
  this.tmpl = function tmpl(str, data){
    // Figure out if we're getting a template, or if we need to
    // load the template - and be sure to cache the result.
    var fn = !/\W/.test(str) ?
      cache[str] = cache[str] ||
        tmpl(document.getElementById(str).innerHTML) :
     
      // Generate a reusable function that will serve as a template
      // generator (and which will be cached).
      new Function("obj",
        "var p=[],print=function(){p.push.apply(p,arguments);};" +
       
        // Introduce the data as local variables using with(){}
        "with(obj){p.push('" +
       
        // Convert the template into pure JavaScript
        str
          .replace(/[\r\t\n]/g, " ")
          .split("<%").join("\t")
          .replace(/((^|%>)[^\t]*)'/g, "$1\r")
          .replace(/\t=(.*?)%>/g, "'
,$1,'")
          .split("\t").join("'
);")
          .split("
%>").join("p.push('")
          .split("\r").join("\\'
")
      + "
');}return p.join('');");
   
    // Provide some basic currying to the user
    return data ? fn( data ) : fn;
  };
})();

You would use it against templates written like this (it doesn't have to be in this particular manner - but it's a style that I enjoy):

<script type="text/html" id="item_tmpl">
  <div id="<%=id%>" class="<%=(i % 2 == 1 ? " even" : "")%>">
    <div class="grid_1 alpha right">
      <img class="righted" src="<%=profile_image_url%>
"/>
    </div>
    <div class="grid_6 omega contents">
      <p><b><a href="/<%=from_user%>"><%=from_user%></a>:</b> <%=text%></p>
    </div>
  </div>
</script>

You can also inline script:

<script type="text/html" id="user_tmpl">
  <% for ( var i = 0; i < users.length; i++ ) { %>
    <li><a href="<%=users[i].url%>"><%=users[i].name%></a></li>
  <% } %>
</script>

Quick tip: Embedding scripts in your page that have a unknown content-type (such is the case here - the browser doesn't know how to execute a text/html script) are simply ignored by the browser - and by search engines and screenreaders. It's a perfect cloaking device for sneaking templates into your page. I like to use this technique for quick-and-dirty cases where I just need a little template or two on the page and want something light and fast.

and you would use it from script like so:

var results = document.getElementById("results");
results.innerHTML = tmpl("item_tmpl", dataObject);

You could pre-compile the results for later use. If you call the templating function with only an ID (or a template code) then it'll return a pre-compiled function that you can execute later:

var show_user = tmpl("item_tmpl"), html = "";
for ( var i = 0; i < users.length; i++ ) {
  html += show_user( users[i] );
}

The biggest falling-down of the method, at this point, is the parsing/conversion code - it could probably use a little love. It does use one technique that I enjoy, though: If you're searching and replacing through a string with a static search and a static replace it's faster to perform the action with .split("match").join("replace") - which seems counter-intuitive but it manages to work that way in most modern browsers. (There are changes going in place to grossly improve the performance of .replace(/match/g, "replace") in the next version of Firefox - so the previous statement won't be the case for long.)

Feel free to have fun with it - I'd be very curious to see what mutations occur with the script. Since it's so simple it seems like there's a lot that can still be done with it.

Tags: javascript

[image]

HTML 5 data- Attributes

A new feature being introduced in HTML 5 is the addition of custom data attributes. This is a, seemingly, bizarre addition to the specification - but actually provides a number of useful benefits.

Simply, the specification for custom data attributes states that any attribute that starts with "data-" will be treated as a storage area for private data (private in the sense that the end user can't see it - it doesn't affect layout or presentation).

This allows you to write valid HTML markup (passing an HTML 5 validator) while, simultaneously, embedding data within your page. A quick example:

<li class="user" data-name="John Resig" data-city="Boston"
     data-lang="js" data-food="Bacon">

  <b>John says:</b> <span>Hello, how are you?</span>
</li>

The above will be perfectly valid HTML 5. This should be a welcome addition to nearly every JavaScript developer. The question of the best means of attaching raw data to HTML elements - in a valid manner - has been a long-lingering question. Frameworks have tried to deal with this in different manners, two solutions being:

Using HTML, but with a custom DTD. Using XHTML, with a specific namespace.

The addition of this prefix completely routes around both issues (including any extra markup for validation or needing to be valid XHTML) with this effective addition.

On top of this a simple JavaScript API is presented to access these attribute values (in addition to the normal get/setAttribute):

var user = document.getElementsByTagName("li")[0];
var pos = 0, span = user.getElementsByTagName("span")[0];

var phrases = [
  {name: "city", prefix: "I am from "},
  {name: "food", prefix: "I like to eat "},
  {name: "lang", prefix: "I like to program in "}
];

user.addEventListener( "click", function(){
  var phrase = phrases[ pos++ ];
  // Use the .dataset property
  span.innerHTML = phrase.prefix + user.dataset[ phrase.name ];
}, false);

The .dataset property behaves very similarly to the the .attributes property (but it only works as a map of key-value pairs). While no browsers have implemented this exact DOM property, it's not hugely needed - the above code could be done with the critical line replaced with:

span.innerHTML = phrase.prefix +
  user.getAttribute("data-" + phrase.name );

I think what is most enticing about this whole specification is that you don't have to wait for any browser to implement anything in order to begin using it. By starting to use data- prefixes on your HTML metadata today you'll be safe in knowing that it'll continue to work well into the future. The time at which the HTML 5 validator is integrated into the full W3C validator your site will already be compliant (assuming, of course, you're already valid HTML 5 and using the HTML 5 Doctype).

Tags: html5, data, dom, javascript

[image]

jQuery LiveSearch

A fun blog post popped up yesterday in which John Nunemaker ported a Quicksilver-style Live Search to jQuery. Taking a look at his code, I decided to have a little fun and re-port it to jQuery - trying to use the functional style that jQuery promotes. I think the end result is quite simple and elegant.

The final code - compare with John's port:

jQuery.fn.liveUpdate = function(list){
  list = jQuery(list);

  if ( list.length ) {
    var rows = list.children('li'),
      cache = rows.map(function(){
        return this.innerHTML.toLowerCase();
      });
     
    this
      .keyup(filter).keyup()
      .parents('form').submit(function(){
        return false;
      });
  }
   
  return this;
   
  function filter(){
    var term = jQuery.trim( jQuery(this).val().toLowerCase() ), scores = [];
   
    if ( !term ) {
      rows.show();
    } else {
      rows.hide();

      cache.each(function(i){
        var score = this.score(term);
        if (score > 0) { scores.push([score, i]); }
      });

      jQuery.each(scores.sort(function(a, b){return b[0] - a[0];}), function(){
        jQuery(rows[ this[1] ]).show();
      });
    }
  }
};

A couple points to note:

.liveUpdate() no longer takes an element ID - it now accepts any jQuery selector (this is the only notable API change that I made). All state is stored in simple variables and accessed via closures, as opposed to as properties of an instance object. Only one function is used - and that's stored away within the function itself (greatly simplifying the resulting code). DOM queries are only done once and cached up front. .map() is used to simplify the creation of new arrays of information.

Tags: javascript, jquery

[image]

jQuery Plugins, Size, and Storage

There is no such thing as a JavaScript plugin contends James Coglan. I completely agree that there are no, specific, techniques within the JavaScript language that make "plugins" possible (such as the ability to namespace code and import it, or some such).

HOWEVER - I will contend that such a thing as plugins exist and are logically distinct from "random JavaScript code that manipulates other JavaScript code" as long as the following points are met:

There have to be explicit points upon which a plugin can attach. James notes the most common one in jQuery (jQuery.fn) but we have tons more - events, animations, selectors - all over the board for developers to snap in to. Even more importantly: Those points have to be documented or, at the very least, be under some sort of agreement that they will be treated like a normal piece of the user-facing API. In jQuery we treat all plugin extension points as "user-facing API" and only ever change them in major releases (if at all) and always provide an alternative for authors to use. Finally, there has to be some sort of repository for navigating these plugins. This is a huge differentiator. Simply referring to "code in the wild" as plugins doesn't really cut it if there's no commitment to hosting them and keeping their documentation and examples alive.

We take our plugin architecture very seriously in the jQuery project and are constantly looking for ways to improve (looking at plugins, reading their code, seeing what we can provide to make their lives easier).


Alex Russell of Dojo recently built a sleek 6kb version of Dojo - presumably for use on mobile platforms. He states in his post that:

Even so-called “lightweight†libraries like jQuery are far too heavy for some environments…not because they (like Dojo) pull in all the code needed to use them, but because they do it all up-front. Often the best time to pay the expense of loading, parsing, and executing JavaScript code is when the user takes an action that needs the enhancement to run.

The way it's worded you would assume that you were paying a large, up-front, cost to using jQuery when, in fact, there is very little overhead. jQuery has been shown to be the fastest loading JavaScript library for non-cached code and considerably fast for cached code.

Arguably a mini-Dojo would be able to provide an extra edge in this respect - however any gains that you would make up-front (which would be minimal - mini-Dojo is only about half the size of jQuery, as it stands) would have to contend with any future overhead incurred by loading additional components at a later time.

I frequently queue up long pages to read on my iPhone while I travel the subway system here in Boston and I think I'd be quite upset if I got halfway through a page, clicked a hide/show link, and found out that the action wasn't able to work since the requisite functionality hadn't been loaded yet.

There is a cost to loading "all" of jQuery up front, absolutely - however there are numerous benefits: It's highly cacheable, you never have to worry about what you do/don't have loaded, the API, documentation, tutorials, and examples are all dramatically simpler since you never have to worry about having extra components or making sure that they're being included correctly.

And, as always, if you're particularly excited about breaking jQuery down into little chunks you can grab the individual pieces from SVN and build a custom copy.


I was out of town when it happened but the release of the Google Ajax Library CDN (which includes the current release of jQuery) was incredibly cool. I've had a few requests from users wondering how this release came about. While I can't speak for the other projects, I can, at least, speak for what happened with jQuery.

Dion and I had been discussing solutions for providing better hosting solutions to JavaScript libraries for a long time (at least a year or two). Progress kept getting stalled at different points but persevered and got the release up at Google. I'm really glad to see this come about and I'm sure that jQuery users will certainly appreciate this release.

I have a couple points of concern with the release, namely:

How do we push a new release out? Currently we have to contact the guys at Google to get it pushed through - a way to automate this and do it programatically would be greatly appreciated (we could integrate it right into our release scripts). How do new pieces of code get added? There's no way for other projects to get added to the repository - some sort of process for joining would be ideal. SSL? Having an SSL-based CDN would be very useful, as well. However I suspect that if a site is going so far as to have SSL on their pages then they're probably not pulling their source code from an external site.

Other than that, though, I'm quite pleased with the release. The more that people pull from there the faster it'll be for everyone who uses JavaScript libraries (cutting away that initial download time).

Tags: jquery, javascript

[image]

eval() Kerfuffle

Last week it came out that, in Firefox (and other Gecko-based browsers) you could dip into the private scope of a function using eval, like so:

// Getting "private" variables
var obj = (function() {
  var a = 21;
  return {
    // public function must reference 'a'
    fn: function() {a;}
  };
})();

var foo;
eval('foo=a', obj.fn);
console.log(foo); // 21
 

I think the common response to seeing the above was something like: WUH!?!?

As can be seen in the ensuing discussions.

Perhaps more interestingly is the dig in to try and figure out how on earth this feature made it in to the language to being with. Brendan Eich provides some insight:

3.2 <fur> 1998-04-23 17:30: Initial checkin of JavaScript 1.3, migrated from JSFUN13_BRANCH in /m/ src repository

This eval extension, if memory serves (I was in mozilla.org at the time, not in the JS group at Netscape) originated in conversations with Microsoft's rep during ECMA-262 standardization, trying to reach agreement on a way to eval in other scopes.

At this point, however, it's pretty safe to say that since so few people know about it, and that (hopefully) there are no important sites relying upon its behavior, it can be stricken from the record.

Which it has been.

It's been a crazy ride but you can expect to see this feature gone in Firefox 3.1 (the next release of Firefox, set to debut this year).

Tags: firefox, mozilla, javascript, links

[image]
· « Previous entries

Current Projects

jQuery JavaScript Library

jQuery

Comprehensive DOM, Event, Animation, and Ajax JavaScript Library.

Recent Projects

Pro JavaScript Techniques

JavaScript Book

The best techniques for professional JavaScript. Published by Apress.


Hosting provided by the cool dudes at Engine Yard.


You are viewing a mobilized version of this site...
View original page here

Mobilized by Mowser Mowser