Yahoo User Interface Library 2.x Cookbook

By Matt Snider
    Advance your knowledge in tech with a Packt subscription

  • Instant online access to over 7,500+ books and videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies
  1. USING YUI 2.x

About this book

The Yahoo! User Interface (YUI) Library is a set of utilities and controls, written in JavaScript, for building richly interactive web applications using techniques such as DOM scripting, DHTML, and AJAX. Although you can create stylish Internet applications by modifying its default components, even advanced users find it challenging to create impressive feature-rich Internet applications using YUI.

This book will help you learn how to use YUI 2.x to build richer, more interactive web applications that impress clients and wow your friends. It has recipes explaining over twenty-five YUI components, showing how to use them, and how to configure them to meet your needs. Each covered component will have extractable code samples that showcase the common ways that the component is used.

The book starts by explaining the core features of YUI 2.x, the utilities that the rest of the library depends on and that will make your life easier. It then explains how to build UI components and make AJAX requests using the YUI framework. Each recipe will cover the most common ways to use a component, how to configure it, and then explain any other features that may be available. We wrap things up by looking at some of the recent beta components and explain how to use them, and how they may be useful on your web application.

For each of the recipes, there is an introductory example, then more advanced examples, followed by an explanation of how the component works and what YUI is doing. For more experienced developers, most recipes also include additional discussion of the solution, explaining to further customize and enhance the component.

Publication date:
January 2011
Publisher
Packt
Pages
436
ISBN
9781849511629

 

Chapter 1. USING YUI 2.x

In this chapter, we will cover:

  • Fetching the latest version of YUI 2.x

  • Letting YDN manage YUI dependencies

  • Configuring the YUILoader component

  • Letting YUI manage my script dependencies

  • Importing CSS and JavaScript with the Get component

  • Configuring the Get component

  • Building namespaces

  • Including YUI CSS tools

  • Evaluating object types, the YUI way

  • Using hasOwnProperty to fix for ...in each loops

  • Extending JavaScript objects, the YUI way

  • Augmenting JavaScript objects

  • Detecting client browsers and platforms

  • Using the Cookie component

  • Using the JSON component

 

Introduction


In this chapter you will learn various ways to include, load, and set up the Yahoo! User Interface (YUI) library. Once YUI is loaded we'll explore how to use it to detect various browsers and platforms, build namespaces, include CSS and JavaScript files, and manage object-oriented JavaScript. We will also see how to use the static functions for working with browser cookies and JSON data.

Throughout this book we will cover how to use YUI 2.x and most of its components. However, even after reading a chapter you may still have questions or you may be interested in learning about a component that is not explained in this book. The Yahoo! Developers Network (YDN) has a comprehensive set of websites where you can find answers or ask questions from experts.

The documentation for all components:

http://developer.yahoo.com/yui/2/

The location for YUI:

http://yuilibrary.com/

And a direct link to the YUI 2 forums:

http://yuilibrary.com/forum/viewforum.php?f=4

 

Fetching the latest version of YUI 2.x


YUI is distributed by YDN and there are several different flavors of distributions depending on your needs. While you can run YUI by including scripts hosted on the YDN; if you plan to host YUI 2.x on your own servers, or to develop on machines that are not always connected to the Internet, then you will need a version of the library available locally.

How to do it...

Download the entire distribution from a zipped file:

http://yuilibrary.com/downloads/#yui2

Download the latest builds from GitHub:

http://github.com/yui/yui2

Use YDN to understand dependencies:

http://developer.yahoo.com/yui/articles/hosting/

How it works...

The zip file or GitHub download contains the complete YUI distribution, which includes: documentation in the as-docs folder, components in the build folder, and component demos in the examples folder.

Each of the components in the build folder will contain at least three JavaScript files. For example, when looking at the cookie component, the /build/cookie directory will contain these files: cookie.js, cookie-min.js and cookie-debug.js. The raw JavaScript, including comments, but no debugging code, will be in the file with the same name as the component.

The file named component-min.js will contain the compressed version of the JavaScript, which will contain no white-spaces, comments, or needless punctuation. The JavaScript is also obfuscated to further reduce its file-size, making it a good version to use on your production servers as it is much smaller than other versions, allowing for faster page loads.

The file named component-debug.js will contain the raw version of the JavaScript, including any debugging code. YUI developers are encouraged to add calls to YAHOO.log in there code to help with debugging. These calls are preserved in this version of the code, making its file-size the largest. This is a good version for your development machines and servers, as the logging will probably explain why components are not working.

Copy all the components that your project needs from the build directory to a web accessible folder in your project. Then you will be able to include these scripts as you would with any other JavaScript file.

There's more...

While you can fetch the latest released version using GitHub, you can also fetch the development version from GitHub. This is the version to which developers are checking in their latest changes, and as such it changes frequently. The development version will contain a similar file structure to the released version with an extra src folder containing the JavaScript files used to create the build directory. GIT is a powerful tool, but outside of the scope of this book. However, you can find out more information about downloading the development version here at http://yuilibrary.com/gitfaq/.

More on YDN dependencies

Each of the components in YUI, except for the Yahoo Global Object, is dependent on one or more other components. When including YUI library JavaScript files, you will need to manage these dependencies. For example, if you want to use the Animation component, you will need to include the JavaScript files for Yahoo Global Object, DOM, and Event components, prior to adding the file for the Animation component to the document. The documentation included with each component describes their dependencies. However, YDN has a configuration tool to simplify dependency management, by automatically showing you the order to include your scripts:

http://developer.yahoo.com/yui/articles/hosting/

Choose the components you plan on using, and the tool will show which component scripts are required and the order they should be included in your document.

YDN provides the Yahoo! or Google Content Delivery Networks (CDN). Use these if you do not intend to host your own version of YUI. Both CDNs are reliable (probably more reliable than your own hosting service), but you have the drawback that they will not be available when developing locally without Internet access.

If you are hosting files on your own servers, choose the Google CDN, and then select all the components you need. Copy the script tags at the bottom and include them in your document, replacing the Google URLs with your own.

If you are relying on the CDN to serve your files, then choose the Yahoo CDN, and enable rollup. This will configure YUI to combine the JavaScript file requests into as few as possible (usually just one request), where each component script is a parameter of the URL. Yahoo! will combine all the components into a single file and serve that instead of requiring a request per component as with the Google CDN or your own servers.

