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 loopsExtending JavaScript objects, the YUI way
Augmenting JavaScript objects
Detecting client browsers and platforms
Using the Cookie component
Using the JSON component
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:
And a direct link to the YUI 2 forums:
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.
Download the entire distribution from a zipped file:
http://yuilibrary.com/downloads/#yui2
Download the latest builds from GitHub:
Use YDN to understand dependencies:
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.
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/.
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.
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.
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>
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
.
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.
This recipe discusses the various configuration options that can be set on the object primitive passed into the constructor when instantiating the YUILoader.
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 |
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 |
filter |
'raw' |
This can be one of three strings ( |
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 |
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
|
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: 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
|
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.
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.
This recipe assumes that you have instantiated YUILoader as shown in the Letting YDN manage YUI dependencies recipe.
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" });
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 |
path |
A relative URL from the |
fullpath |
The absolute URL of the module to include. In the example, the |
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'. |
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. The component was named
'myComponent'
, when callingloaderInst.addModule
, so it should be the same when callingYAHOO.register
.2. The constructor function or static object of your component.
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.
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.
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>
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);
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.
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.
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.
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.
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(); };
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 |
data |
A value to pass into the callback functions; this is |
onFailure |
The callback function executed when an error is detected or |
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 |
scope |
The execution context for the callback functions; this defaults to the current |
timeout |
The numbers of milliseconds to wait for a file to load before executing the |
win |
The |
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 |
nodes |
An array of nodes added to the DOM during the execution of the current Get component transaction. |
win |
The value provided as the |
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. |
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
.
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); };
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.
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
.
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.
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>
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.
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"/>
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.
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'); }
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 ('').
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.
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.
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.
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.
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
.
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.
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.
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
.
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'
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.
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.
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.
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.
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.
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
.
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.
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'); }
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.
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 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.
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.
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.
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.
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);
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.
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/.
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.
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);
{"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 } ] }
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.
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.
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.