YUI 3: Lighter, Faster, Easier to Use

In August of 2008, the Yahoo User Interface team released the first preview release of YUI 3, a complete rewrite of YUI from the ground up (the first beta release of which is due out some time next week!).

Why would they rewrite an already useful and mature library, you might ask? Well, as YUI 2 matured, the manner in which it was built also evolved.

As a result, parts of YUI that were present from the beginning were coded in an entirely different way from parts that were added later on. The result was that the library contained inconsistencies – both in the internal mechanics and the API. So, the team decided that they’d bring the library back to basics and rebuild it from the ground up. Their plan was to apply the experience they gained from the past three years of working on YUI and build a library that was lighter, faster, and easier to use. Finally, in an effort to make the library as flexible as possible, YUI 3 introduces an all new plugin architecture that allows you to add your own functionality to nodes and widgets. We’ll see one such plugin in action a little later. First, let’s see what’s new in YUI 3.

Goodbye YAHOO, hello YUI

The first bit you’ll notice when looking at YUI 3 code examples is the new global object name that it uses. The old YAHOO global object has been replaced with the new YUI global object. It was renamed for a couple of reasons: user feedback suggested that some companies disliked having the Yahoo name in their code; but more importantly, it allows older versions of YUI to coexist with YUI 3 by virtue of having a different variable name.