See also

Letting YDN manage YUI dependencies dynamically, for a more efficient way to include YUI.

 

Letting YDN manage YUI dependencies dynamically


While you can include all your YUI library files when the page loads, this can negatively affect the performance of your site, especially if some components aren't needed right away. To address this issue YUI built the YUILoader component, which can request scripts both when the page loads and on demand. Using the YUILoader allows developers to load the fewest number of YUI components on the initial page load, thereby improving site performance.

How to do it...

Here is how you would include the DataTable and Animation components using YUILoader:

<script type="text/javascript" src="pathToYUIBuild/yuiloader/yuiloader-min.js"></script>
<script type="text/javascript">
// Instantiate and configure YUI Loader:
(function() {
var loader = new YAHOO.util.YUILoader({
base: "pathToYUIBuild/",
require: ["animation","datatable"],
loadOptional: false,
combine: false,
filter: "MIN",
allowRollup: false,
onSuccess: function() {
//you can use of all requested YUI modules here
}
});
// Load the files using the insert() function.
loader.insert();
}());
</script>

How it works...

We included the yuiloader-min.js in the document, initializing the Yahoo Global Object, Get, and YUILoader components. When including the YUILoader component you do not need to include the Yahoo Global Object or Get components, as they are already part of YUILoader. The second script tag executes code to instantiate a new YUILoader instance, with a configuration object passed into the constructor. When loader.insert() is called, the YUI library uses the Get component to fetch and load the necessary files.

None of the configuration options are required, but you will probably want to define the required array to specify the scripts that need to be available for your code when the page loads. Additionally, the base property allows you to specify the root URL where files should be loaded from (path to the build directory). If you are hosting files locally, then you would use http://localhost/pathToYuiBuild.

There's more...

Sometimes you may be using code that is dependent on a legacy version of YUI and might be including two versions of YUI on the same page. When using multiple versions of YUI on the same page, they will probably conflict with each other. To prevent conflicts, YUI supports inserting sandboxed instances of library versions to prevent this. To use multiple versions of YUI, use the YUILoader sandbox function to load other versions of YUI inside the context of an anonymous function.

See also

Configuring YUILoader component, explains all the available loader configuration options.

Letting YDN manage My Script dependencies, explains how to use YUILoader to manage loading script dependencies for your own script files.

 

Configuring the YUILoader component


This recipe discusses the various configuration options that can be set on the object primitive passed into the constructor when instantiating the YUILoader.

How to do it...

var loader = new YAHOO.util.YUILoader({
/* Insert Options Here */
});

How it works...

Properties

Default

Description

allowRollup

true

This Boolean value indicates when YUILoader requests files; it should use aggregate files (like yahoo-dom-event) that pre-combine several YUI components in a single HTTP request. There is little reason to ever set this to false, unless debugging.

base

Yahoo CDN URL

This is a string value specifying the path to the YUI build files. This can be a full or relative file path to the build directory.

charset

undefined

This is a string value and will be applied as an attribute to insert DOM nodes.

combine

true

This Boolean value indicates that the YUI component should combine components fetching into a single request using the combo service provided on the Yahoo CDN.

comboBase

Yahoo CDN URL

This is a string value that points to the URL for combining components scripts into a single request.

data

undefined

This can be any value, which will be passed into the YUILoader callbacks. The callbacks are passed an object as the first argument and this value will be attached as the data property.

filter

'raw'

This can be one of three strings ('MIN','RAW', 'DEBUG') or an object literal. The filter modifies the default path for all components (e.g.'MIN' modifies "event.js" to "event-min.js").

force

undefined

This is an array of component names to be always loaded even if it is already present on the page.

ignore

undefined

This is an array of component names to never be loaded even if it is not present on the page.

insertBefore

undefined

This is an HTML element reference indicating the node where YUILoader should load Script elements before.

loadOptional

false

This is a Boolean value indicating if all optional dependencies should also be included. For optimal performance, you probably want to keep this false.

onSuccess

undefined

This is a function to be executed when your required YUI components are fully loaded.

onFailure

undefined

This is a function to be executed if the insertion fails.

onProgress

undefined

This is a function to be executed each time an individual module is loaded. Using this callback allows you to start working with some components before all of them are loaded.

onTimeout

function

This is a function to execute if the Get component detects a request timeout.

require

undefined

This is an array of component names to be dynamically loaded immediately. You may also add more requirements by calling the require() function on the YUILoader instance. Each subsequent call to require() appends the new requirements rather than overwriting the old ones. So you can add dependencies after instantiation:

loader.require('json', 'element', ...);

root

Yahoo CDN URL

This is a string value specifying the path to the YUI build files. This can be a full or relative file path to the build directory.

scope

window

The execution context for all callback functions.

skin

undefined

An object primitive defining the CSS skin that should be loaded with components. Object can contain the properties: base, defaultSkin, and overrides. The base property is the URL to the directory containing your skin CSS. The defaultSkin is sam by default and is the skin which is to be applied to all components. And overrides is an object whose keys are components and values are arrays of CSS file names to be used to skin those components, instead of defaultSkin.

skin: {
base: 'assets/skins/',
defaultSkin: 'sam',
overrides: {
datatable: ['myDataTableSkin'],
treeview: ['myTreeViewSkin']
}
}

varName

undefined

This is a script value and undefined by default.

For loading non-YUI scripts: This is the name of a variable that the utility will poll for in order to determine when the script is loaded. This is necessary in order to support loading non-YUI scripts in Safari 2.x.

For sandbox(): This specifies the name of a variable that should be present once the sandboxed script is available. A reference to this variable will be returned to the onSuccess handler (the reference field).

There's more...

Yahoo has open-sourced the PHP script which they use for rolling up component scripts into a single request. You can download this and use it to rollup YUI components served from your own servers, and/or modify it to combine your own scripts from http://github.com/yui/phploader.

More on callback function signatures

When defining a callback function, the first argument is always an object containing three properties: the data defined when instantiating YUILoader, a msg string when there is an error, and a success Boolean.

 

Letting YUI manage My Script dependencies


