Blog


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

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:

  1. 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.
  2. 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.
  3. 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

Deep Profiling jQuery Apps

This evening I was playing around with the idea of profiling jQuery applications - trying to find a convenient way to completely analyze all the code that is being executed in your application.

I've come up with a plugin that you can inject into a jQuery site that you own and see how the performance breaks down method-by-method.

Here's how you can try this plugin on your own site:

Step 1: Copy site HTML, add base href, add plugin.

For example, Github.com uses jQuery for a few basic effects and pieces of interaction (they use considerably more on pages beyond the homepage).

I took a copy of their page, added a <base href> to the top and injected the profiling plugin giving a resulting test page.

Before:

  <head>
    <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
    ...
    <script src="/javascripts/bundle.js"></script>
    ...
  </head>

After:

  <head>
    <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
    <base href="http://github.com/"/>
    ...
    <script src="/javascripts/bundle.js"></script>
    <script src="http://dev.jquery.com/~john/plugins/profile/jquery-profile.js"></script>
    ...
  </head>

Step 2: Use the site normally.

Use the site as you normally would. Load it up, click around - perform normal interactions. In the case of the Github.com page I let it load, scrolled down, and clicked on one of the demo images - which caused an overlay to appear. I then closed the X on the overlay and let it hide.

Step 3: View data.

In your console type jQuery.displayProfile(); and scroll down to the bottom of the page. You should see something like the following:

and here's a sample data dump:

Event: ready (165ms)
% (ms) Method in out
0.0% 0 jQuery(#document) 1
0.0% 0   .bind("ready", function()) 1 1
3.6% 6 jQuery("a[hotkey]")
0.0% 0   .each(function())
0.0% 0 jQuery(#document) 1
0.0% 0   .bind("keydown.hotkey", function()) 1 1
0.0% 0 jQuery("#triangle")
0.0% 0 jQuery("body") 1
1.2% 2   .append("<div id="triangle" style="position: absolute; display: none;"> </div>") 1 1
0.6% 1 jQuery("#repo_menu .active")
3.6% 6 jQuery(".jshide")
0.0% 0   .hide()
1.2% 2 jQuery(".toggle_link")
0.0% 0   .click(function())
0.6% 1 jQuery("#beta :text")
0.0% 0   .focus(function())
0.6% 1 jQuery("#beta form")
0.0% 0   .ajaxForm(function())
1.2% 2 jQuery(".hide_alert")
0.0% 0   .click(function())
0.0% 0 jQuery("#login_field")
0.0% 0   .focus()
0.0% 0 jQuery("#versions_select")
0.0% 0   .change(function())
1.2% 2 jQuery("a[rel*=facebox]") 3
17.6% 29   .facebox() 3 3
Event: load (1ms)
% (ms) Method in out
Event: click (29ms)
% (ms) Method in out
6.9% 2 jQuery("#facebox .loading")
3.4% 1 jQuery("facebox_overlay")
3.4% 1 jQuery("body") 1
6.9% 2   .append("<div id="facebox_overlay" class="facebox_hide"></div>") 1 1
0.0% 0 jQuery("#facebox_overlay") 1
6.9% 2   .hide() 1 1
3.4% 1   .addClass("facebox_overlayBG") 1 1
0.0% 0   .css("opacity", 0) 1 1
3.4% 1   .click(function()) 1 1
6.9% 2   .fadeIn(200) 1 1
3.4% 1 jQuery("#facebox .content") 1
3.4% 1   .empty() 1 1
3.4% 1 jQuery("#facebox .body") 1
0.0% 0   .children() 1 2
10.3% 3   .hide() 2 2
0.0% 0   .end() 2 1
6.9% 2   .append("<div class="loading"><img src="/facebox/loading.gif"/></div>") 1 1
0.0% 0 jQuery("#facebox") 1
0.0% 0 jQuery({...}) 1
3.4% 1   .width() 1
0.0% 0   .css({...}) 1 1
6.9% 2   .show() 1 1
0.0% 0 jQuery(#document) 1
0.0% 0   .bind("keydown.facebox", function()) 1 1
0.0% 0 jQuery(#document) 1
3.4% 1   .trigger("loading.facebox") 1 1
Event: beforeReveal.facebox (1ms)
% (ms) Method in out
Event: click (6ms)
% (ms) Method in out
16.7% 1 jQuery(#document) 1
66.7% 4   .trigger("close.facebox") 1 1
Event: close.facebox (3ms)
% (ms) Method in out

This quick table of data should be able to provide you with some interesting information about what's happening in your code. The result is still incredibly basic (really only providing the most basic level of jQuery method introspection) but it definitely shows some merit.

If you wish to create a different view for the data you can access the raw data structure by running jQuery.getProfile();.

The next stage of development for this plugin would be to reveal which methods are running inside other jQuery methods - in addition to monitoring other aspects of the application (such as timers, Ajax callbacks, etc.). I'm pleased with even this most-basic result - it gives me the ability to quickly, and easily, learn much more about a jQuery-using application.

Tags: performance, javascript, jquery

Recent Presentations

During the past couple weeks I've given a number of talks around the globe. Here's a quick dump of the talks for those that are interested in them.

  1. jQuery (BarCamp Boston)
  2. Processing and Processing.js (BarCamp Boston)
  3. jQuery (MeshU)
  4. Managing the Mozilla Way (Slashdot, ITWorld)
  5. jQuery (DrupalCamp Toronto)
  6. JavaScript 1.5 to 2.0 - A new talk examining all the language features introduced by the various versions of JavaScript (and the upcoming features of JavaScript 1.9 and 2.0).
  7. JavaScript Libraries (Kings of Code)
  8. JavaScript Libraries (@Media)
  9. jQuery (BostonPHP)

For the jQuery talks here are the demos that I used:

I want to thank the above conferences and venues for inviting me to speak, it was a lot of fun. I want to thank everyone who attended my talks - it was great to be able to see everyone and get tons of excellent feedback. I'm definitely looking forward to my next batch of speaking.

Tags: speaking, conferences, presentations, javascript, jquery

What's Next in jQuery and JavaScript?

The guys over at Ajaxian asked me to do answer a couple questions for them (on video) so I set about forming it into a mini-presentation. I discuss a couple things:

  • What's happening in jQuery Core, jQuery UI, and the jQuery Project.
  • What upcoming browser features I'm excited about.
  • Some of what's coming in JavaScript 1.9.
  • Some things that should change about Open Web development.

Video: What's Next in jQuery and JavaScript?

View Flash Version (on Vimeo.com) - Download .mp4 (80mb, Must be Logged In to Vimeo.com)

Update: I also have a small 320x240 video: Download .mov (16mb)

Let me know if you have any follow-up questions, I'll be happy to answer them. Additionally, this is the first time recording a presentation in my new office. I'm pleased with the result (even though the quality of the camera is quite poor). It was quite easy to get set up and perform, which is exciting - I may do some more, short, presentations if there's interest.

Tags: standards, jquery, javascript, browsers

Qualified Selectors in jQuery

Shaun Inman wrote a quick post on what he's calling "CSS Qualified Selectors". The syntax that he's proposing looks something like this:

a < img

Which says "Find me all a elements that have an img element inside of them."

To those of you who are familiar with jQuery we've had a similar selector, :has(), for quite some time:

$("a:has(img)")

If you're totally bent on Shaun's syntax (don't care for :has, I would assume) here's the two-line plugin that'll give it to you in jQuery:

jQuery.parse.push(/^\s*(<)(\s*)(.*)$/);
jQuery.expr["<"] = jQuery.expr[":"].has;

Then the following will work as you would expect it to:

$("a < img")

It's really nice having an extensible selector engine at your disposal.

Tags: selectors, javascript, jquery

Summer Speaking

My summer/fall speaking schedule is starting to solidify - with some major intensity occurring around May. Let me know if you're going to be at, or around, any of these events - I'd love to meet up.

May 17-18 - BarCamp - Boston

Don't have an exact topic chosen yet, possibly something on jQuery.

May 20 - MeshU Conference - Toronto

I'll be speaking on building interactive prototype using jQuery. More details...

May 23-24 - DrupalCamp - Toronto

The exact topic isn't clear yet, but probably something having to do with jQuery and Drupal.

May 27 - Kings of Code - Netherlands

I'll be speaking on the features of modern JavaScript libraries. More details...

May 29-30 - @Media - London

I'll be speaking on the features of modern JavaScript libraries. More details...

June 5 - Boston PHP - Boston

I'll be speaking on developing applications using jQuery. More details...

June 12 - Refresh Boston - Boston

I'll be speaking on using jQuery to develop interactive prototypes.

July 22 - OSCON - Portland

I'll be doing a 3.5 hour tutorial session on the Secrets of JavaScript Libraries (covering much of the content which will be presented in my upcoming book). More details...

October - Ajax Experience - Boston

I'll be giving three sessions: two on jQuery (beginning and advanced) and one on modern JavaScript libraries.

Tags: javascript, jquery, presentations, conferences

Classy Query

Over the past two years, seeing hundreds of thousands of people use jQuery, a major point has become apparent: jQuery simply isn't able to scale to handle the development needs of most power users. It lacks the clarity and power functionality that most developers need in order build applications in a collaborative environment.

For this reason I've put a lot of work into a new library (which sits on top of jQuery), called:

Classy Query

» View Source to Classy Query

If one thing has become apparent to me it's that users enjoy working with the typical Class-style of object creation and inheritance. Because of this I've constructed the entirety of Classy Query in an classical manner (you can inherit and override any piece of functionality that you desire).

Before we go too far, let's take a look at the type of clearly-defined code that you can now write with Classy Query:

<script src="jquery.js"></script>
<script src="classy.js"></script>
<script>
jQuery.Events.addEventListener(document, "ready", function(){
  jQuery.querySelectorAll("div").forEach(function(elem){
    jQuery.DOM.append(elem, " <b>More...</b>");
  });
 
  jQuery.querySelectorAll("div b").forEach(function(elem){
    jQuery.Events.addEventListener(elem, "click", function(elem, event){
      var next = jQuery.Traversal.nextSibling(elem);
      var animation = jQuery.Effects.buildAnimation( next, {height: "toggle"});
      amimation.start();
    });
  });
});
</script>

You can even sub-class functionality, creating your own pieces of encapsulation. All of the class and inheritance functionality is taken care of by the Simple JavaScript Inheritance code released last week.

jQuery.DOM.boldWrapInner = jQuery.DOM.wrapInner.extend({
  attach: function(elem){
    this._super(elem, "<b></b>");
  }
});

jQuery.querySelectorAll("div").forEach(function(elem){
  jQuery.DOM.boldWrapInner(elem);
});

Additionally you can treat pieces of functionality as if they were attachable behaviors, like so:

jQuery.querySelectorAll("div").attach(new jQuery.DOM.boldWrapInner());

There's a number of core features encompassed in Classy Query that really help to transform the traditional jQuery experience into something much more usable.

Class creation

The full Simple JavaScript Inheritance API is included in Classy Query, allowing you to extend and create JavaScript 'classes', like so:

var Person = jQuery.Class.create({
  init: function(name){
    this.name = name;
  }
});

var ClassyDude = Person.extend({
  sipWine: function(){
    return "Has a beautiful bouquet.";
  }
});

Inheritance

It's possible to inherit from any existing piece of Classy Query functionality, supporting a true 'programming in the large' mindset. You can inherit from, or completely override, existing methods with ease (especially since they're all classes).

jQuery.Effects.collapse = jQuery.Effects.buildAnimation.extend({
  attach: function(elem){
    this._super(elem, {height: "hide", width: "hide"});
  }
});

Behaviors

Behaviors tend to encapsulate a portion of functionality which is applied against an element (such as making a table sortable or making an item draggable). Classy Query supports a technique to make this particular implementation trivial.

jQuery.querySelectorAll("div")
  .attach(new jQuery.DOM.addClassName("current"));

No more 'this'

The 'this' keyword is confusing and frequently misused. To counter this all access to elements (within functions) is done by the first argument, rather than through 'this'.

jQuery.querySelectorAll("div").forEach(function(elem){
  jQuery.DOM.addClassName(elem, "current");
});

Re-Structuring

The hierarchy of jQuery has been completely re-organized. Rather than having a single, flat, interface through which to access all methods functionality has been broken down into individual groupings of methods. These groupings tend to, also, coordinate with the jQuery Documentation for convenience.

Additionally, nearly all the method names have been re-named in order to provide a greater level of clarity to developers. Frequently it was found that the concise method names of jQuery provided too much confusion to developers just getting started with the library. The end result is a library that is easier to read and understand for developers.

The full list of categories and changed methods can be found in the following table:

jQuery Classy Query
jQuery jQuery.querySelectorAll
each forEach
prepend jQuery.DOM.prepend
append jQuery.DOM.append
before jQuery.DOM.insertBefore
after jQuery.DOM.insertAfter
wrap jQuery.DOM.wrap
wrapInner jQuery.DOM.wrapInner
wrapAll jQuery.DOM.wrapAll
clone jQuery.DOM.clone
empty jQuery.DOM.empty
remove jQuery.DOM.remove
replaceWith jQuery.DOM.replaceWith
removeAttr jQuery.DOM.removeAttribute
addClass jQuery.DOM.addClassName
hasClass jQuery.DOM.hasClassName
removeClass jQuery.DOM.removeClassName
offset jQuery.DOM.getOffset
text jQuery.DOM.getText
text jQuery.DOM.setText
html jQuery.DOM.getHTML
html jQuery.DOM.setHTML
attr jQuery.DOM.getAttribute
attr jQuery.DOM.setAttribute
val jQuery.DOM.getValue
val jQuery.DOM.setValue
height jQuery.DOM.getHeight
height jQuery.DOM.setHeight
width jQuery.DOM.getWidth
width jQuery.DOM.setWidth
css jQuery.DOM.getCSS
css jQuery.DOM.setCSS
children jQuery.Traverse.getChildElements
find jQuery.Traverse.getDescendantElements
next jQuery.Traverse.getNextSiblingElements
nextAll jQuery.Traverse.getAllNextSiblingElements
parent jQuery.Traverse.getParentElements
parents jQuery.Traverse.getAncestorElements
prev jQuery.Traverse.getPreviousSiblingElements
prevAll jQuery.Traverse.getAllPreviousSiblingElements
siblings jQuery.Traverse.getSiblingElements
filter jQuery.Traverse.filterSelector
bind jQuery.Events.addEventListener
unbind jQuery.Events.removeEventListener
trigger jQuery.Events.triggerEvent
hover jQuery.Events.hover
toggle jQuery.Events.toggle
show jQuery.Effects.show
hide jQuery.Effects.hide
toggle jQuery.Effects.toggle
animate jQuery.Effects.buildAnimation
queue jQuery.Effects.queue
dequeue jQuery.Effects.dequeue
$.ajax jQuery.Ajax.request
load jQuery.Ajax.loadAndInsert
ajaxSetup jQuery.Ajax.setup
serialize jQuery.Ajax.getSerializedString
serializeArray jQuery.Ajax.getSerializedArray

querySelector and querySelectorAll

Finally, rather than having a generic, catch-all, method as jQuery selector functionality is deferred to the new querySelector and querySelectorAll methods. These methods only support pure CSS 1-3 selectors (nothing extra) in an attempt to avoid any unpleasant situations when a browser's native querySelectorAll is implemented.

jQuery.querySelectorAll("div").forEach(function(elem){
  jQuery.Effect.hide( elem );
});

jQuery.Effect.hide( jQuery.querySelector("#item") )


» View Source to Classy Query

I hope this particular library will be of use to the general jQuery-using population. I hope we can start to have an open dialog, moving forward, getting everyone to adopt a more standard approach to JavaScript development. There's no reason why we shouldn't be using this tried-and-true approach to application development. It's worked in an untold number of existing applications, it's bound to work here as well. Enjoy.

Tags: javascript, jquery, programming

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