This is the transcript of the seminar taken by John Resig (jQuery's creator) at Miami on last February.
Thanks. I want to thank everyone for having me here today. I really appreciate it. I wanted to talk about a couple of things today, mostly around jQuery itself, but hopefully generic enough that in case you’re not using jQuery at least it will still be applicable to your web apps.
There are a couple of techniques I want to look at that hopefully all of you encounter when developing web applications. You always want to improve the performance of your JavaScript code. You always want to improve the accessibility, and you want to make your code simpler. Finally, you want to make the design process much easier.
When it comes to performance, there are a number of things in JavaScript, three of which are fairly easy, but especially so in jQuery. One is event delegation. Event delegation is a technique that routes around the traditional method of binding events. What I mean by that is typically when you attach a click handler to an element, you’re attaching a click to every single element that you want to attach to. This can be really costly. If you have a table with a couple of thousand table cells in it, that is going to be a lot of click handlers to attach.
Event delegation is whenever you attach a handler to some parent element up the tree, for example you attach it to the table itself rather than each individual table cell, and this takes advantage of the browser’s native event bubbling. What that means is when the user clicks on the table cell, the event will bubble up the tree, up to the table, and you can now handle the even there. That’s event delegation.
In this way, you only have to attach one event to the document, instead of a couple of thousand. It’s much faster and scales much better. jQuery provides a couple of methods for handling this; one is called [“dotlive”]. Another one is one that we just added the other day called “the delegate” method. What I think is really interesting about delegation is, in addition to it being much faster, it works in all current and all future elements as well. Even though there are only a thousand table cells to the document at the moment, if you add another thousand, it will continue to work on those as well. You don’t have to attach new handlers to handle them.
The delegate method is one that we just added the other day. This one embodies delegation at its very core. This is the exact example I was talking about. You’re attaching a click event to a core element, a root element such as the table. Then you’re watching for any (in this case) hovers over any table cell. It’s a very simple piece of code, and it gets you exactly what you want in the end.
Live events is a slightly different way of writing and doing delegation, but the nice thing is it works and appears to work in the same exact way as event binding does. You get to use the same syntax. You can use a selector, and then you can attach events directly to it, or seemingly directly to it. In the background, jQuery routes around and makes sure that it goes in all the right places.
For example, here we have these menus that when you mouse over, it does a little animation to toggle out the sub menu. The nice thing is if we add new menu, the animation will also work on the new one we just added. In this way, it’s live. It will continue to work with both current and future elements on the page.
Another technique is something you don’t necessarily have to do yourself, but it’s something that jQuery internalizes. It’s the ability of storing HTML fragments in DOM fragments. What this means is if you’re generating a piece of HTML on jQuery, an HTML string, we’re taking that and converting it to a number of DOM nodes, and sticking that in a DOM fragment.
What I mean by that is the DOM has this concept of, amongst other things, a document fragment. This fragment is sort of a very lightweight container for holding DOM nodes. You can put them in; it acts as a sort of collection. In this case, it’s a very simplified version of what we’re doing in jQuery core, which is if you want to take that HTML snippet and turn it into something held in a document fragment, we take it and enter HTML to generate some nodes. We extract those nodes back out and stick it in a fragment.
What’s interesting about a fragment is that you can perform operations using a fragment and they’re incredibly fast. What I mean by that is you can insert in an entire fragment into a document, but when you insert it, you’re not inserting the fragment itself but the nodes contained within the fragment. In this way, it’s very fast. We got some big speed ups by switching our code to this.
Also, we can now cache those fragments. If we have an HTML string that is coming in, and we’re generating a fragment out at the end, we can cache that result; we can cache the string to the fragment. We can use that and save having to regenerate that entire fragment again, and again. In this way, jQuery internalizes this and handles all that immediately.
Another technique that is good for performance, and is much larger than jQuery itself, is loading your JavaScript library from a CDN. A CDN is short for Content Delivery Network and what that effectively translates into is a bunch of geo-position datacenters around the globe. Not all of us have the finances to build a whole bunch of datacenters all around, but there are a number of great services that handle this. Amazon CloudFront is one of them. We use EdgeCast in jQuery, and MediaTemple is hosting EdgeCast. Additionally, many of the popular JavaScript libraries are also hosted by Google and Microsoft. They host them on their private CDNs as well.
What is really nice about the CDNs, in addition to being geo-positioned, is that the files are automatically minified and gzipped. Minifying and gzipping your JavaScript is the best way to serve your JavaScript code, absolutely. The very first step you should do when pushing JavaScript code into production is to run it through a minifier first. A typical minifier is like the recently released Google Closure or [0:49:21?], or tools like that. Those strip out all the white space, all the comments, all that stuff, and reduce your code down to the smallest possible code you can send to the browser.
Additionally, gzipping is something you can configure in your web server and this will compress your code even further. In the case of jQuery, we started out at just over 150K with raw script, but when we actually transmitted down, we were only sending about 20K with the code, since it’s been minified and gzipped along the way. This is a huge speed up and obviously, this is a big difference in the amount of data that we have to transmit. It saves us bandwidth and it loads faster for the users.
For improving accessibility, a lot of what can be done is working in a very unobtrusive manner. In this way, this is a generic technique that works in all of your JavaScripting, which is that when you override the way that a browser typically does something, for example submitting a form or clicking a link, you can then go through and implement your own functionality. This is effectively unobtrusive scripting. You’re taking something that works already, in a browser, and you layer on your own functionality.
jQuery makes this really easy in addition to providing techniques for handling that, but all your JavaScripts in jQuery code can be encapsulated in the “document ready event”. This event fires the instant your document is ready to be manipulated and traversed. This is very useful because, almost always, this event will fire before the page is visible to the user. You can get in there and make changes to the page, and change how it’s going to display. Then the user will see that modified result.
Probably the most basic example of doing unobtrusive scripting is overriding all links that are going to be clicked, and when they’re clicked we’re going to load the URL into the page itself. The central line here is “return false”, and in jQuery that is telling the browser to prevent the normal click action and we’re going to override it and do something else instead. In this case, clicking this link would normally take us to a different page, but now we click it instead and we load in that page and inject it into our document. In this case, loading and injecting is done with the jQuery load method.
One technique that can serve really well here is that jQuery provides an additional header in every single Ajax request that occurs. It’s called the “x requested with header”, so if you see in your code – this is a PHP snippet here, but it will be similar in any other language it uses. If you see that this header exists, you can then adapt your content to be displayed in a different manner. Maybe you’ll display in an XML or JSON or what have you. A better technique might be, if you want to load in an HTML fragment, is to strip off the header and footer of your page. That way you can dynamically request your page and only grab the inner contents. You can grab that and then inject it into your page.
One alternative to that is this additional trick in jQuery, which is a load method that I showed before; you can load in a file that you specify and grab the HTML contents from that file and inject it into a page. If you’ll note there, on the end, just after the file there is an H2. That is requesting the HTML file, serializing it into a DOM structure, and then searching and looking for all the H2 elements. This is really useful because you can then grab and load in chunks of a remote HTML document, and inject it into your site, without ever doing any server-side manipulation. In this way, as a front-end developer, you have an incredible amount of control over the content that you can grab and load to your page. I love this trick because I don’t ever have to touch the server in this case. It’s great.
Reducing complexity, at least in jQuery land, all relates to enhancing that relationship around the DOM element. jQuery provides a number of tools for making it easier to communicate through a DOM element. I’ll show you what I mean.
jQuery provide an API called the Data API. It provides this data method that you can use, to both access and set values on a DOM element. What’s interesting about this and what makes this different from accessing and getting DOM attributes, is that when you access and set DOM attributes, they’re visible; they’re permanently attached to the element itself, but what we’re doing in jQuery is we actually create a separate data store and store that separately, away from the element. Any time you set or get data on that element, you’re actually setting it in our data store. It’s very fast.
There are a lot of advantages to that. In jQuery, we use this central data store to store all the events that we bind to an element. Instead of attaching the handlers directly to the element itself, we store it in our separate data store. The nice thing about this is we can manage all the garbage collection issues surrounding Internet Explorer. What’s good about this is we have all our data in this separate store, and when the element is removed, we can handle that and clean up all the data we have there. In that way, you will be recapturing all the memory that is being used, every single time, instead of there being nasty memory leaks shooting all around, as want to happen in Internet Explorer.
Another cool thing we have implemented is if you set or get a data value, we trigger an event. You can override this event and implement new functionality. If you’re writing a plug-in where you’re looking for a specific value to be set or get, you can override that and implement your own values that should be set or get. This could be really cool. Then you use the Data API of setting and getting values for your plug-in.
This comes up to custom events. Custom events – what’s interesting in jQuery is we have the typical way of binding and triggering events, and it’s 100% identical to doing your own custom events, so if you have an event that is the name of your plug-in, or in this case my plug-in, it doesn’t matter; jQuery handles that identically and it works identically everywhere.
What’s nice about this is you can create and trigger your own events and use it as a way of communicating around the document. This is a bit different from the typical way in which you would see it triggered in a document, in that it’s not going to be triggered by a click; this is something you have to trigger manually. One thing that we have that is layered on top of this is the concept of event name spaces. Event name spaces allow you to, for example, attach a click or focus, but scoped within a particular name. In this case, we have a click that is within the scope within the name of a particular plug-in. What’s cool about this is you can then unbind all of the events that were bounded by a particular plug-in.
This is important because otherwise, the plug-in would have to maintain a list of all the handlers that is bound to any given element at any given time. That is very costly and something that we don’t want plug-in authors to have to worry about. This is something that you can handle completely just by giving all your handlers names.
In the end, what’s great is you can create these custom events as a way of communicating. The really nice thing about custom events in general is that it is a way of communicating from one-to-many, meaning that if you want to send a message, for example “remove”, you want to tell these DOM elements to remove, whenever that may occur; there could be any number of actions on the other end waiting to occur, based upon that trigger.
For example, here we have two different plug-ins that are both watching for a “remove” event to occur. They’ll both be triggered when the trigger runs. But, when you run the trigger, you don’t have to know that there are events actually bound. There could be zero events bound. There could be ten events bound. There could be any number and in that way it provides a very graceful way of coding. You can just trigger these events to occur, and it will just gracefully happen in your document.
jQuery also provides a way of doing what are called “special” events. These work at a more core, fundamental level compared to custom events. Special events are actually a complete way of implementing an event at a very core level. For example, if you want to simulate – at least in jQuery core, we use it to simulate the “submit” and “change” events in Internet Explorer. In Internet Explorer, those events are rather broken. We also use it to implement “mouse hunter”, “mouse leave”, “focus in” and “focus out” in all the other browsers.
Just to show you an example of what it takes, this is the total code to implement the focus in and focus out events in all browsers, except for IE. It’s actually very little code and it’s overriding the normal behavior so instead of attaching the handler to the document, we do it in a slightly different way.
One plug-in, for example, that makes use of this is a plug-in I wrote last night called “Hot Keys” where you can bind a key down, or a key up, or key press, and you can give a key combination. For example, when the key down one occurs on Ctrl A, do something. In this case, we’re returning false and that will prevent you from doing Ctrl A on the document, selecting everything presumably. You could also say Ctrl A and space Meta A to prevent on an Apple keyboard. This is the sort of thing you can do with special events.
A nice thing is being able to go in and simplify your design. What we provide in the jQuery project for that is jQuery UI. jQuery UI is a set of components and set of widgets that you can use, you can drop into your application, and they will just work. We designed them in a way that they’re not only easy to use but they have a really good API.
On top of that, they’re all themeable and customizable. I’ll show you an example here. You can completely customize them to the style of your website. This is the jQuery UI ThemeRoller. We can sort of see a selection here of jQuery UI components. For example, we have an accordion, a tabs widget, slider, date picker, dialog, progress bar, etc. These are all in jQuery UI core.
You can go in and you can actually start to customize that and load in other themes. We have a number of prebuilt themes. You can load in this orange-ish style. We can try this grayish style. There are different ones we have prebuilt, but what’s nice about this is you can go in and you can customize each of these things completely to your own desires.
If we didn’t like this particular header background and we wanted something different, we could go in and adapt the color to exactly what we wanted to see. Instead of this dark gray or something, maybe we want something much brighter. It will take that color, and actually generate all the CSS and all the images needed to view this theme, and give it to you in a single, nice, compressed CSS file right to the image file. And, it will work in all browsers, including IE6. We have all the alpha transparencies and everything already done.
I love this personally, because dealing with CSS cross-browser issues is absolutely the last thing I want to do. This is some way to route around that, that we’ve already taken care of all those problems and we’ve implemented in a very slick way. One cool thing about this is, for example, there is a number of jQuery plug-ins out there that are already themeable using the ThemeRoller, ones beyond jQuery UI.
For example, this is a jQuery plug-in called jqGrid. Since jqGrid is themeable with ThemeRoller, you can then load this tool called the ThemeRoller bookmarklet. I don’t remember the full name of it. You get this little panel, the same as what you would see in the ThemeRoller page. You can layer it on top of any page just using ThemeRoller. They’re using this bluish theme but let’s say we wanted a grid but we didn’t want that bluish theme; we wanted one that is more orange-ish. You can click it, load that theme dynamically and you can see how it will look, right inside the website. You can get there, tweak it to your heart’s content; you can tweak the header, colors, and everything. That will just work. That is something I find to be really cool.
What are we working on? In the jQuery project there are a few things we’re working on at the moment. We’re working on a better dynamic script loader that will do all sorts of dependency management. We’re working on a new templating engine so hopefully both of those will be coming up in new releases. We’re also working on a rewrite of the Ajax module, to become more extensible. And, perhaps the largest is that we’re working on good mobile support.
We want to make sure that not only is jQuery the best library for doing desktop web applications, but we want to make sure that same code base is the best library for doing mobile applications. To get there, we’re going to be testing against a whole bunch of mobile browsers on a number of different devices. I have a bunch of devices at home that I have set up and I’m already starting to go through them. We’re going to make sure that jQuery is going to work, not only in normal IE6, IE7, IE8, Opera, all the browsers, but it’s going to work in mobile IE, mobile Safari, Opera, all those mobile browsers, at least the ones that are feasible to implement jQuery on. This is something that we’re working on and we hope to make some very good progress here, very soon. There is some more information about the jQuery project if you have any questions, and I think I’m out of time but thank you all for having me today.
Q + A
Ryan: Thank you very much. While we do the switchover we probably have about a minute for a question. Are there any questions for John?
Q: Will jQuery support internationalization?
John: jQuery core probably won’t but we already do some of that for jQuery UI. I think we’ve been talking about moving the validation plug-in, for example, into jQuery UI. The validation plug-in handles a lot of that stuff, especially number formatting. That is something, that and the addition of something like get text replacement that you can use for translating chunks of strings is another thing I’ve seen requested. Again, I don’t think either of those would be in jQuery core so much as probably jQuery UI. Most of those things typically relate to building a UI component, not a jQuery UI component, but a UI component in general.
Ryan: We probably have time for another one.
Q: The need to support Internet Explorer 6 and some of those older browsers, how much of that is holding back jQuery, and one day when you can get away from that, how much more capability do you think it will bring to jQuery?
John: If we dropped support for IE6 today, absolutely nothing would change, at least for jQuery core. jQuery UI might be able to benefit more. The JavaScript engine and the DOM engine in their export did not change between 6 and 7. That is a bit of a lie. They did change. They introduced two bugs in IE7. It’s really frustrating. In our case, it would be better to drop IE7 support than IE6. The reality is that from my perspective, IE6 is the same as IE7. IE7 is shipping in IE8. IE8 is going to be around for a very long time. There is no reason to drop any of these.
You asked how much is this holding us back? At least for jQuery core, not – obviously we have to deal around all these browser issues, but it’s not degrading the performance of the code. We make sure, at the very least, that the code that we’re shipping is going to perform the best it can, regardless. You don’t have to worry about that. I think that’s the primary concern, does it affect my performance, if so, kill it with fire. The other one is can I save some bandwidth by extracting that. Obviously, we could save some file size by removal of that code, not a massive amount, but maybe a couple K. Again, I don’t think it’s holding us back.