Not only can YUILoader manage script dependencies for the YUI library files, but it can also be configured to handle script dependencies for your JavaScript files as well. This way you can leverage YUI to download your own JavaScript files on demand.

Getting ready

This recipe assumes that you have instantiated YUILoader as shown in the Letting YDN manage YUI dependencies recipe.

How to do it...

Use the addModule function on an instance of YUILoader to configure the loader with the location of your JavaScript files and any dependencies they have. Once your components are added they can be referenced via the required function just like YUI components.

//Add the module to YUILoader
loaderInst.addModule({
name: "myComponent",
type: "js", // use "js" or "css"
path: "", // relative path to file
fullpath: "http://www.myDomain.com/js/myComponent.js",
requires: [], // optional
optional: [], // optional
after: [], // optional
varName: "myComponent"
});

How it works...

Calling loaderInst.addModule registers your JavaScript file with YUILoader. This recipe shows how to register a component named myComponent from an external site, http://www.myDomain.com. You must provide at least the name, type, and path (or fullpath) properties on the object primitive for a component to work correctly.

Property

Description

name

A unique name string, not already in use by YUI, see YUI Modules Names http://developer.yahoo.com/yui/yuiloader/#modulenames.

type

Either the string'js' or'css' depending on the components filetype.

path

A relative URL from the base property of YUILoader. If the base was set to http://someDomain.com, then the path would be /js/someComponent.js.

fullpath

The absolute URL of the module to include. In the example, the base was set to the Google CDN, so the full path is used.

requires

An array of component names that are needed by this component.

optional

An array of component names that may optionally be included with this component.

after

An array of component names that must be loaded prior to including this component. These are not technical dependencies, but will block the loading of this component.

varName

Use when loading external scripts that you do not control, such as the one in this recipe. YUILoader can determine when the script is fully loaded by polling for this property. However, it is only needed in order to support external scripts in Safari 2.x. All other A-Grade browsers work correctly without specifying the 'varName'.

There's more...

When including a JavaScript you do control, the best way to notify YUILoader that the script has loaded is to call YAHOO.register at the end of your file. YAHOO.register is the same function which the YUI components use to register themselves with the global "YAHOO" object and record information about themselves in the YAHOO.env registry. If you have control over the included script, you would register it as follows:

YAHOO.register("myComponent", myNameSpace.MyComponent, {
version: "2.8.0r4",
build: "99"
});

The YAHOO.register function requires three arguments:

  1. 1. The component was named'myComponent', when calling loaderInst.addModule, so it should be the same when calling YAHOO.register.

  2. 2. The constructor function or static object of your component.

  3. 3. The environment object should contain a version and build property indicating the current version and build information for the component.

The example uses YUI version and build numbers, but your components should use your build information.

Note

Using the YAHOO.register() function removes the need for a varName property when loading external scripts.

 

Importing JavaScript and CSS with the Get component


In previous recipes, we have explored how to configure YUI to automatically include JavaScript and CSS files. Behind the scenes, YUI is using the Get component to insert elements into the page and notify when the files are loaded. If you'd rather manually import scripts and CSS, this recipe will show you how to use the Get component to import files dynamically.

Getting ready

To use the Get component, you must include the YUI object and the Get component:

<script src="pathToBuild/yahoo/yahoo-min.js" ></script>
<script src="pathToBuild/get/get-min.js" ></script>

How to do it...

Fetch a single JavaScript file:

var config = {
onSuccess: function() {
alert("file loaded");
}
};
var url = "http://www.myserver.com/js/myJsFile.js";
var transaction = YAHOO.util.Get.script(url, config);

Fetch several JavaScript files:

var config = {
onSuccess: function() {
alert("all files loaded");
}
};
var url = ["/js/jsFile1.js", "/js/jsFile2.js"];
var transaction = YAHOO.util.Get.script(url, config);

Fetch a single CSS file:

var config = {
onSuccess: function() {
alert("file loaded");
}
};
var url = "http://www.myserver.com/css/cssJsFile.css";
var transaction = YAHOO.util.Get.css(url, config);

Fetch several CSS files:

var config = {
onSuccess: function() {
alert("all files loaded");
}
};
var url = ["/css/cssFile1.css" "/css/css File2.css"];
var transaction = YAHOO.util.Get.css(url, config);

How it works...

When the script function executes it first looks to see if the script has already been appended and loaded. If not, it appends a Script element to the targeted window. The src attribute of the new Script element is then set to the provided URL of the JavaScript file. If there are multiple scripts, then they are all added at the same time. When a requested file is fetched, parsed, and executed its node fires a load event. The Get component will wait until the load event fires from all the scripts, and then it will execute the provided onSuccess callback.

Importing CSS using the Get component follows the same pattern as importing JavaScript files, except Link elements are inserted into the DOM instead of Script elements.

There's more...

Most browsers do not consider 404 (page not found) errors occurring when setting the src attribute of the Script element, a failure. As a result the onSuccess handler may fire even if the script fails to load.

While you can use the Get component to request cross-domain JavaScript files, there exists a security restriction in IE 6, when using the Get utility inside of an Iframe element. In IE 6, Get requests made to a cross-domain URLs fail, when made inside of an Iframe element.

Browser issues with multiple CSS imports

Some browsers, (Safari and Firefox at the time this chapter was written), do not fire the load event on link elements. This is not an issue if you are importing only one stylesheet at a time, but causes problems when importing multiple. As a result, YUI cannot ensure the order that the CSS files load. For this reason progressive style enhancements, from one stylesheet to the next, may load out of order.

You can work around this by importing one stylesheet at a time, and polling the DOM to see if one or more styles from the imported CSS file have been applied to the DOM element, before importing the next stylesheet. Or you can import the second stylesheet in the success callback function of the first.

 

Configuring the Get component


As shown in the previous two recipes, a configuration object is passed to the Get component when importing CSS or JavaScript. Some of those configuration properties are functions that execute as callbacks for various events. This recipe explains the available configuration properties for the Get component and the properties on the callback object that is passed into the callback functions.

How to do it...

Here are all the properties available for the Get component configuration:

var config = {
autopurge: true,
data: null,
onFailure: function() {/* ... */ },
onProgress: function() {/* ... */ },
onSuccess: function() {/* ... */ },
onTimeout: function() {/* ... */ },
scope: window,
timeout: 10000,
win: window
};
YAHOO.util.Get.css('/myserver.com/js/library.js', config);

Here is an example of the Get component callback object:

var onSuccess = function(o) {
alert(o.tId);
alert(o.data);
alert(o.nodes.length);
alert(o.win);
o.purge();
};

How it works...

The following table explains how each of these properties works:

Property

Explanation

autopurge

This property indicates that the old elements used to import CSS and JavaScript should be automatically removed from the DOM, when the threshold of 20 (defined on the static YAHOO.util.Get.PURGE_THRESH property) is reached. This keeps the DOM clean and is generally safe with Script elements as once loaded the content remains available, regardless of whether the Script element remains in the DOM. However, the same is not true of Link elements, which will remove the CSS if they are removed from the DOM. Therefore, this property defaults to true for Script elements but false for Link elements.

data

A value to pass into the callback functions; this is null by default.

onFailure

The callback function executed when an error is detected or abort is called.

onProgress

The callback function executed after each import node is loaded into the DOM.

onSuccess

The callback function executed after all files are successfully loaded into the DOM.

onTimeout

The callback function executed if the files fail to load before the timeout property is reached.

scope

The execution context for the callback functions; this defaults to the current window.

timeout

The numbers of milliseconds to wait for a file to load before executing the onTimeout callback function.

win

The window to append elements into; this default to the current window.

The following table explains the properties available on the callback object:

Property

Explanation

tId

A string unique identifier for this transaction.

data

The value provided as the data property of the Get component configuration object.

nodes

An array of nodes added to the DOM during the execution of the current Get component transaction.

win

The value provided as the win property of the Get component configuration object (should be a window object); default will be the current window.

purge()

A function that will immediately remove the nodes created during the current Get component transaction. As stated in the previous recipe, this is safe and a good practice when importing JavaScript, but not when importing CSS.

 

Building namespaces


When following JavaScript best practices, it is recommended to attach as few variables as possible to the global namespace. This way the code added by JavaScript files and libraries do not accidentally conflict with each other, because the code exists under a unique namespace. YUI developers follow this convention by attaching all YUI components under a single global object, YAHOO.

How to do it...

Create the namespace YAHOO.yourproduct1:

YAHOO.namespace("yourproduct1");
YAHOO.yourproduct1.Class1 = function(arg1) {
alert(arg1);
};

Create a chained namespace for yourproduct2 and subproject1:

YAHOO.namespace("yourproduct2.subproject1");
YAHOO.yourproduct2.subproject1.Class1 = function(arg1) {
alert(arg1);
};

Create several namespaces at once:

YAHOO.namespace("yourproduct3", "yourproduct4", ...);
YAHOO.yourproduct3.Class1 = function(arg1) {
alert(arg1);
};
YAHOO.yourproduct4.Class1 = function(arg1) {
alert(arg1);
};

How it works...

The YAHOO.namespace function creates the specified namespace under the YAHOO object. Each argument should be a string containing the name of the desired namespace. You may also specify namespace chains by using a dot (.) to separate each name in the hierarchy. Any number of namespaces may be created at one time by passing that many arguments into the namespace function. The namespace function also returns a reference to the last namespace object created.

There's more...

A namespace is simply a JavaScript object whereon you can organize a component or related components. It is available on the YAHOO object immediately after calling the namespace function. If the namespace already exists, the existing object will be returned instead of creating a new one. Using the namespace function prevents you from overriding existing object, as might happen if you were to use YAHOO.myComponent = {}; instead.

The YAHOO object automatically generates and reserves namespaces for YAHOO.lang, YAHOO.util, YAHOO.widget, YAHOO.env, YAHOO.tool, and YAHOO.example.

 

Including YUI CSS


YUI contains several general CSS files that are useful for normalizing browser default style variations, font-sizes, and grid-based layouts. Additionally, many components also include a component-specific CSS files. While each component can be used without including the base CSS, for best results you should include them or incorporate them into your own CSS. This recipe will explore the global CSS files and explain how component-based CSS will work in the future.

How to do it...

To normalize variations in the default element styles across browsers, include:

<link href="pathToBuild/reset/reset-min.css" rel="stylesheet" type="text/css"/>

To set the base font-size of the Body element to 10 pixels, include:

<link href="pathToBuild/font/font-min.css" rel="stylesheet" type="text/css"/>

To set some intelligent default styles to various elements, include:

<link href="pathToBuild/base/base-min.css" rel="stylesheet" type="text/css"/>

To use YUI to manage your layout, include:

<link href="pathToBuild/grids/grids-min.css" rel="stylesheet" type="text/css"/>

To use grids create a Div element inside the Body element and add one of the "doc" IDs to it ("doc", "doc2", "doc3", "doc4"), use code similar to the following:

<div id="doc2">
<div class="hd">Put header here</div>
<div class="bd">Put body here</div>
<div class="ft">Put footer here</div>
</div>

Later in this book, we will include components which have their own custom CSS, such as the Logger component:

<link href="pathToBuild/logger/assets/skins/sam/logger.css" rel="stylesheet" type="text/css"/>

Components like Logger, will namespace all their styles below the"yui-skin-sam" class, and therefore require the"yui-skin-sam" to be added to an ancestor element of the component markup. Most developers choose to simply apply it to the Body element:

<body class="yui-skin-sam">
...
</body>

How it works...

The reset CSS file applies rules that remove styles, such as font-sizes, padding, margins, and borders that may be applied to various elements by the browser. This ensures that CSS rules will be the same across browsers, as all elements start being styled the same way. The rules in this file have been built by the web community and are found on many sites and in many CSS frameworks.

The fonts CSS file adjusts the font-size of the Body element so that 1em is exactly 10 pixels (or as approximately close to 10 as possible). This makes building sites that scale according to font-size much easier. There are also a few additional rules that correct rounding errors and style discrepancies on several elements.

The base CSS file applies some intelligent default styles, since all the defaults have been reset, such as header (H1, H2, etc.) tag font-sizes, bolding of the Strong element, and italicizing of the Em element.