As well as the global object being renamed, its usage is based on a fundamentally different principle than in 2.x. For starters, it’s no longer a static object but rather a function that, when executed, returns an instance of itself. Now, every execution of YUI returns a self-contained environment where the version of the library and the version of all of its loaded components can run without interfering with any other instance of YUI on the page. By extension this also means that multiple versions of the library can coexist on the same page, by instantiating different versions of the YUI object. The example below illustrates this principle:

 
YUI().use('dd-drop', 'anim', function(Y) {
   // Y.DD is available
   // Y.Anim is available
}

Here, YUI() is executed, which returns an instance of the library from which the use method is executed. A minimum of two parameters are required: the library components, then the callback to run once everything is done loading. In our above example, the first two parameters passed to use are the names of the library components that are to be loaded. The final parameter is our callback function, which receives an object (named Y in the example) – this is an instance of the library containing all of the loaded components. So, like the comments in the example above, the drag and drop component can be found at Y.DD, and the animation component can be found at Y.Anim.

A Cleaner, More Succinct Coding Style

One of the main concerns the team had was to ensure consistent internal mechanics and a consistent API across all YUI components. So YUI 3 now has a new hierarchy of classes that allow exactly that. The Attribute class, for example, provides get and set methods, initial configuration support, and attribute change events to classes that inherit it. There’s also a Base class that inherits from Attribute and provides some out-of-the-box object oriented functionality, like initializer and destructor methods for all classes that inherit from it. Finally, they’ve built a Widget class that inherits from Base and provides common functionality normally used by widgets, such as a render method – a common Model-View-Controller structure for managing the widget’s rendering – and support for common widget attributes. It also provides plugin registration and activation support.

Another problem YUI 3 overcomes is when your script needs to load a heavy piece of the library for only one or two functions contained within it. For example, in YUI 2 you’d have to include the entire Connection Utility (weighing 38KB, 12KB when minified) just to do some XMLHttpRequest calls. YUI 3 solves this problem by splitting up functionality into smaller submodules. That way, there’s no need to pull down the entire utility just to make XMLHttpRequest calls. In YUI 3 you can make an XMLHttpRequest call by only pulling down the IO Utility’s io-base module, weighing a mere 19KB (4KB minified). In fact, the entire IO Utility weighs 29KB (7KB minified) and is much more feature-rich than its predecessor. For example, it’s now possible to make cross-domain or XDomainRequest calls with the io-xdr submodule.

Selecting and Chaining

YUI 2 introduced a selector engine, but it did so a little late in the library’s life. As a result, the whole library is built around old-school element fetching techniques. In other words, you either passed an element’s id or the element itself whenever a reference was needed, and that was about it. With YUI 3, however, the selector engine is built right into the very core of the library. It permeates every part of it so that you can pass CSS selector strings virtually anywhere an element reference is needed. As well as constructors, that also includes setting up event handlers and working with utilities. Here’s an example, which makes the first element with the class author draggable:

var dd = new Y.DD.Drag({
   node: '.author'
});

Whenever YUI 3 has no logical value to return, it tries to make the method chainable. Here's an example of that in action:

Y.get('.author').setStyle('cursor', 'move');

Here, we referred to the first element with the class name author and set a cursor style on it.

Nodes and Event Façades

YUI 3 introduces a new abstraction layer for the DOM with the Node Utility. Rather than return a reference to a DOM element, YUI 3 returns Nodes and NodeLists, which greatly simplify DOM interaction. That's because Nodes include all the functionality you need in order to interact with them, rather than having to go to separate functions. What's more, the methods that are exposed by a Node take care of browser normalization whenever it's needed so the experience of working with them is as painless as possible. Here's what you'd have to write in YUI 2 in order to add a class name to an element:

YAHOO.util.Dom.addClass("navProducts", "selected");

In YUI 3 this becomes:

Y.get("#navProducts").addClass("selected");

In fact, if the node was already in hand, say in a variable named navProducts, then you'd simply be able to do this:

navProducts.addClass("selected");

YUI 3 simplified the interaction with the DOM, and has also normalized event management using the idea of an event façade. Every event listener receives an event façade that takes care of all the browser normalization. So, for example, where in YUI 2 you needed the following code in order to "prevent default":

YAHOO.util.Event.on("navProducts", "click", YUI2callback(e) { 
 e = e || event;
 YAHOO.util.Event.preventDefault(e);
});

Now all you need to do is:

navProducts.on("click", YUI3callback(e) { 
 e.preventDefault();
});

What's more, this behavior extends over to purely custom events as well, so even these receive event façades that allow them to prevent default and stop propagation.

Next: let's put some of these new methods into practice.

Show Me the Money!

I've built a simple example page demonstrating how easy and powerful YUI 3 is to use. To save space here in the article, I'll avoid showing you all the markup and code, but you can view the full source on the example page.

The first task I do is load up an instance of YUI with the Animation Utility, Slider Widget, MenuNav Node Plugin, and Drag & Drop Utilities. YUI goes and fetches the necessary files and their dependencies from the Yahoo servers. It then returns an instance of YUI with the loaded components to the callback function, which receives it in the variable named Y:

YUI().use('anim', 'slider', 'node-menunav', 'dd-drag', function (Y) { ...

Next, I create a simple animation to bounce the page's contents into view. In order to do this, I instantiate a new Animation object. I pass it a reference to the element with the id main and tell it to animate the top value to 0 from wherever it currently is (right now it's at -1000px, as specified in the example page's markup). I also specify that the animation should take three seconds and that it should use the elasticOut easing method. Once the object is instantiated, it's just a simple case of running it with the run method:

  
/*  
* Bounce-in Anim  
*/  
var anim = new Y.Anim({  
 node: '#main',  
 to: {  
     top: 0  
 },  
 duration: 3,  
 easing: Y.Easing.elasticOut  
 });  
anim.run();  

Next, I set up a Slider object in order to let users adjust the page's base font size. YUI's fonts CSS (included in the example page) sets the page's base font size to 13 pixels. It does this by setting the font-size value on the body element, from which all other font sizes are calculated. This is what we're going to manipulate in order to change the whole page's font sizes.

I grab a node reference for the body element, which will be used later in conjunction with the slider. Then I create a slider widget. I set the minimum value to 13 and the maximum to 28 because I want the font size to stay within these values. I then make sure the slider's rail size is 100 pixels wide. Finally, I set the slider's thumb image (which is loaded directly off Yahoo's hosted servers):

/*  
* Font Size Slider  
*/  
 var body = Y.get('body');  
 var slider = new Y.Slider({  
   min: 13,  
   max: 28,  
   railSize: '100px',  
   thumbImage:  
   'http://yui.yahooapis.com/3.0.0pr2/build/  
   slider/assets/skins/sam/thumb-classic-x.png'  
   });

Once the slider is instantiated, it's just a simple matter of rendering it. I do this by calling the slider's render method with the class name of the element I want it rendered in. The slider widget will render in the first element in the DOM that matches that class name:

slider.render('.horiz_slider');

The only task left to do now is to wire up the slider so that it actually adjusts the page's font size. This I do by hooking into its after event. YUI 3 has standard on and after events you can hook into to make event handling like this much easier than in the prior version. Now, whenever the valueChange event is fired, our body element's fontStyle value is changed:

slider.after('valueChange', function (e) {  
 body.setStyle('fontSize', e.newVal + 'px');  
});

I've also set up a navigation menu. Once the page's content is ready I plug the MenuNav Node Plugin into the nav node. It then automatically sets up a navigation menu based on the markup that it finds - as simple as that! Here's the code:

/*  
* MenuNav  
*/  
Y.on('contentready', function () {  
 this.plug(  
   Y.plugin.NodeMenuNav,  
     {mouseOutHideDelay: 1});  
}, '#nav');

Finally, I make the picture of yours truly draggable by simply instantiating a new drag and drop object and passing it a reference to my picture's class name. As soon as the object is created, the image is draggable. As an added touch, I change the mouse cursor when it hovers over the image so that it's apparent that the image is draggable:

/*  
* Drag and Drop  
*/  
 var dd = new Y.DD.Drag({  
   node: '.author'  
 });  
 
 Y.get('.author').setStyle('cursor', 'move');  
});
Summary

So, as you can see, YUI 3 is a completely different animal than its predecessor. Along with a new syntax, you gain a faster, lighter, easier, and more flexible library - ready to take on your most ambitious web projects.

Seeing this was a primer, we've barely scratched the surface of what's possible with YUI 3. For more reading, check out the YUI 3.x Preview Release 2 page, Satyen Desai's presentation on YUI 3, and the YUI 3 forum.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • http://www.justinwyllie.net Justin Wyllie

    Can you get a node with Y.get() as in

    1. Y.get(“#navProducts”).addClass(“selected”);

    I’m using 3.3.0 and it says no method ‘get’. one() and all() work.

  • http://www.justinwyllie.net Justin Wyllie

    Can you get a node with Y.get() as in

    1. Y.get(“#navProducts”).addClass(“selected”);

    I’m using 3.3.0 and it says no method ‘get’. one() and all() work.