Upload
cyrildoussin
View
11.698
Download
0
Tags:
Embed Size (px)
DESCRIPTION
My presentation for the YAHOO! Europe Front-End Engineering Summit in December 2007.
Citation preview
Building interactive widgets with YUI
Cyril DoussinYahoo! EU Front-End Summit, Dec 2007
WarningsLots of code
/** * Returns the namespace specified and creates it if it doesn't exist * <pre> * YAHOO.namespace("property.package"); * YAHOO.namespace("YAHOO.property.package"); * </pre> * Either of the above would create YAHOO.property, then * YAHOO.property.package * * Be careful when naming packages. Reserved words may work in some browsers * and not others. For instance, the following will fail in Safari: * <pre> * YAHOO.namespace("really.long.nested.namespace"); * </pre> * This fails because "long" is a future reserved word in ECMAScript * * @method namespace * @static * @param {String*} arguments 1-n namespaces to create * @return {Object} A reference to the last namespace object created */YAHOO.namespace = function() { var a=arguments, o=null, i, j, d; for (i=0; i<a.length; i=i+1) { d=a[i].split("."); o=YAHOO;
// YAHOO is implied, so it is ignored if it is included for (j=(d[0] == "YAHOO") ? 1 : 0; j<d.length; j=j+1) { o[d[j]]=o[d[j]] || {};
YAHOO.env.ua = function() { var o={
/** * Internet Explorer version number or 0. Example: 6 * @property ie * @type float */ ie:0,
/** * Opera version number or 0. Example: 9.2 * @property opera * @type float */ opera:0,
/** * Gecko engine revision number. Will evaluate to 1 if Gecko * is detected but the revision could not be found. Other browsers * will be 0. Example: 1.8 * <pre> * Firefox 1.0.0.4: 1.7.8 <-- Reports 1.7 * Firefox 1.5.0.9: 1.8.0.9 <-- Reports 1.8 * Firefox 2.0.0.3: 1.8.1.3 <-- Reports 1.8 * Firefox 3 alpha: 1.9a4 <-- Reports 1.9
YAHOO.env.ua = function() { var o={
/** * Internet Explorer version number or 0. Example: 6 * @property ie * @type float */ ie:0,
/** * Opera version number or 0. Example: 9.2 * @property opera * @type float */ opera:0,
/** * Gecko engine revision number. Will evaluate to 1 if Gecko * is detected but the revision could not be found. Other browsers * will be 0. Example: 1.8 * <pre> * Firefox 1.0.0.4: 1.7.8 <-- Reports 1.7 * Firefox 1.5.0.9: 1.8.0.9 <-- Reports 1.8
Warnings(probably) Norm incompatible
Warningssoz
(some) YUI Goals
• Provide solid foundations:
• Patch up DOM API
• Do the hard work for you (CSS layouts, browser testing etc.)
• Avoid repeating yourself (reusable components)
• Make it simple: consistent design, good documentation
Javascript widgets
• Common need: enhance page functionality in an unobtrusive, accessible way
• ActiveX Flash Javascript is (most often) the appropriate way to do this
• nice to make this reusable on many pages/sites
YUI widgets
• Autocomplete
• Button
• Calendar
• Color Picker
• Container
• DataTable
• Logger
• Menu
• Rich Text Editor
• Slider
• TabView
• TreeView
YUI widgets
• Autocomplete
• Button
• Calendar
• Color Picker
• Container
• DataTable
• Logger
• Menu
• Rich Text Editor
• Slider
• TabView
• TreeView
YUI Container
“The Container family of components is designed to enable developers to create different kinds of content-containing modules on the web.”
“Module and Overlay are the most basic containers, and they can be used directly or extended to build custom containers.”
YUI ContainerModule
Overlay
SimpleDialog
Dialog
Tooltip Panel
YUI ContainerModule
Overlay
SimpleDialog
Dialog
Tooltip Panel
Your Control
YAHOO.widget.Module
• Common markup structure
• Customisation through Configuration
• Custom Events
• Utility functions
Our example:Contact List with pagination
Setting things up:Basic Markup
<h2>Contacts</h2><ol> <li> <dl> <dt>Name</dt> <dd>Ed Eliot</dd> <dt>Web site</dt> <dd><a href="http://www.ejeliot.com/">ejeliot.com</a></dd> </dl> </li> <li> <dl> <dt>Name</dt> <dd>Stuart Colville</dd> <dt>Web site</dt> <dd><a href="http://muffinresearch.co.uk/">Muffin Research Labs</a></dd> </dl> </li> <li> <dl> <dt>Name</dt> <dd>Ben Ward</dd> <dt>Web site</dt> <dd><a href="http://ben-ward.co.uk/">ben-ward.co.uk</a></dd> </dl> </li></ol>
Setting things up:Structured Markup
<div id="contact-list"> <div class="hd"> <h2>Contacts</h2> </div> <div class="bd"> <ol> <li> <dl> <dt>Name</dt> <dd>Ed Eliot</dd> <dt>Web site</dt> <dd><a href="http://www.ejeliot.com/">ejeliot.com</a></dd> </dl> </li> <li> <dl> <dt>Name</dt> <dd>Stuart Colville</dd> <dt>Web site</dt> <dd><a href="http://muffinresearch.co.uk/">Muffin Research Labs</a></dd> </dl> </li> <li> <!-- ... --> </li> </ol> </div> <div class="ft"></div></div>
Setting things up:
<div id="contact-list"> <div class="hd"> <h2>Contacts</h2> </div> <div class="bd"> <ol> <li> <dl> <dt>Name</dt> <dd>Ed Eliot</dd> <dt>Web site</dt> <dd><a href="http://www.ejeliot.com/">ejeliot.com</a></dd> </dl> </li> <li> <dl> <dt>Name</dt> <dd>Stuart Colville</dd> <dt>Web site</dt> <dd><a href="http://muffinresearch.co.uk/">Muffin Research Labs</a></dd> </dl> </li> <li> <!-- ... --> </li> </ol> </div> <div class="ft"></div></div>
Structured Markup
Setting things up:
<script type="text/javascript" src="http://yui.yahooapis.com/2.4.0/build/yahoo-dom-event/yahoo-dom-event.js"></script>
dependencies• YAHOO
• YAHOO.util.Dom
• YAHOO.util.Event
• YAHOO.widget
• YAHOO.widget.Module<script type="text/javascript" src="http://yui.yahooapis.com/2.4.0/build/container/container-min.js"></script>
Setting things up:
YAHOO.Cyril.ContactList = function(el, userConfig) { YAHOO.Cyril.ContactList.superclass.constructor.call(this, el, userConfig);};YAHOO.extend(YAHOO.Cyril.ContactList, YAHOO.widget.Module);
extending Module
YAHOO.namespace(YAHOO.Cyril);
Done :)
YAHOO.util.Event.onDOMReady(function() { var contact_list = new YAHOO.Cyril.ContactList('contact-list'); YAHOO.util.Dom.batch( [ contact_list.element, contact_list.header, contact_list.body, contact_list.footer], function(el) { el.style.border = '1px solid red'; } );});
YAHOO.util.Event.onDOMReady(function() { var contact_list = new YAHOO.Cyril.ContactList('contact-list');});
Done :)
Configuration
YAHOO.Cyril.ContactList.prototype.initDefaultConfig = function () {
YAHOO.Cyril.ContactList.superclass.initDefaultConfig.call(this);
/** * Maximum number of contacts to show * @config * @type Number * @default 2 */ this.cfg.addProperty('num_contacts', { handler: this.configNumContacts, validator: this.validateNumContacts, suppressEvent: true, supercedes: false, value: 2 });}
Configuration
YAHOO.Cyril.ContactList.prototype.validateNumContacts = function(value) {
value = parseInt(value);
return !(isNan(value) || (value < 1) || (value > 3));
};
Configuration
contact_list.config.setProperty('num_contacts', 1);
alert(contact_list.getProperty('num_contacts));
Custom Events
Custom Events
Custom Events
• with other Controls
• with itself
A structured way to make your Control play well:
Custom Events/*** Initializes the custom events for YAHOO.Cyril.ContactList.* This method gets called by YAHOO.widget.Module.prototype.init* @method initEvents*/YAHOO.Cyril.ContactList.prototype.initEvents = function() {
// call the base class method to make sure inherited custom events get set up YAHOO.Cyril.ContactList.superclass.initEvents.call(this);
/** * CustomEvent fired before showing a different contact * @event beforeUpdateContactsEvent * @param {HTMLElement} contactElement LI HTMLElement for the contact to show */ this.beforeUpdateContactsEvent = new YAHOO.util.CustomEvent("beforeShowContact", this);
/** * CustomEvent fired after showing a different contact * @event updateContactsEvent * @param {HTMLElement} contactElement LI HTMLElement for the contact now displayed */ this.updateContactsEvent = new YAHOO.util.CustomEvent("showContact", this);};
Custom Events
var contactElement = get a reference to the new contact element here;if (this.beforeUpdateContactsEvent.fire(contactElement)) { // ... change the contact displayed to contactElement here}this.updateContactsEvent.fire(contactElement);
giving control to third-party code
contact_list.updateContactsEvent.subscribe(function(type, args) { alert(args[0].current_index);});
Init function
• called upon instantiation
• some “mandatory” things to do
• gets your widget up and running
Init function
Call YAHOO.widget.Module.prototype.init
YAHOO.Cyril.ContactList.superclass.init.call(this, el/*, userConfig*/);
Init function
Fire "beforeInit" and "init" events when appropriate
this.beforeInitEvent.fire(YAHOO.Cyril.ContactList);
// .. rest of the init function
this.initEvent.fire(YAHOO.Cyril.ContactList);
Note there is no need to call this.initEvents(...) to initialise Custom Events
Init function
Cache DOM references
// cache element reference
this.some_element = document.getElementById('some_element_id');
No need for the main widget’s element + header, body, and footer child elements.
Init function
Do DOM manipulations
// create/modify DOM elements (ie. previous/next links)
this.initDOMManipulations();
Init function
set up Event listeners
// initialise event delegation
this.initEventListeners();
Init function
Default behaviour
// show/hide contact elements
this.updateDisplay();
Init functionYAHOO.Cyril.ContactList.prototype.init = function(el, userConfig) { // Note that we don't pass the user config in here yet because we only want it processed once, at the lowest subclass level (by calling this.cfg.applyConfig later on) // this also calls this.initEvents YAHOO.Cyril.ContactList.superclass.init.call(this, el/*, userConfig*/); // fire event saying we are about to start the initialisation this.beforeInitEvent.fire(YAHOO.Cyril.ContactList); if (userConfig) { this.cfg.applyConfig(userConfig, true); } this.contact_elements = this.body.getElementsByTagName('li'); if (this.contact_elements.length == 0) { return; } this.current_index = 0;
// create/modify DOM elements (ie. previous/next links) this.initDOMManipulations(); // show/hide contact elements this.updateDisplay(); // initialise event delegation this.initEventListeners(); // fire event saying initialisation of the Control is done this.initEvent.fire(YAHOO.Cyril.ContactList); };
Multiple instances• store instance in an Array
• Custom Events don’t get in the way
YAHOO.util.Event.onDOMReady(function() {
// grab all contact lists by their classes and instanciate them.
var contact_lists = YAHOO.util.Dom.getElementsByClassName('contact-list');
for (var i = 0, contact_list; contact_list = contact_lists[i]; i ++) { var control = new YAHOO.Cyril.ContactList(contact_list); // store a reference to the instance YAHOO.Cyril.contactLists = [ control ] control.updateContactsEvent.subscribe(function(type, args) { alert('Current index: ' + args[0].current_index); }); }
});
Teh End
• http://www.wat.tv/playlist/689334
• http://www.jaunted.com/files/3873/French_baguette.jpg
• http://flickr.com/photos/plasticbag/971055811/
• http://flickr.com/photos/intranation/1113203816/
• http://flickr.com/photos/intranation/1870271315/
Cyril Doussin (http://cyril.doussin.name)
YUI: http://developer.yahoo.com/yui/
YUI blog: http://yuiblog.com