The grids CSS file sets up rules that can be used to layout the page. The example in this recipe will be centered on the page with a 950 pixel width, and have simple textual content for the header, body, and footer. You can use grids to manage simple or nested columns, and even more complex document sections. To find out more about what you can do with grids, see: http://developer.yahoo.com/yui/grids/.

The CSS for components will be mentioned in their respective chapters, but it is important to remember that the files are generally located at "someComponent/assets/skins/sam/someComponent.css" and that the"yui-skin-sam" class needs to be applied to an ancestor of the component markup for the styles to be applied.

There's more...

Additionally, there are two convenient files that combine the minified versions of reset, font, and grid for use in your production environment:

<link href="pathToBuild/reset-fonts/reset-fonts.css" rel="stylesheet" type="text/css"/>
<link href="pathToBuild/reset-fonts-grids/reset-fonts-grids.css" rel="stylesheet" type="text/css"/>
 

Evaluating object types, the YUI way


Although JavaScript is a loosely typed language, there may be instances where you need to ensure that a variable is of a particular type. YUI provides several convenient functions for simplifying variable type detection, and this recipe shows how to use these functions.

How to do it...

All YUI type detection functions are available on the YAHOO.lang namespace.

Detecting if a variable is an instance of an Array:

var o = [];
if (YAHOO.lang.isArray(o)) {
alert('You have an array value');
}

Detecting if a variable is an instance of a Boolean:

var o = true;
if (YAHOO.lang.isBoolean(o)) {
alert('You have a boolean value');
}

Detecting if a variable is an instance of a Function:

var o = function() {};
if (YAHOO.lang.isFunction(o)) {
alert('You have a function');
}

Detecting if a variable is equal to null:

var o = null;
if (YAHOO.lang.isNull(o)) {
alert('You a NULL value');
}

Detecting if a variable is an instance of a Number:

var o = 4;
if (YAHOO.lang.isNumber(o)) {
alert('You have a number value');
}

Detecting if a variable is an instance of an Object:

var o = {};
if (YAHOO.lang.isObject(o)) {
alert('You have an object value');
}

Detecting if a variable is an instance of a String:

var o = 'test string';
if (YAHOO.lang.isString(o)) {
alert('You have a string value');
}

Detecting if a variable is undefined:

var o;
if (YAHOO.lang.isUndefined(o)) {
alert('You have an undefined value');
}

Lastly, detecting if a variable has a value:

var o = false;
if (YAHOO.lang.isValue(o)) {
alert('You have a value');
}

How it works...

As you might imagine, YUI uses a combination of the typeof and === operators to determine the type of the provided argument. These functions accept a single argument, which will be evaluated and a Boolean value is returned. The isValue function returns false if the value is undefined, null, or NaN, but will return true for false values such as 0, false, and empty strings ('').

There's more...

The most frequent reason you will use the functions in this recipe is when a function argument can be of multiple object types. For example, YAHOO.util.Dom.get function can accept a string, HTMLElement, or an array as the first argument. The function uses type detection on the first argument to determine which code path to use when executing.

More on function detection in IE

Internet Explorer thinks certain DOM functions are objects:

var obj = document.createElement("object");
YAHOO.lang.isFunction(obj.getAttribute) // reports false in IE
var input = document.createElement("input"); YAHOO.lang.isFunction(input.focus) // reports false in IE

You will have to implement additional tests if these DOM functions matter to you.

 

Using hasOwnProperty to fix for...in loops


One of the shortcomings of JavaScript is that it doesn't differentiate well if a property is applied directly to the current object or not. This can cause issues with for ... in loop where it returns unnecessary functions and values from the object's prototype. Newer browsers have implemented a hasOwnProperty function, which differentiates between properties added to the object instance versus its prototype. YUI has implemented its own version of the hasOwnProperty function for older browsers that do not support it. Use this recipe when iterating on for ... in loops.

Getting ready

Here is the problem with for...in loops. If you create an object and add a function to it:

var MyObject = function(foo, bar) {
this.foo = foo;
this.bar = bar;
};
YAHOO.extend(MyObject, Object);

Then you instantiate the object:

var obj = new MyObject('test1', 'test2', 'test3');

When you iterate on the instantiated object, using the for...in loop:

for (var key in obj) {
alert(key);
}

It will alert the property values and the constructor function name: test1, test2, test3, constructor, but you only want the property values.

How to do it...

Instead iterate on the object using a for ... in loop and call the hasOwnProperty function located on the YAHOO.lang namespace:

for (var key in obj) {
if (YAHOO.lang.hasOwnProperty(obj, key)) {
alert(key);
}
}

Now this will alert only the property values test1, test2, test3.

How it works...

The hasOwnProperty function returns false if the property is not set directly to the current object. By default YUI uses the built in browser support for hasOwnProperty, but implements a similar fallback version of the function in older browsers.

There's more...

YUI do not modify any native JavaScript objects, and if your code does not either, you may not need to use the hasOwnProperty, when iterating on a for ... in loop. However, it is considered best practice to always check the hasOwnProperty, thereby guaranteeing the correct behavior, especially if you are including third-party scripts.

Note

It is considered a bad practice to modify the prototype of native JavaScript objects, such as Object. Because YUI uses native objects to build its components, modifying their behavior could have unexpected cascading consequences.

 

Extending JavaScript objects, the YUI way


JavaScript uses prototype-based object oriented programming, where behavior reuse (called inheritance in classical OOP) is handled by cloning the prototypes of existing objects. Therefore JavaScript does not have traditional classes or class-based inheritance. However, since inheritance is so powerful and needed to build scalable/extendable JavaScript libraries, that YUI implemented Pseudo-Classical Inheritance with the YAHOO.lang.extend function. This recipe explains how to use YAHOO.lang.extend.

How to do it...

First create the Parent function (sometimes called a 'class') and augment its prototype:

YAHOO.test.Parent = function(info) {
alert("Parent: " + info);
};
YAHOO.test.Parent.prototype = {
testMethod: function() {
alert("Class: Parent");
}
};

Here is how Parent is instantiated and used:

var parentInstance = new YAHOO.test.Parent(1234);
alert(parentInstance.testMethod()); // alerts 'Class: Parent'

Now create the Child function (sometimes called a 'subclass') and use extend to have it inherit from the Parent function:

YAHOO.test.Child = function(info, arg1, arg2) {
// chain the constructors
YAHOO.test.Child.superclass.constructor.apply( this, arguments);
// or YAHOO.test.Child.superclass.constructor.call( this, info, arg1, arg2);
};

Next, have the Child extend the Parent function. This must be done immediately after the Child constructor, before modifying the prototype of Child:

YAHOO.lang.extend(YAHOO.test.Child,YAHOO.test.Parent, {
testMethod: function() {
alert("Class: Child");
}
});

You can also manually modify the prototype of Child after the extension, without affecting the Parent:

YAHOO.test.Child.prototype.parentTestMethod = function() {
return YAHOO.test.Child.superclass.testMethod.call(this);
};

Here is how a child is instantiated and used:

var childInstance = new YAHOO.test.Child("constructor executed");
childInstance.testMethod("testMethod invoked"); // alerts: 'Child: testMethod invoked'
childInstance.parentTestMethod("testMethod invoked"); // alerts: 'Parent: testMethod invoked'

How it works...

The YAHOO.lang.extend function requires a Child and a Parent function as the first and second arguments. The third argument is optional, but is an option object containing additional properties and functions to apply to the prototype property of the Child function. Be aware that the prototype property of the Child function will be overwritten by YAHOO.lang.extend, while the Parent function will remain unmodified.

Under the hood, the YAHOO.lang.extend function instantiates a constructor-less clone of the Parent function and assigns the instantiated object to the prototype property of the Child function. In this way the prototype of the Child function will be a separate object from the prototype of the Parent function, but still reference the same properties and functions as the Parent function. Therefore modifying the prototype of the Child function does not affect the prototype of the Parent function.

The Parent function is set to a special property, superclass, which is attached directly to the Child constructor function. Therefore, you can access any function on the Parent function that may have been overwritten by the Child function. In this recipe, superclass was used in the constructor of the Child function and one of its functions. In the Child constructor the Parent function constructor is called and executed (YAHOO.test.Child.superclass.constructor). Anytime you use superclass to access the Parent function, you need to use either the call or apply functions so the parent function executes using the current execution context (this).

In the parentTestMethod function, superclass is called to fetch and execute the testMethod from the Parent function, even though it had been overwritten by the Child function.

There's more...

You can specify additional prototype properties of the Child function by using the third argument of YAHOO.lang.extend, but you are not required to. At any point after calling YAHOO.lang.extend, you may alter the prototype property of the Child function, as shown in the recipe by the creation of the parentTestMethod function. Modifying the prototype of the Child function will not affect the Parent function.

Note

It is a good convention to capitalize functions used as classes, so you can easily tell them apart from the non-class functions.

 

Augmenting objects using YUI


YUI can combine two objects for you by applying the values from a supplier object to a receiver object. This recipe explains how to augment an object using the YAHOO.lang.augmentObject function.

How to do it...

To apply all members of the supplier object to the receiver object, unless it would override an existing value on the receiver:

var receiver = {
testValueA: 1
};
var supplier = {
testValueA: 2,
testValueB: 3
};
YAHOO.lang.augmentObject(receiver, supplier);

The receiver will have testValueA equals to 1 and testValueB equal to 3, as all values from the supplier are added to the receiver, except testValueA, because it would have overwritten an existing value in the receiver.

Apply all members of the supplier object to the receiver object, overriding any existing values on the receiver:

var receiver = {
testValueA: 1,
testValueC: 4
};
var supplier = {
testValueA: 2,
testValueB: 3
};
YAHOO.lang.augmentObject(receiver, supplier, true);

The receiver will have testValueA equal to 2, testValueB equal to 3, and testValueC equal to 4, as all values from the supplier are added to the receiver, overwriting any existing values.

Apply some members of the supplier object to the receiver object:

var receiver = {
testValueA: 1
};
var supplier = {
testValueB: 2,
testValueC: 3,
testValueD: 4
};
YAHOO.lang.augmentObject(receiver, supplier, 'testValueB', 'testValueC');

The receiver will have testValueA equal to 1, testValueB equal to 2, and testValueC equal 3, as only the testValueB and testValueC properties are copied. You can specify any number of properties to copy by adding the names as additional arguments to the augmentObject function.

How it works...

The augmentObject function uses a for ... in loop and the hasOwnProperty function to iterate through each value in the supplier object. The function then evaluates whether it should use or override the current value on the supplier object, as defined by its third argument. The receiver object is modified directly with references to the values in the supplier object.

There's more...

One of the best ways to use this recipe is when using configuration arguments. For example, you may have a class with several properties that may be overwritten when instantiated, but should use default values when not overwritten:

var TestClass = function(conf) {
this.conf = {};
// push default configuration onto this.conf
YAHOO.lang.augmentObject(this.conf, TestClass.DEFAULT_CONFIG);
// overwrite default with user provided configuration
YAHOO.lang.augmentObject(this.conf, conf || {}, true);
};
TestClass.DEFAULT_CONFIG = {
name: 'matt',
isUser: true
};
var testClassObj = new TestClass({
name: 'janey'
});
alert(testClassObj.conf.name); // 'janey' overwrote 'matt'
alert(testClassObj.conf.isUser); // 'true' from DEFAULT_CONFIG

By using YAHOO.lang.augmentObject the DEFAULT_CONFIG object remains unchanged, but populates the configuration of the local configuration object with its values. Then the properties of the local configuration object can be overwritten by the properties on the object passed into the constructor when instantiating TestClass.

See also

Using hasOwnProperty to fix for ... in loops recipe, covered earlier in this chapter, explains how YAHOO.lang.hasOwnProperty works to properly iterate through the values stored in JavaScript objects.

 

Detecting client browser and platforms


Although YUI is a cross-browser library, there may be instances where your application needs to behave differently depending on the user's browser or platform. Browser and platform detection are simple tasks in YUI, and this recipe shows how it is done.

How to do it...

YUI stores browser and client information on the YAHOO.env.ua namespace by setting the version number to a property for each browser.

Browsers such as Mozilla, Camino, and Firefox use the Gecko rendering engine, and are detected with YAHOO.env.ua.gecko:

if (YAHOO.env.ua.gecko) {
alert('You are using a Gecko based browser.');
}

Internet Explorer version is stored as YAHOO.env.ua.ie:

if (YAHOO.env.ua.ie) {
alert('You are using IE');
}
if (6 === YAHOO.env.ua.ie) {
alert('Upgrade your browser immediately.');
}
else if (7 === YAHOO.env.ua.ie) {
alert('You probably should upgrade.');
}
else if (7 < YAHOO.env.ua.ie) {
alert('Finally, a decent version of IE'.);
}

Opera is detected with YAHOO.env.ua.opera:

if (YAHOO.env.ua.opera) {
alert('You are using Opera.');
}

Browsers such as Safari and Chrome use the Web-Kit rendering engine, and are detected with YAHOO.env.ua.webkit:

if (YAHOO.env.ua.webkit) {
alert('You are using a Web-Kit based browser.');
}

When it comes to platform detection, Adobe AIR is detected using YAHOO.env.ua.air:

if (YAHOO.env.ua.air) {
alert('You are using Adobe AIR');
}

Mobile platforms are detected using YAHOO.env.ua.mobile:

if (YAHOO.env.ua.mobile) {
alert('You are using an A-Grade mobile platform');
}

And the operating system is available on YAHOO.env.ua.os, but is limited to'macintosh' or'windows':

if ('windows' == YAHOO.env.ua.os) {
alert('You are using a Windows PC');
}
if ('macintosh' == YAHOO.env.ua.os) {
alert('You are using a Mac');
}

Yahoo also detects the version of Google CAJA being used on YAHOO.env.ua.caja:

if (YAHOO.env.ua.caja) {
alert('You are using Google CAJA');
}

And it detects whether the page appears to be loaded under SSL as YAHOO.env.ua.secure:

if (YAHOO.env.ua.secure) {
alert('You are running a secure site');
}

How it works...

The values for YUI's browser and platform detection are mostly obtained from parsing the platform and userAgent properties of the JavaScript document.navigator object.

There's more...

When populating the YAHOO.env.ua values for Gecko and Web-Kit based browsers, YUI uses the browser's build number instead of the browser version number. To convert the build number to specific version of Safari or Firefox, see how the build numbers map to version numbers in the YUI documentation, http://developer.yahoo.com/yui/docs/YAHOO.env.ua.html.

Graded browser support

Graded browser support is a process for managing what browsers and browser versions YUI will be tested against and support. YUI has four grades of browsers: grade A means it is well tested and all features should be supported; grade C are deprecated browsers that are still commonly used and the core features are supported, but not all the widgets; grade X are beta browsers or fringe browsers that YUI may or may not work on, and they receive no official support; every other browser (for example, versions of Internet Explorer prior to 6) are not graded and receive no support. Generally speaking, if you use a major browser on a common operating system, and keep it up-to-date, then you will be using an A-grade browser.

A complete write-up on Graded Browser Support is available at http://developer.yahoo.com/yui/articles/gbs/index.html.

More on mobile detection

Currently mobile detection is limited to Safari on the iPhone/iPod Touch, Nokia N-series devices with the Web Kit-based browser, and any device running Opera Mini. YUI leverages the best and most reliable techniques for detecting platforms and these browsers are the ones that can be reliably detected.

More on Caja

Caja allows developers to safely include AJAX applications from third parties, and enables rich interaction between the embedding page and the embedded applications. For more information see http://code.google.com/p/google-caja/. Yahoo! uses Caja to power their application platform.

Note

When using JavaScript best practices, browser and/or client detection should be a last resort, because it is not an exact science. A better approach is to detect the existence of a needed object or function. For example, older versions of IE do not implement indexOf on the Array object. So before using this function, instead of detecting if the browser is IE, detect if the function indexOf exists.

 

Using the Cookie component


Cookies are a powerful way to store variables between user visits. YUI has a set of static functions that simplifies the reading and writing of cookies. Additionally, you can use it to write fewer cookies by storing multiple values in a single cookie. This recipe will explain how to use the static functions of the Cookie component, available on the YAHOO.util.Cookie namespace.

How to do it...

The simplest way to add a cookie that expires at the end of the current user session is:

YAHOO.util.Cookie.set('testCookie1', 'testValue');

You can provide a third optional object literal argument to define any of these additional cookie properties:

YAHOO.util.Cookie.set('testCookie2', 'testValue', {
expires: new Date("January 1, 2012"), // last until date
domain: 'myserver.com', // bind to your server
path: '/', // bind to a path on the server
secure: true // use SSL
});

Use the following to read a cookie:

var value = YAHOO.util.Cookie.get("testCookie1");

To convert the cookie to another basic data type, such as a Number, use:

var value = YAHOO.util.Cookie.get("aNumberCookie", Number);

To convert the cookie to another data type manually, use:

var value = YAHOO.util.Cookie.get("aHexCookie", function(stringValue) {
return parseInt(stringValue, 16); // hexadecimal value
});

When deleting a cookie you must specify the same options as were used when creating the cookie (except expires). Here is how to delete the two cookies we previously created:

YAHOO.util.Cookie.remove('testCookie');
YAHOO.util.Cookie.remove('testCookie2', {
domain: 'myserver.com',
path:'/',
secure: true
});

To check for the existence of a cookie, use:

var cookieName = 'testCookie1';
if (YAHOO.util.Cookie.exists(cookieName)) {
alert('Cookie "' + cookieName + '" exists');
}

To reduce the number of cookies used by your site, the YUI Cookie component provides a subcookie framework, which stores multiple pieces of data into a single cookie. First create the configuration options that will be used for the subcookies:

var cookieOptions = {
domain: "yourserver.com"
};
var cookieName = 'testCookie';

To set a subcookie use:

var subCookieName = 'count';
var subCookieValue = 22;
YAHOO.util.Cookie.setSub( cookieName, subCookieName, subCookieValue, cookieOptions);

To set many subcookies at one time use:

var testCookiesObject = {
count: 22,
name: 'matt',
isUser: true
};
YAHOO.util.Cookie.setSubs( cookieName, testCookiesObject, cookieOptions);

To fetch a subcookie, use:

var value = YAHOO.util.Cookie.getSub(cookieName, 'count');

You may also convert subcookies to non-string datatypes by passing a third argument:

var value = YAHOO.util.Cookie.getSub( cookieName, 'count', Number);

To retrieve all subcookies as an object, use:

var oData = YAHOO.util.Cookie.getSubs(cookieName);

And to remove a subcookie use:

YAHOO.util.Cookie.removeSub( cookieName, "count", cookieOptions);

How it works...

The cookie property of document is where browsers store cookies for the JavaScript engine. The YUI Cookie component reads and writes to this value in a simple and concise way, so you don't have to. It is important to remember when creating cookies that if you do not specify the expires property, then the cookie will expire at the end of the user's session. Also, when deleting cookies, the same options that were used to create the cookie should be passed to the remove function or the cookie will not be removed.

There's more...

Subcookies are cookies containing a collection of other name/value pairs. This is accomplished by using a URL parameter query like structure:

cookiename=subcookiename1=value1&subcookiename2=value2&subcookiename3=value3

When a cookie is converted into a subcookie, any existing value will be overwritten by the subcookies infrastructure.

To learn more about cookies and see additional examples, visit http://developer.yahoo.com/yui/cookie/.

 

Using the JSON package


JSON is a powerful notation used to store and/or transfer data, especially to and from web servers. YUI has a set of static functions that simplifies JSON validation, parsing, and formatting. This recipe will explain how to use the static functions provided by the JSON Component, available on the YAHOO.util.JSON namespace.

How to do it...

To parse JSON strings into JavaScript data, use:

var jsonString = '{"id":1234,"amount":20.5,"isNew":true,"name":null}';
var data = YAHOO.lang.JSON.parse(jsonString);

If your JSON string is malformed, then the parse function will throw exceptions (this happens a lot), so you should usually wrap the call in a try/catch block:

try {
var data = YAHOO.lang.JSON.parse(jsonString);
// use the data
if (data.isNew && 20 < data.amount) {
alert(data.id + ',' + data.name);
}
}
catch (e) {
alert("Invalid product data");
}

A second, optional argument (a filtering function) may be passed to the parse function. This function filters the resulting JSON object by replacing the original parsed value by the value returned by the filtering function (returning undefined will cause the property to be removed):

function myFilter(key,val) {
// format amount as a string
if (key == "amount") {
return '$' + val.toFixed(2);
}
// omit keys by returning undefined
else if (key == "name") {
return undefined;
}
return val;
}
var filteredData = YAHOO.lang.JSON.parse(jsonString, myFilter);
if (YAHOO.lang.isUndefined(filteredData.name)) {
alert("The name property does not exist");
}
alert("Amount = " + filteredData.amount); // "$20.50"

To convert JavaScript objects into JSON strings, use:

var myData = {
fruits: [
{name: 'apple', qty: 12, color: 'red'},
{name: 'banana', qty: 5, color: 'yellow'}
]
};
var jsonStr = YAHOO.lang.JSON.stringify(myData);

This creates the string:

{"fruits":[{"name":"apple","qty":12,"color":"red"},{"name":"banana"," qty":5,"color":"yellow"}]}

To only convert some of the object properties into a JSON string, you can provide a second, optional argument to filter for keys to keep an array of key names or a filter function. To use the array to white list the name and qty properties, use:

var jsonStr = YAHOO.lang.JSON.stringify(myData.fruits, ['name','qty']);

This creates the string:

{"fruits":[{"name":"apple","qty":12},{"name":"banana","qty":5}]}

Instead, you might use the replacer function to filter:

function replacer(key, value) {
// replace the 'qty' value
if (key === 'qty') {
if (12 > value) {
return 'less than a dozen';
}
else {
return value / 12 + 'dozen';
}
}
// keep the 'name' value
else if (key === 'name') {
return value;
}
// filter any other keys
else {
return undefined;
}
}
var jsonStr = YAHOO.lang.JSON.stringify(myData, replacer);

This creates the string:

{"fruits":[{"name":"apple","qty":"1 dozen"},{"name":"banana","qty":"less than a dozen"}]}

By default the strings created by the stringify function are flat, having no extra white-spaces. If you need the results to be legibly spaced then pass in the optional integer third argument (indicates number of spaces for indentation):

var jsonStr = YAHOO.lang.JSON.stringify( myData, ['name','qty'], 4);

This creates the more readable string:

{
"fruits": [
{
"name":"apple",
"qty":12
},
{
"name":"banana",
"qty":5
}
]
}

How it works...

The parse function validates the provided string against a series of regular expressions, ensuring the string consists of a valid JSON object and that it is not malicious. Then the function executes an eval statement to convert the value into its JavaScript object. Afterwards, any provided filter function will be applied against the JSON object.

The stringify function iterates through the properties of the provided object and converts them to JSON strings. The function does this by delegating to the native toJSON function or falling back to similar YUI-based toJSON implementations. As the object is iterated on the filtering and formatting functions are applied to each property, an array of strings for white-listing keys can only be used when your JSON data is an array of objects.

There's more...

In YUI version 2.8, the stringify and parse functions were changed to default to the native browser JSON.parse and JSON.stringify. The native implementations are much faster and safer than any JavaScript implementation. Consequently, the stringify function's signature was changed to more closely emulate the ECMAScript version 5 specification.

See the webpage, http://developer.yahoo.com/yui/json/, for more JSON examples and additional explanations.

More on parsing options

If you do not want to use the native browser JSON functions, you can force YUI to use the JavaScript versions of either by setting the following values to true:

YAHOO.lang.JSON.useNativeParse = false;
YAHOO.lang.JSON.useNativeStringify = false;

Note

The YUI JSON package is based on an open-source project started by Douglas Crockford. Find out more about JSON at http://www.json.org.

About the Author

  • Matt Snider

    Matt Snider is a software engineer and JavaScript enthusiast. He has built various web applications from the ground up, since 2003, but has maintained a passion for the UI. He quickly discovered YUI because of its early adoption, great documentation, and strong open-source community. Matt currently maintains a blog dedicated to web application development, leads the UI team at http://www.mint.com, and authored the YUI 2.x Storage Component. In the near future, he plans to port the Storage component to YUI 3. Examples of his work can be found on his personal website http://www.mattsnider.com.

    Browse publications by this author
Book Title
Unlock this book and the full library for FREE
Start free trial