<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.yuiblog.com/~d/styles/itemcontent.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>Yahoo! User Interface Blog</title>
	
	<link>http://www.yuiblog.com/blog</link>
	<description>News and Articles about Designing and Developing with Yahoo! Libraries.</description>
	<lastBuildDate>Thu, 02 Sep 2010 15:16:04 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.yuiblog.com/YahooUserInterfaceBlog" /><feedburner:info uri="yahoouserinterfaceblog" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
		<title>Ask Satyam: Writing Clean, Debuggable Code</title>
		<link>http://feeds.yuiblog.com/~r/YahooUserInterfaceBlog/~3/vQThnUuCflA/</link>
		<comments>http://www.yuiblog.com/blog/2010/09/02/ask-satyam-writing-clean-code/#comments</comments>
		<pubDate>Thu, 02 Sep 2010 15:13:25 +0000</pubDate>
		<dc:creator>Satyam</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[debugging]]></category>
		<category><![CDATA[Satyam]]></category>
		<category><![CDATA[yui 2]]></category>

		<guid isPermaLink="false">http://www.yuiblog.com/blog/?p=2562</guid>
		<description><![CDATA[Satyam (a.k.a Daniel Barreiro) is a long-time YUI contributor and one of the most prolific, generous experts in the YUI forums.  He is also the author of a new book on YUI 2.8.0, YUI 2.8.0: Learning the Library. This article in the &#8220;Ask Satyam&#8221; series was suggested by JoeDev.  While its focus (like [...]]]></description>
			<content:encoded><![CDATA[<blockquote><p><a href="https://www.packtpub.com/yahoo-user-interface-yui-2-8-learning-library/book"><img align="right" src="http://yuiblog.com/assets/satyam-book-small-20100809-120823.jpg" width="106" height="132" hspace=20></a>Satyam (a.k.a Daniel Barreiro) is a long-time YUI contributor and one of the most prolific, generous experts in the <a href="http://yuilibrary.com/forum/" title="YUI Library :: Forums :: Index page">YUI forums</a>.  He is also <a href="https://www.packtpub.com/yahoo-user-interface-yui-2-8-learning-library/book">the author of a new book on YUI 2.8.0, <em>YUI 2.8.0: Learning the Library</em></a>. This article in the &#8220;Ask Satyam&#8221; series <a href="http://www.yuiblog.com/blog/2010/07/29/ask-satyam/#comment-593089" title="Ask Satyam — and Be Eligible for a Free Copy of the New YUI 2.8 Book from Packt &raquo; Yahoo! User Interface Blog (YUIBlog)">was suggested by JoeDev</a>.  While its focus (like the focus of the new book) is mostly on YUI 2, many of the practices described here are applicable to YUI 3 as well — and to frontend development in general, regardless of your library of choice.</p>
</blockquote>
<p><P>Before posting a question <a href="http://yuilibrary.com/forum/" title="YUI Library :: Forums :: Index page">in the YUI Library forums</a>, there are plenty of things you can do by yourself and, if you have your tools handy, you may find your answer all by yourself in no time. Besides, clean code is robust code, much less likely to break when subjected to stress. Good practices not only avoid fatal errors (the kind that drive you to the IRC channel or forums in search of help), but they surface warnings about minor errors and help you stay away from the fatal edge. </P> </p>
<p>In this article, I&#8217;m going to take a look at some of those best practices.  Some of these are specific to developing with <a href="http://developer.yahoo.com/yui/" title="YUI Library">YUI</a>, but the vast majority apply to frontend development regardless of your choice of Ajax library.</p>
<p><span id="more-2562"></span></p>
<p><H3>JSLint</H3> </p>
<p><P>A trip through <A HREF="http://www.jslint.com/">JSLint</A> is part of the build process for YUI. JSLint, itself fully written in JavaScript, is like a compiler but without code generation. It will, however, produce many of the useful error messages and warnings that a compiler would.  A browser&#8217;s JavaScript interpreter forgives many errors and assumes defaults you might be unaware of; JSLint forgives little, and it points you to better choices in your program. <a href="http://widgets.yahoo.com/widgets/jslint-1" title="JSLint - Yahoo! Widgets"><img src="http://yuiblog.com/assets/JSLintYahooWidget-20100827-130355.jpg" align="right" hspace="10" vspace="5" alt="Yahoo! Widget for JSLint"></a>JSLint is available in many formats; the <a href="http://yuilibrary.com/projects/builder" title="YUI Build Tool :: YUI Library">YUI Builder tool</a> uses it as a standalone command line application, but you can also integrate it into Eclipse or whatever IDE or more-or-less capable editor you use — there is even a <a href="http://widgets.yahoo.com/widgets/jslint-1" title="JSLint - Yahoo! Widgets">Yahoo! Widget for JSLint</a>. </P> </p>
<p><P>If the zillion lines of code in the YUI library can go through JSLint with no errors and few warnings (it can&#8217;t avoid a few things), so should your code. I find JSLint very helpful when I&#8217;m tired. Bean counting in the late evening is a waste of time; you are likely to miss the most obvious errors. JSLint doesn&#8217;t care what time of the day it is and will point you straight to that mismatched curly bracket or misspelled variable that&#8217;s at the root of the problem.</P> </p>
<p><P>JSLint is most helpful if you keep your code clean from the start. If you&#8217;ve never used JSLint before and try it out on an application that is already hundreds of lines long, it will flood you with so many warnings that it will feel that it hampers your job more than it helps. However, that only happens the first few times. Once you learn to stay away from bad coding practices, JSLint diagnostics will be few and straight to the point; your real error, the one that&#8217;s driving you nuts, will clearly stand out. In the meantime, JSLint will teach you to make your code safe and robust.</P> </p>
<p><P>So, don&#8217;t wait for a tough error to show up. If you have never used JSLint, try it with what you believe to be good code, it might not be as good as you assumed. As <a href="http://yuiblog.com/crockford/" title="Crockford on JavaScript: A Public Lecture Series at Yahoo!">Douglas Crockford</a> (author of JSLint) says, &#8220;JSLint will hurt your feelings.&#8221; But that&#8217;s a small price to pay for better code.</P> </p>
<p><H3>Global and unused variables</H3> </p>
<p><P>One of the things JSLint will warn you about is the use of global variables. There should be no global variables except those you know about, such as the ones created automatically like <CODE>window</CODE> or <CODE>document</CODE> and those for the libraries you are using such as <CODE>YAHOO</CODE> (in <a href="http://developer.yahoo.com/yui/2/" title="YUI 2 &mdash; Yahoo! User Interface Library">YUI 2</a>) or  <CODE>YUI</CODE> (in <a href="http://developer.yahoo.com/yui/3/" title="YUI 3 &mdash; Yahoo! User Interface Library">YUI 3</a>).  You may also want to create a single global for your own namespace.</P> </p>
<p><P><a href="http://yuiblog.com/blog/2006/06/01/global-domination/" title="Global Domination &raquo; Yahoo! User Interface Blog (YUIBlog)">Global variables are dangerous</a>, and there are usually more of them than you&#8217;d expect; moreover, &#8220;extras&#8221; such as mashups and banners might add more of them. Since the <CODE>window</CODE> HTML DOM namespace can be omitted from compound names, <CODE>window.name</CODE>, <CODE>window.length</CODE> and <CODE>window.location</CODE> become the global variables <CODE>name</CODE>, <CODE>length</CODE> and <CODE>location</CODE>. Have you never used such variable names in your code? I don&#8217;t mean in their HTML DOM sense but as everyday field names in a table — like the <em>name</em> of a person, the <em>length</em> of an object or the <em>location</em> of a book on the shelf. What if you use those variables without declaring them? You might assume that <CODE>length</CODE> is initially <CODE>undefined</CODE> but, in fact, if you don&#8217;t declare your own local copy of it (and initialize it), <code>length</code> will be a reference to <CODE>window.length</CODE>. And if you assign something to <CODE>location</CODE>, you might accidentally cause your user to navigate away from the current page. Here, I am just giving examples of possible collisions with the browser&#8217;s built-in variables, but if you start adding libraries from other sources, the chance for collisions increases. The JavaScript syntax highlighter used in YUI&#8217;s examples uses the global variable <CODE>dp</CODE> as its root and the number of global variables any Google script might add when you insert a map or other tool in your page is impolite, to say the least.</P> </p>
<p><P>It&#8217;s not merely that you want to step lightly with respect to other people&#8217;s variables — by staying out of the global namespace, you reduce the risk that they will step on yours (or that you&#8217;ll step on your own). With asynchronous threads weaving themselves around each other, as is often the case in modern JavaScript applications, you can&#8217;t really be sure the order in which your various pieces of code will execute and what global variable will be left at what value by whom.  The only sane approach is to avoid them whenever possible.</P> </p>
<p><P>Global variables might also point to a typo. If you have a global variable you didn&#8217;t mean to have and you have an unused variable with a similar name, you might have misspelled one of them: that is, you may have declared it with one name and used it with another different spelling. It can also mean that you have declared it after you have used it, which means that at execution time you might find your variable not initialized as you would have expected.</P> </p>
<p><H3>Using <CODE>this</CODE></H3> </p>
<p><P>Using <CODE>this</CODE> is often troublesome for beginners because it&#8217;s easy to lose track of where we are in the scope chain. Until we get some practice, keeping track of scope can be an issue. Also, developing a coding style and a structure for the application helps greatly; after all, it&#8217;s all about knowing where you place things. If you learn to organize your code consistently and store state in logical places (<CODE>this</CODE> points to one such place), information in your program will be easy find. Any debugger will show you what <CODE>this</CODE> points to at each step. It&#8217;s always a good thing to check first if <CODE>this</CODE> is referencing the object you expect. A lot of times when we have a bug, it&#8217;s be because <CODE>this</CODE> is pointing to <CODE>window</CODE>. There are several situations that can produce this result.</P> </p>
<p><P>If we have a method with an inner function, when we call that inner function it won&#8217;t get the scope of the caller but rather that of <CODE>window</CODE>. This example shows that the inner function doesn&#8217;t share the scope of the method within which it is contained:</P> </p>
<p><PRE>var someObject = {
    outerFunction: function () {
        console.log(&#8216;outer&#8217;,this); // prints outer Object {}
        var innerFunction = function () {
            console.log(&#8216;inner&#8217;,this);<FONT COLOR="#0000ff"><B>// prints inner Window</B></FONT>
        };
        innerFunction();
    }
};
someObject.outerFunction();</pre> </p>
<p><P> There are a couple of ways to fix this. In this example, we ask JavaScript to correct the scope using <code>call()</code>:</P> </p>
<p><PRE>var someObject = {
    outerFunction: function () {
        console.log(&#8216;outer&#8217;,this); // prints outer Object {}
        var innerFunction = function () {
            console.log(&#8216;inner&#8217;,this); // prints inner Object {}
        };
        innerFunction<FONT COLOR="#008000"><B>.call(this)</B></FONT>;
    }
};
someObject.outerFunction();</pre> </p>
<p><P> In the next example, we take advantage of the ability for inner functions to share the variables of containing functions. We create a variable <CODE>self</CODE> which we initialize to the value of <CODE>this</CODE> in the outer function. We can then use <code>self</code> in the inner function to refer to the outer function:</P> </p>
<p><PRE>var someObject = {
    outerFunction: function () {
        console.log(&#8216;outer&#8217;,this); // prints outer Object {}
        <FONT COLOR="#008000"><B>var self = this;</B></FONT>
        var innerFunction = function () {
            console.log(&#8216;inner&#8217;,<FONT COLOR="#008000"><B>self</B></FONT>); // prints inner Object {}
        };
        innerFunction();
    }
};
someObject.outerFunction();   </pre> </p>
<p><P> Finally, with events, the scope of the listener is that of the element to which the listener is attached:</P> </p>
<p><PRE>var someObject = {
    outerFunction: function () {
        console.log(&#8216;outer&#8217;,this); // prints outer Object {}
        YAHOO.util.Event.on(&#8216;button&#8217;,'click&#8217;,function () {
            console.log(&#8216;inner&#8217;,this); <FONT COLOR="#0000ff"><B>// prints inner &lt;button id=&quot;button&quot;&gt;</B></FONT>
        });
    }
};
someObject.outerFunction();</pre> </p>
<p><P> Unless we adjust the scope of the listener when setting it up </P> </p>
<p><PRE>var someObject = {
    outerFunction: function () {
        console.log(&#8216;outer&#8217;,this); // prints outer Object {}
        YAHOO.util.Event.on(&#8216;button&#8217;,'click&#8217;,function () {
            console.log(&#8216;inner&#8217;,this); // prints <FONT COLOR="#008000"><B>inner Object {}</B></FONT>
        }<FONT COLOR="#008000"><B>,this,true</B></FONT>);
    }
};
someObject.outerFunction();</pre> </p>
<p><P> This also applies to YUI components. If we listen to a click event on a DataTable cell, a TreeView node or a MenuItem, the scope of the listeners will be those of the YUI component instance that owns the event — unless it is explicitly set otherwise.</P> </p>
<p><H3>Sandboxes for applications</H3> </p>
<p><P>Another good way to keep your code clean is to start with a clean skeleton. The style of coding JavaScript applications has changed over time. Nowadays, most developers use two different styles: one for applications and one for library components. Most YUI 2 examples reflect the old style, where we used <CODE>YAHOO.namespace</CODE> to create a namespace for our own code.</P> </p>
<p><P>Nowadays, for our applications, we mostly use a single sandbox declared within the scope of an anonymous function that executes <code>onDOMReady</code>:</P> </p>
<p><PRE>YAHOO.util.Event.onDOMReady(function() {
    var Dom = YAHOO.util.Dom,
        Event = YAHOO.util.Event,
        Lang = YAHOO.lang;

    var yourVariable = initialValue,
        moreOfTheSame = otherInitialValue;

    var myFunction1 = function ( …) {
        // body of function;
    };
    var myFunction2 = function (… ) {
        // body of function;
    };
    &#8230;
});</pre> </p>
<p><P> This technique has several benefits. </P> </p>
<p><OL> <LI>We check for the readiness of the DOM right from the start, ensuring that all the pieces of HTML that we might want to manipulate are present and safe to use. </li>
<li>The function provided to <CODE>onDOMReady</CODE> is not wasted at all; it&#8217;s the container of the sandbox and, because it&#8217;s anonymous, it does not pollute the global namespace. </li>
<li>We immediately start defining our variables, including aliases or short names for our most often-used objects. This has several other advantages of its own: </P> <OL> <LI>We save some typing </li>
<p> <LI>The YUI Compressor can compress our short names, whereas it cannot compress a global name like <CODE>YAHOO</CODE> or its properties such as <CODE>util</CODE> or <CODE>Menu</CODE> no matter how deep they might be. If they are anchored in a global name such as <CODE>YAHOO</CODE>, the whole branch is untouchable. Thus our already short names <CODE>Dom</CODE>, <CODE>Event</CODE> and <CODE>Lang</CODE> might be further reduced to <CODE>A</CODE>, <CODE>B</CODE> and <CODE>C</CODE> when Compressor is run at build time. </li>
<p> <LI>The interpreter does not need to resolve over and over again the long names. Each dot in a name such as <CODE>YAHOO.util.Event.onDOMReady</CODE> is time consumed in a symbol table look up. </li>
<p> <LI>All variables will be available anywhere inside this anonymous function, even to functions defined within, unless a variable of the same name was defined in them. Basically, it is as if a sub-global environment has been defined within, and all variables there will be available anywhere just by name. </li>
<p> <LI>For the variables in the sandbox we don&#8217;t need to use <CODE><B>this</B></CODE>, which gives us such headaches when using traditional OOP technique of making even the main function an object. </li>
<p> </OL> <LI><P>We define our functions. We can do this with the <CODE>var</CODE> statement, as I did above or the <CODE>function</CODE> statement; there is a subtle difference but it is mostly irrelevant. I use the <CODE>var</CODE> statement to highlight that they are just as accessible as the other variables: we can access them anywhere in the sandbox. </P> </OL> </p>
<p><P>Of course this relies on the ability of JavaScript to allow us to define functions within functions and on the fact that the inner functions have access to all the variables defined in the outer function. Basically, each function you define creates a new local environment accessible to any further functions within.</P> </p>
<p><P>Sandboxing is the standard way of doing things in YUI 3:</P> </p>
<p><PRE>YUI().use(&#8216;module1&#8242;, … , function (Y) {
    // This is the sandbox
});</pre> </p>
<p><H3> Namespaces for libraries</H3> </p>
<p><P>While the sandboxing technique is great for final applications, it&#8217;s not good for libraries. What happens in the sandbox stays in the sandbox, completely invisible to the outside world. However, when you define a library utility or component of your own, you want to re-use it, so it needs to be globally accessible (which is not the same as being purely global). Anything you define under <CODE>YAHOO.example</CODE> — e.g., <CODE>YAHOO.example.myUtility</CODE> — is globally accessible. You can access it by its full name once it has been defined. <CODE>myUtility</CODE>, as a member of the global <code>YAHOO</CODE>, is not global but it is globally accessible. </P> </p>
<p><P>When we build libraries, we usually use the sandbox for our code and namespacing for sharing, like this:</P> </p>
<p><PRE>(function () {
    var MyClass = function () {
        // this would be the constructor
    };
    MyClass.prototype = {
        // properties and methods
    };
    YAHOO.namespace('MyLibrary');
    YAHOO.MyLibrary.MyClass = MyClass
})();</pre> </p>
<p>We create a sandbox by defining everything within an anonymous function, which we immediately execute (see all those parenthesis there?, they mean 'take the result of defining this function and execute it'). Here, we don't wait for the DOM to be ready; libraries seldom do, since the application that uses them is responsible to check that everything is in place. Within the sandbox, we have the same advantages as with the previous sandbox. We can define short names for anything we use often, even for the class we are defining: none of them will be visible outside. Then, to make it globally accessible, we create a namespace of our own and place our recently created class in it. </P></p>
<p><H3>YUI Logger</H3> </p>
<p><P>Let's say we have our code nice and clean, with no JSLint errors or warnings, but we still have problems to debug. YUI can be helpful in telling us what's wrong. For production code, we will usually load the minified versions of the YUI components, but there are also two other versions. The <TT>-debug.js</TT> version is the one that can help us uncover problems. For example, we might be using Dom Collection's method <CODE>setStyle</CODE> to, lets say, change the color of a block of text. The change doesn't happen and we can't find what's wrong. The file <TT>dom-debug.js</TT> has this helpful line, which is deleted in the <CODE>-min</CODE> version:</P></p>
<p><PRE>YAHOO.log('element ' + el + ' is undefined', 'error', 'Dom');</pre></p>
<p><P>This is executed when <CODE>setStyle</CODE> tries to locate the element to be styled and does not find it. The error message will probably show a misspelled element id or some such error that is so hard to pick after a long day of staring at the same code. </P></p>
<p><P>It's easy to get the logger up and running; you just need to <a href="http://developer.yahoo.com/yui/articles/hosting/?logger&amp;MIN&amp;loadOptional&amp;norollup" title="YUI 2: Dependency Configurator">include its files</a>:</P></p>
<p><PRE>&lt;link type=&quot;text/css&quot; rel=&quot;stylesheet&quot; href=&quot;http://yui.yahooapis.com/2.8.1/build/logger/assets/skins/sam/logger.css&quot;&gt;
&lt;script src=&quot;http://yui.yahooapis.com/2.8.1/build/yahoo-dom-event/yahoo-dom-event.js&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;http://yui.yahooapis.com/2.8.1/build/dragdrop/dragdrop-min.js&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;http://yui.yahooapis.com/2.8.1/build/logger/logger-min.js&quot;&gt;&lt;/script&gt;</pre></p>
<p><P> The basic <CODE>yahoo-dom-event</CODE> aggregate is most likely to be there already while the Drag &amp; Drop optional dependency is  handy to move the logger window out of the way. Then,  just add:</P> </p>
<p><PRE>var myLogReader = new YAHOO.widget.LogReader();</pre></p>
<p><P>Then, it's a matter of loading the <CODE>-debug</CODE> versions of the component files and all the messages they produce will be shown. The amount of information can be overwhelming so, it is wise not to load all <CODE>-debug</CODE> versions at once. The Logger can also filter the information it displays and you may uncheck the Info category of messages to concentrate on Warn and Fail. The filters do not affect what is logged, only what is shown; Logger logs all messages, regardless of whether the LogReader displays them or not. There is only one message queue per application and logging will start as soon as the Logger component file is loaded, it doesn't matter whether it is shown or not. </P></p>
<p>Another power-user strategy for YUI debugging with the Logger Control is to take advantage of Logger's ability to leverage the browser's console (in supported browsers):</p>
<pre>YAHOO.widget.Logger.enableBrowserConsole();</pre>
<p>With this line of code, you can pipe all Logger messages to the browser console, which is a natural part of your workflow in development in any case.</p>
<p><P>The only tedious thing about the Logger is remembering to change the component files from the minified, aggregates and combos to the <CODE>-debug</CODE> versions. In this, as always, the <A HREF="http://developer.yahoo.com/yui/articles/hosting/">Dependency Configurator</A> is a great help; click on the <CODE>-debug</CODE> button on the top left and it will produce the correct list of files.</p>
<p>And, now that I've mentioned it:</P></p>
<p><H3>Check your dependencies</H3> </p>
<p><P>It's a good idea when you start with YUI to pick an example that closely resembles what you want to do and modify it. As you add more features, you take bits and pieces from other examples and incorporate them into your developing application. One frequent source of problems are the dependencies. Many people paste the dependency files of each and every example into their application, duplicating some of them over and over again. Missing, duplicate or out of order dependencies might produce unexpected errors. Not having the YAHOO Global Object loaded before anything else is most certainly going to be fatal. At any rate, loading something twice will certainly increase the time it takes your page to load. </P></p>
<p><P>Finally, if you are using your own servers to load the YUI library, you might have the base path for your copy of the library wrong. If you have Firebug, go to the Net tab and check that there are no lines in red. Those would mean that a requested resource could not be found.</P></p>
<p><H3>Debugging</H3> </p>
<p><P>The Firebug add-on for Firefox is still the best debugger around, plus it is free. By default, when the debugger is activated the "Break on all errors" option is on, which means that Firebug will stop at the point where it finds an error and show the error message and source code. Some of those errors will be the same that JSLint would have diagnosed, so JSLint is still the first place to start — especially because Firebug can only signal one error on each run while JSLint can signal them all at once. Some errors will only show up at runtime, so the debugger is the only option. Whenever you get to such a break, the first thing to check is <CODE>this</CODE>, a four-letter word of the trickiest sort.</P></p>
<p><P>When using a sandbox, debuggers will usually show local variables and arguments, but they will not normally offer to show the variables in containing functions, such as those declared in the sandbox and used from within the inner functions. The debugger will be able to show their value if you explicitly ask for them by name, but it will not show them automatically. Another alternative is to make them globally accessible. For example, since <CODE>YAHOO.example</CODE> is always available (when YUI 2 is on the page), if you want to keep an eye on a certain component at all times you can simply copy it there:</P></p>
<p><PRE>var myTree = new YAHOO.widget.TreeView('tree-container');
YAHOO.example.myTree = myTree;</pre> </p>
<p>We add the last assignment while debugging so we can keep an eye on the TreeView instance while debugging since <CODE>myTree</CODE> would normally be within the sandbox and invisible elsewhere.</P></p>
<p><H3>Don't use members starting with underscore</H3> </p>
<p><P>Many people use the debugger to look for the names of variables or properties holding the information they need. Sometimes that information is stored in properties starting with an underscore, like <CODE>_nodes</CODE>. By convention, those are <em>private</em> properties, not for general use. JavaScript enforces no true concept of private or protected members; thus the leading underscore is the conventional way to signal the intent of the class developer to keep that property private. These properties can present a temptation (after all, you can see them there in the debugger), but there are a couple of good reasons why it is not wise to use them in your programs. </P></p>
<p><P>First, only public interfaces are supported. Since you are not supposed to use a property starting with an underscore, the class developer is free to change it at any time. The developer should have provided some public mechanism to access this same value; for example, a <CODE>_nodes</CODE> private property might have a <CODE>getNodes()</CODE> method or a <CODE>nodes</CODE> configuration attribute. Either of these would be the public interface to that same value. The second reason not to use properties with leading underscores is that their accessors might need to produce a secondary effect which accessing the private property would completely bypass. The component would then be left in an inconsistent, fragile state.</P></p>
<p><P>So, if you are using the debugger to look for a value and you find it in a property starting with a leading underscore, don't use it. Go instead to the API docs and look in the Methods index for some <CODE>getXxxx()</CODE> method with a similar name or in the Configuration Attributes index. Avoid using private variables.</P></p>
<p><H3>Stack Traces</H3> </p>
<p><P>The YUI library is robust and reliable. If the debugger breaks within the YUI library, it's more likely that it is because of an error induced by your code that by some failure in YUI itself. JavaScript was designed to carry on, always coping with errors as best as possible. The YUI library does likewise. The <CODE>-debug </CODE>versions will have extra checks and will emit diagnostic messages, but they are all stripped out in the minified versions. If you see an error in a YUI file it is likely that it got there out of pure stubbornness, but don't try to find the error there. When the debugger stops it is not because it has found the cause of the error but because the JavaScript interpreter can no longer carry on. </P></p>
<p><P>Breaks in minified files are not helpful, if you get a message such as...</P></p>
<p><PRE>F is not an object in line 7 of dom-min.js</pre></p>
<p><P>...it is pointless to ask in the forum about it. First of all, <CODE>F</CODE> is an alias generated by YUI Compressor to replace a longer, descriptive variable name in the original uncompressed file; secondly, the first few lines of a component file are usually taken by the copyright notice, which the YUI Compressor always respects. And since the YUI Compressor also deletes all new-line characters, as it does with other white space, all the executable code in <CODE>dom-min.js</CODE> is in lines 7, 8 and 9 — so the line number doesn't say much either.</P><br />
<P>That is when the Stack Trace tab in the right sub-panel of the Script tab of Firebug (or wherever you can find the stack trace in your debugger) comes handy. It tells you how you got there. Here is a screen capture from Firebug:</P></p>
<p><P><IMG SRC="http://yuiblog.com/assets/asksatyam3-stack-20100902-080216.jpg" alt="Firebug's Script tab." ></p>
<p>The item on top of the right panel, <CODE>createEvent()</CODE>, is the name of the function where the current statement is; the one below is the place where <CODE>createEvent()</CODE> was called; the one below that the function that called the previous one; and so on. Most debuggers will let you select any of those trace points and see how you got to where you are. Firebug also lets you check the value of the variables at each of the trace points. We can see in the left subpanel the yellow arrow pointing to <CODE>createEvent()</CODE>. We got lucky this time, as it will always point to the line containing the offending code, but that code might be anywhere within that line; fortunately, <CODE>createEvent()</CODE>happens to be at the beginning of the line. See the variables and arguments? <CODE>B</CODE>, <CODE>G</CODE>, <CODE>L</CODE>...there is no guessing what the original names could have been, as this is the YUI Compressor at work. However, note that <CODE>YAHOO</CODE> is untouched and the copyright notices are preserved.</P></p>
<p><P>You might not recognize many of the names in the stack trace, as they might be functions within functions within the YUI library itself. Eventually, you might see function names you recognize. In this image, I know <CODE>showAlbums</CODE>, which calls the constructor for the <CODE>REDT</CODE> class (which is code I wrote). I don't have a clue about anything else. That's where I will go looking...to the ones I know.</P></p>
<p><P>If you are not sure, you can go through the full list. If you see compressed code (assuming you have not compressed your own yet), simply ignore it, or switch to the non-minified version of the YUI source files. But focus primarily on those names you recognize that belong to your own code. Note that Firebug uses <CODE>(?)()</CODE> for traces it cannot name. Usually, your code in the sandbox will be signaled that way since the sandbox is contained in an anonymous function. At any such point in your trace, check the values of the variables you provided as arguments; some of them might not be what you assumed. Check them against the API docs to see if it is a valid argument value. </P></p>
<p><P>If the argument and variable values you see are inconclusive, at least you can place a breakpoint right before your code calls the YUI component. You might then be able to see how you got into the mess.</P> <P>Stack traces are often ignored by developers but with JavaScript's tendency to keep digging itself into an ever deeper hole as it tries to carry on, it is good to be able to come up to the surface to look around. As with all debugging techniques, it might not always work, but it is nice to know it is there. Let's be honest, if you have read this far, you know what desperation is.</P></p>
<p><H3>Asynchronous calls</H3> </p>
<p><P>A lot of the interactivity that characterizes Web2.0 apps is based on the ability to handle asynchronous events. In contrast with the original style of web applications, where every interaction involved waiting for a new page to be delivered from the server, the modern interactive applications involve setting things up and listening for any of several possible events to happen...and then responding to those events. We set listeners for clicks or keystrokes and or other programmatic events; these are usually intuitive. Asynchronous calls and their callbacks, however, often are harder to understand. </P></p>
<p><P>The most common error I see in implementations on the forums is to place code right after an asynchronous call such as Connection Manager's <CODE>asyncRequest()</CODE>or DataSource's <CODE>sendRequest()</CODE> methods, assuming that such code will be executed when the operation is completed. When you call such a function, you are just priming the operation, not executing it in full. No data will be available after the call to the async method. Only the request for such data would have been produced so far. The server must receive it and process it, and the reply (if it ever comes) is still off in the future. That is why for such asynchronous operations we use callback functions. </P></p>
<p><P>A callback is like the function we assign as a listener for an event such as a click on a button in a form; when the user clicks, the listener gets called. The callback on a asynchronous event is very much like this; just as we send a form for a user to fill in, we send a request message to the server, and just as we wait for the filled-in form to be submitted back to our program, we wait for the reply sent by the server to come back to us. We cannot know when the user will submit the form or the server send the reply; that's why we set what for a user action we call an event listener and for an asynchronous event we call a callback function. It might seem the server reply is fast if not instantaneous, but it means ages in CPU time.</P></p>
<p><H3>Debugging asynchronous calls</H3> </p>
<p><P>Sometimes there is no alternative but to single-step through the code. Be careful when you get to asynchronous calls such as Connection Manager's <CODE>asyncRequest()</CODE> method, DataSource's <CODE>sendRequest()</CODE> or DOM's <CODE>window.setTimeout()</CODE>. These start an asynchronous request for data against the server or delay some action a given time and their callbacks get called when the data has been received or the time has elapsed. There are two issues to consider; first, the debugger won't step into the callback automatically. If you want to catch up with the reply, you have to put a breakpoint inside the callback function. Lots of people reach the point of calling the asynchronous method and expect the single-stepping to resume within the callback when the async operation is finished. This will not happen; the thread of execution does not flow automatically into the callback, and the debugger cannot be expected to figure that one out.</P></p>
<p><P>Second, when you reach the line with the asynchronous call, put the breakpoint in the callback and then let the application run. Usually, whatever goes after the async call is not really important; in fact, the async call is normally at the end of a function, since there isn't much to do until the reply arrives. Don't single-step over the async call, because the debugger will keep the JavaScript interpreter on hold and, when the reply arrives or the time lapses, it will be missed since the interpreter was frozen and unable to handle it. So, if you were clicking on Step Over, when you get to the async call, make sure you have the breakpoint in the callback and then click  Run so the JavaScript interpreter is active and able to handle the reply.</P></p>
<p><P>For repetitive events such as callbacks to <CODE>window.setInterval()</CODE> or other time-critical operations, it is better not to put breakpoints in them. While you are on hold, many events will be missed. It is usually better to use <CODE>console.log</CODE> or the YUI Logger to simply signal that the event is happening and show a few critical values. Don't use <CODE>window.alert()</CODE> for the same reason; it puts a hold on the browser and the events you care about will be missed.</P></p>
<p><H3>Conclusion</H3> </p>
<p><P>There are many tools to help us find out what is happening in our programs. With the proper tools at hand, we can find an error in less time than what it takes us to write a question in the forums. The first step, however, is to write good and reliable code and make JSLint an integral part of your development process.</P></p>
<div class="feedflare">
<a href="http://feeds.yuiblog.com/~ff/YahooUserInterfaceBlog?a=vQThnUuCflA:JIsU6U2IIN8:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/YahooUserInterfaceBlog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.yuiblog.com/~ff/YahooUserInterfaceBlog?a=vQThnUuCflA:JIsU6U2IIN8:dnMXMwOfBR0"><img src="http://feeds.feedburner.com/~ff/YahooUserInterfaceBlog?d=dnMXMwOfBR0" border="0"></img></a> <a href="http://feeds.yuiblog.com/~ff/YahooUserInterfaceBlog?a=vQThnUuCflA:JIsU6U2IIN8:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/YahooUserInterfaceBlog?i=vQThnUuCflA:JIsU6U2IIN8:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.yuiblog.com/~ff/YahooUserInterfaceBlog?a=vQThnUuCflA:JIsU6U2IIN8:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/YahooUserInterfaceBlog?i=vQThnUuCflA:JIsU6U2IIN8:F7zBnMyn0Lo" border="0"></img></a>
</div>]]></content:encoded>
			<wfw:commentRss>http://www.yuiblog.com/blog/2010/09/02/ask-satyam-writing-clean-code/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.yuiblog.com/blog/2010/09/02/ask-satyam-writing-clean-code/</feedburner:origLink></item>
		<item>
		<title>YUI Theater —  Douglas Crockford: “Crockford on JavaScript — Scene 6: Loopage”  (52 min.)</title>
		<link>http://feeds.yuiblog.com/~r/YahooUserInterfaceBlog/~3/zWLts5KIbd4/</link>
		<comments>http://www.yuiblog.com/blog/2010/08/30/yui-theater-douglas-crockford-crockford-on-javascript-scene-6-loopage-52-min/#comments</comments>
		<pubDate>Mon, 30 Aug 2010 23:47:40 +0000</pubDate>
		<dc:creator>Eric Miraglia</dc:creator>
				<category><![CDATA[YUI Theater]]></category>
		<category><![CDATA[Douglas Crockford]]></category>
		<category><![CDATA[yahoo]]></category>

		<guid isPermaLink="false">http://www.yuiblog.com/blog/?p=2556</guid>
		<description><![CDATA[
Douglas Crockford&#8217;s latest installment in the &#8220;Crockford on JavaScript&#8221; series, a talk in which he covers the role of event loops and the importance of server-side JavaScript, is now available on video.  Flash video is embedded below, or you can download the HD video (480p ~370MB).  Video from the first five lectures is [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://bit.ly/duCFAh"><img src="http://yuiblog.com/assets/crockford-loopage-20100830-104930.jpg" alt="Douglas Crockford speaking at Yahoo! on August 27, 2010, as part of his Crockford on JavaScript lecture series." width="510"></a></p>
<p>Douglas Crockford&#8217;s latest installment in the &#8220;<a href="http://yuiblog.com/crockford/">Crockford on JavaScript</a>&#8221; series, a talk in which he covers the role of event loops and the importance of server-side JavaScript, is now available on video.  Flash video is embedded below, or you can <a href='http://bit.ly/duCFAh'>download the HD video (480p ~370MB)</a>.  Video from the first five lectures is available on the <a href="http://yuiblog.com/crockford/">Crockford on JavaScript page</a>.</p>
<p>
<div><object width="510" height="287"><param name="movie" value="http://d.yimg.com/m/up/ypp/default/player.swf"></param><param name="flashVars" value="vid=21663596&#038;"></param><param name="allowfullscreen" value="true"></param><param name="wmode" value="transparent"></param><embed width="510" height="287" allowFullScreen="true" src="http://d.yimg.com/m/up/ypp/default/player.swf" type="application/x-shockwave-flash" flashvars="vid=21663596&#038;"></embed></object></div>
</p>
<ul>
<li><a href='http://bit.ly/duCFAh'>Download HD video (480p ~370MB)</a></li>
<li><a href="http://yuiblog.com/yuitheater/crockford-loopage.m4v">Download video (m4v)</a></li>
<li><a href="http://yuiblog.com/assets/crockford-loopage-slides.pdf">Download slides</a></li>
</ul>
<h3>Other Recent YUI Theater Videos:</h3>
<ul>
<li><a href='http://developer.yahoo.com/yui/theater/video.php?v=zakas-tsaran'><strong>Nicholas Zakas and Victor Tsaran:</strong> Accessibility on the Yahoo Homepage</a> &mdash; Nicholas Zakas, a principal developer of the Yahoo! homepage, and Victor Tsaran&#8217;s, Yahoo!&#8217;s senior accessibility manager, discuss the strategies and methods that made one of the most visited websites in the world fully accessible. The talk took place at the June 2010 BayJax meetup at Yahoo.</li>
<li><a href='http://developer.yahoo.com/yui/theater/video.php?v=lembree-a11y'><strong>Dennis Lembree:</strong> Making JavaScript Accessible</a> &mdash; Dennis Lembree, an accessibility expert and the creator of AccessibleTwitter discusses the challenges of making JS-enabled sites accessible. The talk took place at the June 2010 BayJax meetup at Yahoo.</li>
<li><a href='http://developer.yahoo.com/yui/theater/video.php?v=dahl-node'><strong>Ryan Dahl:</strong> Introduction to NodeJS</a> &mdash; Ryan Dahl, the creator of NodeJS, introduces the project and talks about performance improvements and new architecture. The talk took place at the May 2010 BayJax meetup at Yahoo.</li>
<li><a href='http://developer.yahoo.com/yui/theater/video.php?v=insua-dom'><strong>Elijah Insua:</strong> jsdom: a CommonJS Implementation of the DOM</a> &mdash; Elijah Insua introduces a server-side implementation of the JavaScript DOM at the May 2010 BayJax meetup at Yahoo.</li>
<li><a href='http://developer.yahoo.com/yui/theater/video.php?v=zakas-hpjs'><strong>Nicholas Zakas, Stoyan Stefanov, Ross Harmes, Julien Lecomte, Matt Sweeney:</strong> High Performance JavaScript</a> &mdash; Five contributors to O&#8217;Reilly&#8217;s <em>High Performance JavaScript</em> discuss advanced JavaScript and DOM scripting optimizations at the April 2010 BayJax meetup at Yahoo.</li>
</ul>
<h3>Subscribing to YUI Theater:</h3>
<ul>
<li><a href="http://feeds.feedburner.com/yuiblog/yui-theater">YUI Theater RSS feed</a></li>
<li><a href="http://phobos.apple.com/WebObjects/MZStore.woa/wa/viewPodcast?id=263846173&amp;s=143441">YUI Theater on iTunes</a></li>
</ul>
<div class="feedflare">
<a href="http://feeds.yuiblog.com/~ff/YahooUserInterfaceBlog?a=zWLts5KIbd4:G3M_PBTvaH4:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/YahooUserInterfaceBlog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.yuiblog.com/~ff/YahooUserInterfaceBlog?a=zWLts5KIbd4:G3M_PBTvaH4:dnMXMwOfBR0"><img src="http://feeds.feedburner.com/~ff/YahooUserInterfaceBlog?d=dnMXMwOfBR0" border="0"></img></a> <a href="http://feeds.yuiblog.com/~ff/YahooUserInterfaceBlog?a=zWLts5KIbd4:G3M_PBTvaH4:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/YahooUserInterfaceBlog?i=zWLts5KIbd4:G3M_PBTvaH4:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.yuiblog.com/~ff/YahooUserInterfaceBlog?a=zWLts5KIbd4:G3M_PBTvaH4:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/YahooUserInterfaceBlog?i=zWLts5KIbd4:G3M_PBTvaH4:F7zBnMyn0Lo" border="0"></img></a>
</div>]]></content:encoded>
			<wfw:commentRss>http://www.yuiblog.com/blog/2010/08/30/yui-theater-douglas-crockford-crockford-on-javascript-scene-6-loopage-52-min/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		<feedburner:origLink>http://www.yuiblog.com/blog/2010/08/30/yui-theater-douglas-crockford-crockford-on-javascript-scene-6-loopage-52-min/</feedburner:origLink></item>
		<item>
		<title>Introducing Yeti: The YUI Easy Testing Interface</title>
		<link>http://feeds.yuiblog.com/~r/YahooUserInterfaceBlog/~3/PLqo_CKr5R0/</link>
		<comments>http://www.yuiblog.com/blog/2010/08/25/introducing-yeti-the-yui-easy-testing-interface/#comments</comments>
		<pubDate>Wed, 25 Aug 2010 23:16:54 +0000</pubDate>
		<dc:creator>Reid Burke</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[NodeJS]]></category>
		<category><![CDATA[testing]]></category>
		<category><![CDATA[unit testing]]></category>
		<category><![CDATA[yeti]]></category>
		<category><![CDATA[yui test]]></category>

		<guid isPermaLink="false">http://www.yuiblog.com/blog/?p=2516</guid>
		<description><![CDATA[
Testing JavaScript is an important but often overlooked part of web development. One reason is because developing for the web means targeting more than one browser. YUI currently classifies 11 different environments that enjoy our highest support level. In addition, we also test YUI on emerging X-grade environments like mobile devices. When you have so [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://yuilibrary.com/projects/yeti/"><img src="http://yuiblog.com/assets/yeti/splash.jpg" alt="Welcome to Yeti" title="" /></a></p>
<p>Testing JavaScript is an important but often overlooked part of web development. One reason is because developing for the web means targeting more than one browser. YUI currently classifies <a href="http://developer.yahoo.com/yui/articles/gbs/">11 different environments</a> that enjoy our highest support level. In addition, we also test YUI on emerging X-grade environments like mobile devices. When you have so many different environments to support, it&#8217;s tempting to just pick a couple important ones to develop with locally and hope for the best.</p>
<p>At YUI, we use <a href="http://seleniumhq.org/">Selenium</a> and <a href="http://hudson-ci.org/">Hudson</a> for running <a href="http://developer.yahoo.com/yui/3/test/">YUI Test</a>-based unit tests on various browser and operating system configurations as part of our continuous integration strategy. This is great for catching problems that result from integrating your work with the rest of a complex software stack. It comes with a price: CI tools like these are complicated to setup and maintain. In any case, they don&#8217;t help you while you&#8217;re developing code and testing <em>before</em> you commit.</p>
<p>Today, I&#8217;m excited to release <a href="http://yuilibrary.com/projects/yeti/">Yeti</a> 0.1.0, an experimental command-line tool designed to make cross-browser testing easier before you commit a line of code.</p>
<p>Yeti automatically launches JavaScript unit tests in a browser and reports the results without leaving your terminal. It&#8217;s very simple to use: Just run <code>yeti test.html</code> to get the results of the YUI Test-based test in <code>test.html</code>. You can pass multiple HTML documents to test multiple components at once.</p>
<pre><code>$ yeti dom/tests/dom.html attribute/tests/attribute.html json/tests/json.html
✔  DOM Tests from Safari (Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-us) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16)
  20 passed
  0 failed

✔  Y.JSON (JavaScript implementation) from Safari (Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-us) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16)
  68 passed
  0 failed

✔  Attribute Unit Tests from Safari (Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-us) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16)
  106 passed
  0 failed

194 tests passed! (3217ms)
</code></pre>
<p>The real power of Yeti is running tests in multiple browsers simultaneously. Although Yeti can open your tests one-by-one on your computer, Yeti allows you to run tests on any browser on any device&mdash;all at the same time.</p>
<p>If you run Yeti without arguments, it will start a web server that you can access at <code>http://localhost:8000</code>. You can then point browsers or devices on your network to that URL and every test you run from that point will be executed on all browsers visiting the test page.</p>
<p><a href="http://yuilibrary.com/projects/yeti/"><img src="http://yuiblog.com/assets/yeti/multiple-browsers.jpg" alt="Multiple browsers with Yeti" title="Yeti running a test on these browsers. Note that even uncaught exceptions are captured!" /></a></p>
<p>When combined with the excellent <a href="http://localtunnel.com/">localtunnel</a>, firewalls between you and other computers are less painful. If you&#8217;re not working with sensitive information, it&#8217;s a simple way to make your Yeti available to the internet:</p>
<pre><code>$ localtunnel 8000
   Port 8000 is now publicly accessible from http://example.localtunnel.com
</code></pre>
<p>You can then visit that URL to access Yeti and start running tests:</p>
<p><a href="http://yuilibrary.com/projects/yeti/"><img src="http://yuiblog.com/assets/yeti/mobilesafari.jpg" alt="Yeti on iOS 4 Safari" style="border:10px solid #000"></a></p>
<p>This is especially useful for cellular devices: You can use your carrier&#8217;s internet connection without needing to get your device on the same network as your development computer.</p>
<p>Yeti aims to make JavaScript testing easier; however, it&#8217;s far from being complete. (Don&#8217;t take the 0.1.0 version number lightly.) Yeti assumes you&#8217;re using YUI Test, has only been tested on Mac OS X, and may not work with some kinds of testing scenarios. Despite these shortcomings, Yeti has been so useful internally that we didn&#8217;t want to wait any longer to share it with the YUI community.</p>
<h4>Getting the code</h4>
<p>Yeti is <a href="http://github.com/yui/yeti">available on GitHub</a> and offered under <a href="http://developer.yahoo.com/yui/license.html">YUI&#8217;s BSD license</a>.</p>
<h4>Installing</h4>
<p>Yeti is written entirely in JavaScript and runs on top of <a href="http://nodejs.org/">NodeJS</a>. If you&#8217;re already a NodeJS and <a href="http://npmjs.org/">npm</a> user, installing is very simple:</p>
<pre><code style="font-size:170%">$ npm install yeti@stable
</code></pre>
<p>If you haven&#8217;t installed NodeJS and npm and you&#8217;re on a recent Mac, you can still install Yeti with a convenient installer.</p>
<table>
<tr><!-- going all web 1.0 for the rss crowd --></p>
<td><a href="http://yuilibrary.com/downloads/download.php?file=3716057163a82b4b00c2a00ab0bb186e"><img src="http://yuiblog.com/assets/yeti/download0.jpg" alt="Yeti Icon"></a></td>
<td><a style="font-size:120%;font-weight:bold" href="http://yuilibrary.com/downloads/download.php?file=3716057163a82b4b00c2a00ab0bb186e">Download the Yeti 0.1.0 Installer</a> 2.7 MB<br />
<span style="color:#666">Requires Mac OS X 10.6 and a Intel Core 2 processor or better</span></td>
</tr>
</table>
<p>If your computer doesn&#8217;t meet the installer&#8217;s requirements, you can still use Yeti if you&#8217;re able to install npm. More installation and usage instructions are available in <a href="http://github.com/yui/yeti/blob/master/README.md">Yeti&#8217;s README</a>.</p>
<h4>Your participation is welcome</h4>
<p>Yeti is the first project we&#8217;ve launched in <a href="http://yuilibrary.com/labs/">YUI Labs</a>, an umbrella category where our new ideas and initiatives will take shape. As such, Yeti is offered without the same level of support as our other projects. We still encourage you to ask questions and give feedback in <a href="http://yuilibrary.com/forum/viewforum.php?f=230">Yeti&#8217;s forums</a> and hope Yeti makes testing easy and fun. If it doesn&#8217;t, please <a href="http://yuilibrary.com/forum/viewforum.php?f=230">tell us</a>, <a href="http://yuilibrary.com/projects/yeti/newticket">file a bug</a> or consider <a href="http://yuilibrary.com/projects/yeti/pullreq">contributing to Yeti</a>.</p>
<p>Happy testing!</p>
<p class="authorbox"><em><strong>About the Author:</strong> Reid Burke (<a href="http://twitter.com/reid">@reid</a>) is the newest member of the YUI team. He loves abominable snow monsters and JavaScript.</em></p>
<div class="feedflare">
<a href="http://feeds.yuiblog.com/~ff/YahooUserInterfaceBlog?a=PLqo_CKr5R0:DEpO_NVzc38:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/YahooUserInterfaceBlog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.yuiblog.com/~ff/YahooUserInterfaceBlog?a=PLqo_CKr5R0:DEpO_NVzc38:dnMXMwOfBR0"><img src="http://feeds.feedburner.com/~ff/YahooUserInterfaceBlog?d=dnMXMwOfBR0" border="0"></img></a> <a href="http://feeds.yuiblog.com/~ff/YahooUserInterfaceBlog?a=PLqo_CKr5R0:DEpO_NVzc38:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/YahooUserInterfaceBlog?i=PLqo_CKr5R0:DEpO_NVzc38:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.yuiblog.com/~ff/YahooUserInterfaceBlog?a=PLqo_CKr5R0:DEpO_NVzc38:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/YahooUserInterfaceBlog?i=PLqo_CKr5R0:DEpO_NVzc38:F7zBnMyn0Lo" border="0"></img></a>
</div>]]></content:encoded>
			<wfw:commentRss>http://www.yuiblog.com/blog/2010/08/25/introducing-yeti-the-yui-easy-testing-interface/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		<feedburner:origLink>http://www.yuiblog.com/blog/2010/08/25/introducing-yeti-the-yui-easy-testing-interface/</feedburner:origLink></item>
		<item>
		<title>Developing an Accessible Star Ratings Widget</title>
		<link>http://feeds.yuiblog.com/~r/YahooUserInterfaceBlog/~3/Vsez1L425Ak/</link>
		<comments>http://www.yuiblog.com/blog/2010/08/24/developing-an-accessible-star-ratings-widget/#comments</comments>
		<pubDate>Tue, 24 Aug 2010 17:00:41 +0000</pubDate>
		<dc:creator>Thierry Koblentz</dc:creator>
				<category><![CDATA[Accessibility]]></category>
		<category><![CDATA[Development]]></category>

		<guid isPermaLink="false">http://www.yuiblog.com/blog/?p=2500</guid>
		<description><![CDATA[
/*
 * for the forms in this article
 */
.tjk-form {margin-bottom:3em;}
/*
 ****************************
 Image Replacement Techniques 
 ****************************
 *
 * A la Yahoo! Music
 * the original sprite is vertical and the styling is slightly different
 */
 #noticias .tjk-spans span, .tjk-spans span {
	background:transparent url(http://yuiblog.com/assets/starTutorial/sprite.png) no-repeat scroll 0; 
	font-size:0;
	height: 19px;
	overflow:hidden;
	vertical-align:middle;
	width: 96px; 
	display: block;
}
.tjk-spans em {
	display:block;
}
.tjk-spans .r15 {
	background-position:-385px 0;
}
.tjk-spans [...]]]></description>
			<content:encoded><![CDATA[<style type="text/css">
/*
 * for the forms in this article
 */
.tjk-form {margin-bottom:3em;}
/*
 ****************************
 Image Replacement Techniques 
 ****************************
 *
 * A la Yahoo! Music
 * the original sprite is vertical and the styling is slightly different
 */
 #noticias .tjk-spans span, .tjk-spans span {
	background:transparent url(http://yuiblog.com/assets/starTutorial/sprite.png) no-repeat scroll 0; 
	font-size:0;
	height: 19px;
	overflow:hidden;
	vertical-align:middle;
	width: 96px; 
	display: block;
}
.tjk-spans em {
	display:block;
}
.tjk-spans .r15 {
	background-position:-385px 0;
}
.tjk-spans .r25 {
	background-position:-288px 0;
}
.tjk-spans .r35 {
	background-position:-192px 0;
}
.tjk-spans .r45 {
	background-position:-96px 0;
}
/*
 * TIP Method
 * http://www.tjkdesign.com/articles/tip.asp
 */
.tjk-tip span {
	position:relative;
	height:1.6em;
	width:8.1em;
	overflow:hidden;
	vertical-align:middle;
	display:block;
}
.tjk-tip img {
	position: absolute;
	width: 40.5em;
	height: 1.55em;
	top: 0;
	border:1px solid #fff;
}
.tjk-tip .r15 img {
	right:0;
}
.tjk-tip .r25 img {
	left:-24.4em;
}
.tjk-tip .r35 img {
	left:-16.2em;
}
.tjk-tip .r45 img {
	left:-8.1em;
}
/*
 * to overwrite SM styles sheet
 */
.tjk-tip img,
#tjk-une img,
#tjk-deux img,
#tjk-trois img,
#tjk-quatre img,
#tjk-cinq img,
#tjk-six img,
#tjk-sept img,
#tjk-huit img {
	margin: 0;
}
.tjk-tip {
	line-height:3;
}
/*
 ****************************
 Labels, <br />, <img> in Forms 
 ****************************
 *
 *
 * #1
 */
#tjk-une img {
	width:2.8em;
	height:1.4em;
}
/*
 * #2
 */
#tjk-deux label {
	position:relative;
}
#tjk-deux img {
	width:2.8em;
	height:1.4em;
	position:absolute;
	top:0;
	left:0;	
}
/*
 * #3
 */
#tjk-trois label {
	position:relative;
	height:1.4em;
	width:1.4em;
	overflow:hidden;
	display:block;
}
#tjk-trois img {
	width:2.8em;
	height:1.4em;
	position:absolute;
	top:0;
	left:0;		
}
/*
 * #4
 */
#tjk-quatre br {
	display:none;
}
#tjk-quatre label {
	position:relative;
	height:1.4em;
	width:1.4em;
	overflow:hidden;
	float:left;
}
#tjk-quatre img {
	width:2.8em;
	height:1.4em;
	position:absolute;
	top:0;
	left:0;
}
/*
 * #5
 */
#tjk-cinq br {
	display:none; 
}
#tjk-cinq label {
	position:relative;
	height:1.4em;
	width:1.4em;
	overflow:hidden;
	float:left;
}
#tjk-cinq img {
	width:2.8em;
	height:1.4em;
	position:absolute;
	top:0;
	left:0;
}
#tjk-cinq .no_star img {
	left:-1.4em;
}
/*
 * #6
 */
#tjk-six br {
	display:none; 
} 
#tjk-six label {
	position:relative;
	height:1.4em;
	width:1.4em;
	overflow:hidden;
	float:left;
	background:teal;
	margin-right:1px;
	text-indent:-999em; /* this also remmoves the jump of the stars on focus */
}
#tjk-six img {
	width:2.8em;
	height:1.4em;
	position:absolute;
	top:0;
	left:0;
}
#tjk-six .no_star img {
	left:-1.4em;
}
#tjk-six label.no_star {
	background:#ccc;
}
/*
 * #7
 */
#tjk-sept br {
	display:none; 
} 
#tjk-sept label {
	position:relative;
	height:1.4em;
	width:1.4em;
	overflow:hidden;
	float:left;
	background:teal;
	margin-right:1px;
	text-indent:-999em; 
}
#tjk-sept img {
	width:2.8em;
	height:1.4em;
	position:absolute;
	top:0;
	left:0;
	cursor: pointer;
}
#tjk-sept .no_star img {
	left:-1.4em;
}
#tjk-sept label.no_star {
	background:#ccc;
}
/*
 * of course this does not work in IE6 
 * Opera lets the background color bleed through 
 */
#tjk-sept label:hover {
	opacity:.5;
	filter:alpha(opacity=50);
}
#tjk-sept fieldset {
	border:0;
}
#tjk-sept legend {
	text-indent:-999em;
}
/*
 * #8
 */
#tjk-huit br {
	display:none; 
} 
#tjk-huit label {
	position:relative;
	height:1.4em;
	width:1.4em;
	overflow:hidden;
	float:left;
	background:teal;
	margin-right:1px;
	text-indent:-999em; 
}
#tjk-huit img {
	width:2.8em;
	height:1.4em;
	position:absolute;
	top:0;
	left:0;
}
#tjk-huit .no_star img {
	left:-1.4em;
}
#tjk-huit label.no_star {
	background:#ccc;
}
#tjk-huit fieldset {
	border:0;
}
#tjk-huit legend {
	text-indent:-999em;
}
</style>
<p>In a hurry? Skip to the <a href="http://tjkdesign.com/lab/stars/demo.html">demo page</a>.</p>
<p>Many ecommerce sites, social networking services, and online communities include rating or assessment features. Soliciting people&#8217;s opinion has even become a business model; there are now  sites dedicated to rating products, services, businesses, and more.</p>
<p>The most common interface used to display votes is the &#8220;star rating system,&#8221; in which a particular number of points (often expressed as stars) is assigned to an item by each reviewer. We find this model on many sites, from Amazon to Yelp.</p>
<div class="figure">
<img src="http://yuiblog.com/assets/star-examples-20100823-131202.jpg" width="501" height="284" alt="Examples of star rating systems"></p>
<p class="legend">Figure A. Star rating examples from <a href="http://www.amazon.com">Amazon</a> and <a href="http://www.yelp.com">Yelp</a>.</p>
</div>
<p>As <a href="#A">Figure A</a> shows, both visual interfaces are similar, but what makes these two solutions <em>interesting</em> is their markup base. One relies on <code>&lt;map&gt;</code>, the other on <code>&lt;img&gt;</code>.</p>
<p>You might think that most rating systems would be based on some markup proven to be semantic and &quot;operational&quot; across many User Agents — that is, that rating systems would be based on a specific set of HTML elements and attributes to which one applies behavior and style via JS and CSS. That would make sense, but it is far from the truth. When it comes to markup, authors try just about everything:</p>
<ul>
<li><code>&lt;a&gt;</code>,</li>
<li><code>&lt;img&gt;</code>,</li>
<li><code>&lt;span&gt;</code>,</li>
<li><code>&lt;li&gt;</code>,</li>
<li><code>&lt;map&gt;</code>,</li>
<li><code>&lt;div&gt;</code>,</li>
<li><code>&lt;input&gt;</code>,</li>
<li>and more&#8230;</li>
</ul>
<h3>The case of Microformats</h3>
<p>Before presenting a few image-based techniques to mark up ratings, I think it is worth mentioning a basic and straightforward approach (from <a href="http://microformats.org/wiki/hreview">Microformats</a>) that uses <em>characters</em>:</p>
<pre><code>&lt;abbr class=&quot;rating&quot; title=&quot;3 stars&quot;&gt;***&lt;/abbr&gt;</code></pre>
<dl>
<dt>Pros</dt>
<dd>It is straightforward and semantic.</dd>
<dd>The markup is minimal.</dd>
<dd>The method is not reliant on <abbr title="Cascading Styles Sheet">CSS</abbr>.</dd>
<dd>The method is not reliant on images.</dd>
<dd>There is no HTTP request.</dd>
<dt>Cons</dt>
<dd>It is impossible to represent half values (i.e. 3.5 stars)</dd>
<dd>It &quot;works&quot; only with asterisks (&quot;star rating&quot;).</dd>
<dd>Screen-readers, by default, do not expand abbreviations (which may not be a big deal in this case).</dd>
</dl>
<p><em>Note</em>: I use &quot;*&quot; rather than &#9733; (&#9733;) because screen-readers (at least <a href="http://www.freedomscientific.com/products/fs/jaws-product-page.asp">JAWS</a> and <a href="http://www.nvda-project.org/">NVDA</a>) seem to ignore html entities.</p>
<h3>Markup to <em>display</em> image-based ratings</h3>
<p>When it comes to display images, authors have many options.</p>
<h4>One image per rating</h4>
<p>Using a single image:</p>
<pre><code>&lt;img src=&quot;4stars.png&quot; alt=&quot;4 out of five&quot;&gt;</code></pre>
<dl>
<dt>One star</dt>
<dd><img src="http://yuiblog.com/assets/starTutorial/1-star.png" alt="1 out of five"></dd>
<dt>Two stars</dt>
<dd><img src="http://yuiblog.com/assets/starTutorial/2-stars.png" alt="2 out of five"></dd>
<dt>Three stars</dt>
<dd><img src="http://yuiblog.com/assets/starTutorial/3-stars.png" alt="3 out of five"></dd>
<dt>Four stars</dt>
<dd><img src="http://yuiblog.com/assets/starTutorial/4-stars.png" alt="4 out of five"></dd>
<dt>Five stars</dt>
<dd><img src="http://yuiblog.com/assets/starTutorial/5-stars.png" alt="5 out of five"></dd>
</dl>
<dl>
<dt>Pros</dt>
<dd>Using one image per rating is straightforward and semantic.</dd>
<dd>The method is not reliant on <abbr>CSS</abbr>.</dd>
<dd>Minimal markup.</dd>
<dt>Cons</dt>
<dd>It creates many HTTP requests as there are many different images.</dd>
<dd>On top of the performance issue, it can be a maintenance nightmare as authors have to deal with <em>more</em> assets (images to create, to push to a CDN, to modify when site colors change, etc.).</dd>
<dd>Text selection <em>is not possible</em> in Opera (at least in version 9.52) as the <em>alternate text</em> is ignored</dd>
</dl>
<h4>One image per unit</h4>
<p>From the <abbr title="Web Hypertext Application Technology Working Group">whatwg</abbr>&#8217;s <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/embedded-content-0.html#a-group-of-images-that-form-a-single-larger-picture-with-no-links">working draft</a>: </p>
<pre><code>&lt;img alt=&quot;4 out of 5&quot; src=&quot;one-star.png&quot;&gt;
&lt;img alt=&quot;&quot; src=&quot;one-star.png&quot;&gt;
&lt;img alt=&quot;&quot; src=&quot;one-star.png&quot;&gt;
&lt;img alt=&quot;&quot; src=&quot;one-star.png&quot;&gt;
&lt;img alt=&quot;&quot; src=&quot;no-star.png&quot;&gt;</code></pre>
<dl>
<dt>One star</dt>
<dd> <span class="rating"> <img src="http://yuiblog.com/assets/starTutorial/one-star.png" alt="1 out of five"> <img src="http://yuiblog.com/assets/starTutorial/no-star.png" alt=""> <img src="http://yuiblog.com/assets/starTutorial/no-star.png" alt=""> <img src="http://yuiblog.com/assets/starTutorial/no-star.png" alt=""> <img src="http://yuiblog.com/assets/starTutorial/no-star.png" alt=""> </span> </dd>
<dt>Two stars</dt>
<dd> <span class="rating"> <img src="http://yuiblog.com/assets/starTutorial/one-star.png" alt="2 out of five"> <img src="http://yuiblog.com/assets/starTutorial/one-star.png" alt=""> <img src="http://yuiblog.com/assets/starTutorial/no-star.png" alt=""> <img src="http://yuiblog.com/assets/starTutorial/no-star.png" alt=""> <img src="http://yuiblog.com/assets/starTutorial/no-star.png" alt=""> </span> </dd>
<dt>Three stars</dt>
<dd> <span class="rating"> <img src="http://yuiblog.com/assets/starTutorial/one-star.png" alt="3 out of five"> <img src="http://yuiblog.com/assets/starTutorial/one-star.png" alt=""> <img src="http://yuiblog.com/assets/starTutorial/one-star.png" alt=""> <img src="http://yuiblog.com/assets/starTutorial/no-star.png" alt=""> <img src="http://yuiblog.com/assets/starTutorial/no-star.png" alt=""> </span> </dd>
<dt>Four stars</dt>
<dd> <span class="rating"> <img src="http://yuiblog.com/assets/starTutorial/one-star.png" alt="4 out of five"> <img src="http://yuiblog.com/assets/starTutorial/one-star.png" alt=""> <img src="http://yuiblog.com/assets/starTutorial/one-star.png" alt=""> <img src="http://yuiblog.com/assets/starTutorial/one-star.png" alt=""> <img src="http://yuiblog.com/assets/starTutorial/no-star.png" alt=""> </span> </dd>
<dt>Five stars</dt>
<dd> <span class="rating"> <img src="http://yuiblog.com/assets/starTutorial/one-star.png" alt="5 out of five"> <img src="http://yuiblog.com/assets/starTutorial/one-star.png" alt=""> <img src="http://yuiblog.com/assets/starTutorial/one-star.png" alt=""> <img src="http://yuiblog.com/assets/starTutorial/one-star.png" alt=""> <img src="http://yuiblog.com/assets/starTutorial/one-star.png" alt=""> </span> </dd>
</dl>
<dl>
<dt>Pros</dt>
<dd>Using two <code>img</code> elements per rating diminishes the number of HTTP requests.</dd>
<dd>The method is not reliant on <abbr>CSS</abbr>.</dd>
<dt>Cons</dt>
<dd>In Opera, when images are disabled, <em>alternate text</em> is not selectable, and (in small-screen view) that text is rendered with a border which makes it less legible.</dd>
</dl>
<p>Note that this is taken from a <em>controversial working draft</em>. In my opinion, this method is not acceptable because the alternate text does not describe the image <em>accurately</em> and succinctly. Besides, if the basis of this approach is that these images represent content, then why leave some of them with <em>no</em> <code>alt</code> text?</p>
<p> On <a href="http://ajaxian.com/">Ajaxian</a>, for example, the author is using alternate text with <em>every single image</em>, which makes a lot of sense if he considers that <em>each one is content</em>: </p>
<pre><code>&lt;img [snip] alt=&quot;+&quot; src=&quot;star1.png&quot;/&gt;
&lt;img [snip] alt=&quot;+&quot; src=&quot;star1.png&quot;/&gt;
&lt;img [snip] alt=&quot;+&quot; src=&quot;star1.png&quot;/&gt;
&lt;img [snip] alt=&quot;-&quot; src=&quot;star0.png&quot;/&gt;
&lt;img [snip] alt=&quot;-&quot; src=&quot;star0.png&quot;/&gt;</code></pre>
<p>In any case, using as many images as there are stars versus using a single element (an <code>img</code> or  something else) has the main advantage of facilitating voting mechanisms &#8211; where a user selects one of the stars to cast his vote. So we should keep this in mind&#8230;</p>
<h4>A sprite for background images</h4>
<p>The following technique is a adaptation of a strategy originally implemented by developers at <a href="http://new.music.yahoo.com/">Yahoo! Music</a>:</p>
<h5>Markup</h5>
<pre><code>&lt;span class=&quot;rating r1 stars&quot;&gt;1 of 5&lt;/span&gt;
&lt;span class=&quot;rating r2 stars&quot;&gt;2 of 5&lt;/span&gt;
&lt;span class=&quot;rating r3 stars&quot;&gt;3 of 5&lt;/span&gt;
&lt;span class=&quot;rating r4 stars&quot;&gt;4 of 5&lt;/span&gt;
&lt;span class=&quot;rating r5 stars&quot;&gt;5 of 5&lt;/span&gt;
</code></pre>
<h5>CSS</h5>
<pre><code>.stars {
  background: transparent url(img/sprite.png) no-repeat;
}
.rating {
  font-size: 0;
  height: 19px;
  overflow: hidden;
  vertical-align: middle;
  width: 96px;
  display: block;
}
.r1 { background-position: -385px 0; }
.r2 { background-position: -288px 0; }
.r3 { background-position: -192px 0; }
.r4 { background-position: -96px 0; }</code></pre>
<dl class="tjk-spans">
<dt>One star</dt>
<dd><span class="r15"><em>1 of 5</em></span></dd>
<dt>Two stars</dt>
<dd><span class="r25"><em>2 of 5</em></span></dd>
<dt>Three stars</dt>
<dd><span class="r35"><em>3 of 5</em></span></dd>
<dt>Four stars</dt>
<dd><span class="r45"><em>4 of 5</em></span></dd>
<dt>Five stars</dt>
<dd><span class="r55"><em>5 of 5</em></span></dd>
</dl>
<dl>
<dt>Pros</dt>
<dd>This method requires a single HTTP request as it relies on a single sprite image.</dd>
<dd>Minimal &quot;foot print&quot;.</dd>
<dt>Cons</dt>
<dd>Content is not revealed with images off.</dd>
<dd>Nothing shows  when the page is printed (a print stylesheet could take care of this issue). </dd>
<dd>In Opera, the high contrast stylesheet makes all the stars disappear; the same is true in <a href="http://www.artzstudio.com/2010/04/img-sprites-high-contrast/">High Contrast Mode Optimization</a>.</dd>
<dd>Text selection is possible, but it&#8217;s not obvious (via highlighting).</dd>
</dl>
<h4>A sprite in the markup</h4>
<p>This approach is based on the <a href="http://tjkdesign.com/articles/how-to_use_sprites_with_my_Image_Replacement_technique.asp">TIP method</a>, which uses a <a href="http://tjkdesign.com/lab/stars/img/sprite.gif">sprite image</a> as an <code>&lt;img&gt;</code> element rather than a background image:</p>
<h5>Markup</h5>
<pre><code>&lt;span title=&quot;1 of 5&quot; class=&quot;rating r1&quot;&gt;&lt;img width=&quot;0&quot; height=&quot;1&quot; src=&quot;sprite.gif&quot; alt=&quot;&quot;/&gt;1 out of 5&lt;/span&gt;
&lt;span title=&quot;2 of 5&quot; class=&quot;rating r2&quot;&gt;&lt;img width=&quot;0&quot; height=&quot;1&quot; src=&quot;sprite.gif&quot; alt=&quot;&quot;/&gt;2 out of 5&lt;/span&gt;
&lt;span title=&quot;3 of 5&quot; class=&quot;rating r3&quot;&gt;&lt;img width=&quot;0&quot; height=&quot;1&quot; src=&quot;sprite.gif&quot; alt=&quot;&quot;/&gt;3 out of 5&lt;/span&gt;
&lt;span title=&quot;4 of 5&quot; class=&quot;rating r4&quot;&gt;&lt;img width=&quot;0&quot; height=&quot;1&quot; src=&quot;sprite.gif&quot; alt=&quot;&quot;/&gt;4 out of 5&lt;/span&gt;
&lt;span title=&quot;5 of 5&quot; class=&quot;rating r5&quot;&gt;&lt;img width=&quot;0&quot; height=&quot;1&quot; src=&quot;sprite.gif&quot; alt=&quot;&quot;/&gt;5 out of 5&lt;/span&gt;
</code></pre>
<h5>CSS</h5>
<pre><code>.rating {
  position: relative;
  height: 1.6em;
  width: 8.1em;
  overflow: hidden;
  vertical-align: middle;
  display: block;
}
.rating img {
  position: absolute;
  width: 40.5em;
  height: 1.55em;
  top: 0;
  border: 1px solid #fff;
}
.r1 img { right: 0; }
.r2 img { left: -24.4em; }
.r3 img { left: -16.2em; }
.r4 img { left: -8.1em; }
</code></pre>
<dl class="tjk-tip">
<dt>One star</dt>
<dd><span title="1 out of 5" class="r15"><img width="0" height="1" src="http://yuiblog.com/assets/starTutorial/sprite.gif" alt="">1 out of 5</span></dd>
<dt>Two stars</dt>
<dd><span title="2 out of 5" class="r25"><img width="0" height="1" src="http://yuiblog.com/assets/starTutorial/sprite.gif" alt="">2 out of 5</span></dd>
<dt>Three stars</dt>
<dd><span title="3 out of 5" class="r35"><img width="0" height="1" src="http://yuiblog.com/assets/starTutorial/sprite.gif" alt="">3 out of 5</span></dd>
<dt>Four stars</dt>
<dd><span title="4 out of 5" class="r45"><img width="0" height="1" src="http://yuiblog.com/assets/starTutorial/sprite.gif" alt="">4 out of 5</span></dd>
<dt>Five stars</dt>
<dd><span title="5 out of 5" class="r55"><img width="0" height="1" src="http://yuiblog.com/assets/starTutorial/sprite.gif" alt="">5 out of 5</span></dd>
</dl>
<dl>
<dt>Pros</dt>
<dd>This method requires a single HTTP request.</dd>
<dd>This technique is the only one of the four methods above that <em>reveals</em> content when  Firefox users select &quot;hide images&quot; or &quot;make images invisible&quot; (from the developer&#8217;s toolbar).</dd>
<dd>When images are unavailable a red &quot;x&quot; appears only in the highest rating (i.e. 5 out of 5) instead of in each one as it is the case with other solutions that rely on <code>img</code> elements.</dd>
<dt>Cons</dt>
<dd>The display of images is reliant on <abbr>CSS</abbr>.</dd>
</dl>
<p> It is worth noting that unlike other Image Replacement techniques, this method allows:</p>
<ul>
<li> images to <em>scale</em> depending on text-size settings.</li>
<li>images to be <em>printed</em>.</li>
<li> alternate text to be easily <em>selected</em> as the whole image appears highlighted (Firefox). </li>
<li>the image to not disappear in a high-contrast setting/stylesheet.</li>
<li>alternate text selection in Opera (when images are disabled).</li>
<li>borderless alternate text in Opera&#8217;s small screen view.</li>
</ul>
<h3>Markup to <em>cast</em> votes</h3>
<h4>Starting with a native mechanism</h4>
<p>To <em>cast votes</em>, we need  a low-level voting mechanism that allows  simple user selection and submission. For this, we can rely on using a form with labels and controls: </p>
<h5>Markup</h5>
<pre><code>&lt;fieldset&gt;
  &lt;legend&gt;Rating&lt;/legend&gt;
  &lt;label&gt;&lt;input type=&quot;radio&quot; name=&quot;movie&quot; value=&quot;1_5&quot;&gt;1/5&lt;/label&gt;
  &lt;label&gt;&lt;input type=&quot;radio&quot; name=&quot;movie&quot; value=&quot;2_5&quot;&gt;2/5&lt;/label&gt;
  &lt;label&gt;&lt;input type=&quot;radio&quot; name=&quot;movie&quot; value=&quot;3_5&quot;&gt;3/5&lt;/label&gt;
  &lt;label&gt;&lt;input type=&quot;radio&quot; name=&quot;movie&quot; value=&quot;4_5&quot;&gt;4/5&lt;/label&gt;
  &lt;label&gt;&lt;input type=&quot;radio&quot; name=&quot;movie&quot; value=&quot;5_5&quot;&gt;5/5&lt;/label&gt;
&lt;/fieldset&gt;</code></pre>
<h5>Result</h5>
<form class="tjk-form"  name="form_1" action="" method="post">
<fieldset>
<legend>Rating</legend>
<p><label><br />
<input type="radio" name="movie" value="1_5">1/5</label><br />
<label><br />
<input type="radio" name="movie" value="2_5">2/5</label><br />
<label><br />
<input type="radio" name="movie" value="3_5">3/5</label><br />
<label><br />
<input type="radio" name="movie" value="4_5">4/5</label><br />
<label><br />
<input type="radio" name="movie" value="5_5">5/5</label><br />
</fieldset>
</form>
<h4>Adding breaks and whitespace</h4>
<p>For better legibility, we add <code>&lt;br&gt;</code> and whitespace.</p>
<h5>Markup</h5>
<pre><code>&lt;fieldset&gt;  &lt;legend&gt;Rating&lt;/legend&gt;
  &lt;label&gt;&lt;input type=&quot;radio&quot; name=&quot;movie&quot; value=&quot;1_5&quot;&gt; 1/5&lt;/label&gt;&lt;br&gt;
  &lt;label&gt;&lt;input type=&quot;radio&quot; name=&quot;movie&quot; value=&quot;2_5&quot;&gt; 2/5&lt;/label&gt;&lt;br&gt;
  &lt;label&gt;&lt;input type=&quot;radio&quot; name=&quot;movie&quot; value=&quot;3_5&quot;&gt; 3/5&lt;/label&gt;&lt;br&gt;
  &lt;label&gt;&lt;input type=&quot;radio&quot; name=&quot;movie&quot; value=&quot;4_5&quot;&gt; 4/5&lt;/label&gt;&lt;br&gt;
  &lt;label&gt;&lt;input type=&quot;radio&quot; name=&quot;movie&quot; value=&quot;5_5&quot;&gt; 5/5&lt;/label&gt;
&lt;/fieldset&gt;</code></pre>
<h5>Result</h5>
<form class="tjk-form"  name="form_2" action="" method="post">
<fieldset>
<legend>Rating</legend>
<p><label><br />
<input type="radio" name="movie" value="1_5"> 1/5</label><br />
<label><br />
<input type="radio" name="movie" value="2_5"> 2/5</label><br />
<label><br />
<input type="radio" name="movie" value="3_5"> 3/5</label><br />
<label><br />
<input type="radio" name="movie" value="4_5"> 4/5</label><br />
<label><br />
<input type="radio" name="movie" value="5_5"> 5/5</label><br />
</fieldset>
</form>
<h4>Introducing the sprite image in the markup</h4>
<p>For this solution, we are using a smaller sprite than the one in the example above. It is now composed of <a href="http://tjkdesign.com/lab/stars/img/small-sprite.gif">two single stars</a> (&#8220;on&#8221; and &#8220;off&#8221;).</p>
<p>We place <code>img</code> elements inside the labels. We assume they will have no value without CSS support, thus we &quot;hide&quot; them by setting specific dimensions via their <code>width</code> and <code>height</code> attributes. Note that using <code>0</code> with both attributes would show a broken image in some <abbr title="User Agent">UA</abbr>s.</p>
<pre><code>
&lt;form ...&gt;
  &lt;fieldset&gt;
    &lt;legend&gt;Rating&lt;/legend&gt;
    &lt;label class=&quot;one&quot; title=&quot;1 out of 5&quot;&gt;&lt;input name=&quot;LandOf&quot; value=&quot;1&quot; checked=&quot;checked&quot; type=&quot;radio&quot;&gt; 1/5&lt;img src=&quot;star-sprite.gif&quot; alt=&quot;&quot; height=&quot;0&quot; width=&quot;0&quot;&gt;&lt;/label&gt;
    &lt;label class=&quot;two&quot; title=&quot;2 out of 5&quot;&gt;&lt;input name=&quot;LandOf&quot; value=&quot;2&quot; type=&quot;radio&quot;&gt; 2/5&lt;img src=&quot;star-sprite.gif&quot; alt=&quot;&quot; height=&quot;0&quot; width=&quot;0&quot;&gt;&lt;/label&gt;
    &lt;label class=&quot;three&quot; title=&quot;3 out of 5&quot;&gt;&lt;input name=&quot;LandOf&quot; value=&quot;3&quot; type=&quot;radio&quot;&gt; 3/5&lt;img src=&quot;star-sprite.gif&quot; alt=&quot;&quot; height=&quot;0&quot; width=&quot;0&quot;&gt;&lt;/label&gt;
    &lt;label class=&quot;four&quot; title=&quot;4 out of 5&quot;&gt;&lt;input name=&quot;LandOf&quot; value=&quot;4&quot; type=&quot;radio&quot;&gt; 4/5&lt;img src=&quot;star-sprite.gif&quot; alt=&quot;&quot; height=&quot;0&quot; width=&quot;0&quot;&gt;&lt;/label&gt;
    &lt;label class=&quot;five&quot; title=&quot;5 out of 5&quot;&gt;&lt;input name=&quot;LandOf&quot; value=&quot;5&quot; type=&quot;radio&quot;&gt; 5/5&lt;img src=&quot;star-sprite.gif&quot; alt=&quot;&quot; height=&quot;0&quot; width=&quot;0&quot;&gt;&lt;/label&gt;
  &lt;/fieldset&gt;
&lt;/form&gt;</code></pre>
<p>Note that with the above markup, we can expect (in most browsers) field selection via label selection.</p>
<h5>Considering Accessibility</h5>
<p>Unfortunately, <em>as is</em>, this markup creates issues in at least two screen-readers: <a href="http://www.freedomscientific.com/products/fs/jaws-product-page.asp">JAWS</a> and <a href="http://www.nvda-project.org/">NVDA</a> (see <a href="http://public.yahoo.com/~thierryk/test-case/implicit-labeling-and-attributes.html">test case</a> for these bugs). The problem is  related to the use of a <code>title</code> attribute and an empty string for alternate text. </p>
<p>The workaround to not confuse screen-reader users is to use &quot;stars&quot; as alternate text (<code>alt</code>) and use JavaScript to insert <code> title</code> on <em>mouseover</em>. </p>
<h5>Better Markup</h5>
<pre><code>&lt;fieldset&gt;  &lt;legend&gt;Rating&lt;/legend&gt;
  &lt;label&gt;&lt;img src=&quot;img/small-sprite.gif&quot;  width=&quot;0&quot; height=&quot;1&quot; alt=&quot;stars&quot;&gt;&lt;input type=&quot;radio&quot; name=&quot;movie&quot; value=&quot;1_5&quot;&gt; 1/5&lt;/label&gt;&lt;br&gt;
  &lt;label&gt;&lt;img src=&quot;img/small-sprite.gif&quot;  width=&quot;0&quot; height=&quot;1&quot; alt=&quot;stars&quot;&gt;&lt;input type=&quot;radio&quot; name=&quot;movie&quot; value=&quot;2_5&quot;&gt; 2/5&lt;/label&gt;&lt;br&gt;
  &lt;label&gt;&lt;img src=&quot;img/small-sprite.gif&quot;  width=&quot;0&quot; height=&quot;1&quot; alt=&quot;stars&quot;&gt;&lt;input type=&quot;radio&quot; name=&quot;movie&quot; value=&quot;3_5&quot;&gt; 3/5&lt;/label&gt;&lt;br&gt;
  &lt;label&gt;&lt;img src=&quot;img/small-sprite.gif&quot;  width=&quot;0&quot; height=&quot;1&quot; alt=&quot;stars&quot;&gt;&lt;input type=&quot;radio&quot; name=&quot;movie&quot; value=&quot;4_5&quot;&gt; 4/5&lt;/label&gt;&lt;br&gt;
  &lt;label&gt;&lt;img src=&quot;img/small-sprite.gif&quot;  width=&quot;0&quot; height=&quot;1&quot; alt=&quot;stars&quot;&gt;&lt;input type=&quot;radio&quot; name=&quot;movie&quot; value=&quot;5_5&quot;&gt; 5/5&lt;/label&gt;
&lt;/fieldset&gt; </code></pre>
<h5>Result</h5>
<form class="tjk-form" name="form_trois" action="" method="post">
<fieldset>
<legend>Rating</legend>
<p><label><img src="http://yuiblog.com/assets/starTutorial/small-sprite.gif"  width="0" height="1" alt="stars"><br />
<input type="radio" name="movie" value="1_5"> 1/5</label><br />
<label><img src="http://yuiblog.com/assets/starTutorial/small-sprite.gif"  width="0" height="1" alt="stars"><br />
<input type="radio" name="movie" value="2_5"> 2/5</label><br />
<label><img src="http://yuiblog.com/assets/starTutorial/small-sprite.gif"  width="0" height="1" alt="stars"><br />
<input type="radio" name="movie" value="3_5"> 3/5</label><br />
<label><img src="http://yuiblog.com/assets/starTutorial/small-sprite.gif"  width="0" height="1" alt="stars"><br />
<input type="radio" name="movie" value="4_5"> 4/5</label><br />
<label><img src="http://yuiblog.com/assets/starTutorial/small-sprite.gif"  width="0" height="1" alt="stars"><br />
<input type="radio" name="movie" value="5_5"> 5/5</label><br />
</fieldset>
</form>
<h3>Styling</h3>
<h4>Giving dimensions to the image via CSS</h4>
<p>We use <code>em</code>  to allow the image to grow or shrink depending on font-size.</p>
<h5>Markup</h5>
<p>Unchanged</p>
<h5>CSS</h5>
<pre><code>img {
  width:2.8em;
  height:1.4em;
}</code></pre>
<h5>Result</h5>
<form class="tjk-form" id="tjk-une" name="tjk-une" action="" method="post">
<fieldset>
<legend>Rating</legend>
<p><label><img src="http://yuiblog.com/assets/starTutorial/small-sprite.gif"  width="0" height="1" alt="stars"><br />
<input type="radio" name="movie" value="1_5"> 1/5</label><br />
<label><img src="http://yuiblog.com/assets/starTutorial/small-sprite.gif"  width="0" height="1" alt="stars"><br />
<input type="radio" name="movie" value="2_5"> 2/5</label><br />
<label><img src="http://yuiblog.com/assets/starTutorial/small-sprite.gif"  width="0" height="1" alt="stars"><br />
<input type="radio" name="movie" value="3_5"> 3/5</label><br />
<label><img src="http://yuiblog.com/assets/starTutorial/small-sprite.gif"  width="0" height="1" alt="stars"><br />
<input type="radio" name="movie" value="4_5"> 4/5</label><br />
<label><img src="http://yuiblog.com/assets/starTutorial/small-sprite.gif"  width="0" height="1" alt="stars"><br />
<input type="radio" name="movie" value="5_5"> 5/5</label><br />
</fieldset>
</form>
<p>As you can see already, clicking on an image selects the corresponding radio button. There is no need for scripting as implicit labeling produces this behavior (except in IE).</p>
<h4>Removing the image from the flow</h4>
<p>Styling the <code>label</code> with <code>position:relative</code> and the image with <code>position:absolute</code> with <code>top</code>/<code>left</code> values is enough to hide <code>input</code> and text inside the labels.</p>
<h5>Markup</h5>
<p>Unchanged</p>
<h5>CSS</h5>
<pre><code><ins>label {</ins>
  <ins>position:relative;
}</ins>
img {
  width:2.8em;
  height:1.4em;
  <ins>position:absolute;</ins>
  <ins>top:0;</ins>
  <ins>left:0;</ins>
}</code></pre>
<h5>Result</h5>
<form class="tjk-form" id="tjk-deux" name="tjk-deux" action="" method="post">
<fieldset>
<legend>Rating</legend>
<p><label><img src="http://yuiblog.com/assets/starTutorial/small-sprite.gif"  width="0" height="1" alt="stars"><br />
<input type="radio" name="movie" value="1_5"> 1/5</label><br />
<label><img src="http://yuiblog.com/assets/starTutorial/small-sprite.gif"  width="0" height="1" alt="stars"><br />
<input type="radio" name="movie" value="2_5"> 2/5</label><br />
<label><img src="http://yuiblog.com/assets/starTutorial/small-sprite.gif"  width="0" height="1" alt="stars"><br />
<input type="radio" name="movie" value="3_5"> 3/5</label><br />
<label><img src="http://yuiblog.com/assets/starTutorial/small-sprite.gif"  width="0" height="1" alt="stars"><br />
<input type="radio" name="movie" value="4_5"> 4/5</label><br />
<label><img src="http://yuiblog.com/assets/starTutorial/small-sprite.gif"  width="0" height="1" alt="stars"><br />
<input type="radio" name="movie" value="5_5"> 5/5</label><br />
</fieldset>
</form>
<h4>Displaying one star per label</h4>
<p>We style the label so its dimensions match the height and width of a single star.</p>
<h5>Markup</h5>
<p>Unchanged</p>
<h5>CSS</h5>
<pre><code>label {
  position:relative;
  <ins>height:1.4em;
</ins>  <ins>width:1.4em;
</ins>  <ins>overflow:hidden;
</ins>  <ins>display:block;</ins>
}
img {
  width:2.8em;
  height:1.4em;
  position:absolute;
  top:0;
  left:0;
}</code></pre>
<h5>Result</h5>
<form class="tjk-form" id="tjk-trois" name="tjk-trois" action="" method="post">
<fieldset>
<legend>Rating</legend>
<p><label><img src="http://yuiblog.com/assets/starTutorial/small-sprite.gif"  width="0" height="1" alt="stars"><br />
<input type="radio" name="movie" value="1_5"> 1/5</label><br />
<label><img src="http://yuiblog.com/assets/starTutorial/small-sprite.gif"  width="0" height="1" alt="stars"><br />
<input type="radio" name="movie" value="2_5"> 2/5</label><br />
<label><img src="http://yuiblog.com/assets/starTutorial/small-sprite.gif"  width="0" height="1" alt="stars"><br />
<input type="radio" name="movie" value="3_5"> 3/5</label><br />
<label><img src="http://yuiblog.com/assets/starTutorial/small-sprite.gif"  width="0" height="1" alt="stars"><br />
<input type="radio" name="movie" value="4_5"> 4/5</label><br />
<label><img src="http://yuiblog.com/assets/starTutorial/small-sprite.gif"  width="0" height="1" alt="stars"><br />
<input type="radio" name="movie" value="5_5"> 5/5</label><br />
</fieldset>
</form>
<h4>Displaying the stars horizontally</h4>
<p>We remove the <code>br</code>s and we float  the labels.</p>
<h5>Markup</h5>
<p>Unchanged</p>
<h5>CSS</h5>
<pre><code><ins>br {
</ins>  <ins>display:none;
}</ins>
label {
  position:relative;
  height:1.4em;
  width:1.4em;
  overflow:hidden;
  <del>display:block;</del>
  <ins>float:left;</ins>
}
img {
  width:2.8em;
  height:1.4em;
  position:absolute;
  top:0;
  left:0;
}</code></pre>
<h5>Result</h5>
<form class="tjk-form" id="tjk-quatre" name="tjk-quatre" action="" method="post">
<fieldset>
<legend>Rating</legend>
<p><label><img src="http://yuiblog.com/assets/starTutorial/small-sprite.gif"  width="0" height="1" alt="stars"><br />
<input type="radio" name="movie" value="1_5"> 1/5</label><br />
<label><img src="http://yuiblog.com/assets/starTutorial/small-sprite.gif"  width="0" height="1" alt="stars"><br />
<input type="radio" name="movie" value="2_5"> 2/5</label><br />
<label><img src="http://yuiblog.com/assets/starTutorial/small-sprite.gif"  width="0" height="1" alt="stars"><br />
<input type="radio" name="movie" value="3_5"> 3/5</label><br />
<label><img src="http://yuiblog.com/assets/starTutorial/small-sprite.gif"  width="0" height="1" alt="stars"><br />
<input type="radio" name="movie" value="4_5"> 4/5</label><br />
<label><img src="http://yuiblog.com/assets/starTutorial/small-sprite.gif"  width="0" height="1" alt="stars"><br />
<input type="radio" name="movie" value="5_5"> 5/5</label><br />
</fieldset>
</form>
<h4>Displaying the sprite image depending on rating</h4>
<p>To set a &quot;3 out of 5&quot; rating, we  apply the same class  to the last two labels. This class will shift the position of the image inside the label.</p>
<h5>Markup</h5>
<pre><code>&lt;fieldset&gt;
&lt;legend&gt;Rating&lt;/legend&gt;
&lt;label&gt;&lt;img src=&quot;img/small-sprite.gif&quot;  width=&quot;0&quot; height=&quot;1&quot; alt=&quot;stars&quot;&gt;&lt;input type=&quot;radio&quot; name=&quot;movie&quot; value=&quot;1_5&quot;&gt; 1/5&lt;/label&gt;&lt;br&gt;
&lt;label&gt;&lt;img src=&quot;img/small-sprite.gif&quot;  width=&quot;0&quot; height=&quot;1&quot; alt=&quot;stars&quot;&gt;&lt;input type=&quot;radio&quot; name=&quot;movie&quot; value=&quot;2_5&quot;&gt; 2/5&lt;/label&gt;&lt;br&gt;
&lt;label&gt;&lt;img src=&quot;img/small-sprite.gif&quot;  width=&quot;0&quot; height=&quot;1&quot; alt=&quot;stars&quot;&gt;&lt;input type=&quot;radio&quot; name=&quot;movie&quot; value=&quot;3_5&quot;&gt; 3/5&lt;/label&gt;&lt;br&gt;
&lt;label <ins>class=&quot;no_star&quot;</ins>&gt;&lt;img src=&quot;img/small-sprite.gif&quot;  width=&quot;0&quot; height=&quot;1&quot; alt=&quot;stars&quot;&gt;&lt;input type=&quot;radio&quot; name=&quot;movie&quot; value=&quot;4_5&quot;&gt; 4/5&lt;/label&gt;&lt;br&gt;
&lt;label <ins>class=&quot;no_star&quot;</ins>&gt;&lt;img src=&quot;img/small-sprite.gif&quot;  width=&quot;0&quot; height=&quot;1&quot; alt=&quot;stars&quot;&gt;&lt;input type=&quot;radio&quot; name=&quot;movie&quot; value=&quot;5_5&quot;&gt; 5/5&lt;/label&gt;
&lt;/fieldset&gt;</code></pre>
<h5>CSS</h5>
<pre><code>br {
  display:none;
}
label {
  position:relative;
  height:1.4em;
  width:1.4em;
  overflow:hidden;
  float:left;
}
img {
  width:2.8em;
  height:1.4em;
  position:absolute;
  top:0;
  left:0;
}
<ins>.no_star img {
</ins>  <ins>left:-1.4em;
}</ins></code></pre>
<h5>Result</h5>
<form class="tjk-form" id="tjk-cinq" name="tjk-cinq" action="" method="post">
<fieldset>
<legend>Rating</legend>
<p><label><img src="http://yuiblog.com/assets/starTutorial/small-sprite.gif"  width="0" height="1" alt="stars"><br />
<input type="radio" name="movie" value="1_5"> 1/5</label><br />
<label><img src="http://yuiblog.com/assets/starTutorial/small-sprite.gif"  width="0" height="1" alt="stars"><br />
<input type="radio" name="movie" value="2_5"> 2/5</label><br />
<label><img src="http://yuiblog.com/assets/starTutorial/small-sprite.gif"  width="0" height="1" alt="stars"><br />
<input type="radio" name="movie" value="3_5"> 3/5</label><br />
<label class="no_star"><img src="http://yuiblog.com/assets/starTutorial/small-sprite.gif"  width="0" height="1" alt="stars"><br />
<input type="radio" name="movie" value="4_5"> 4/5</label><br />
<label class="no_star"><img src="http://yuiblog.com/assets/starTutorial/small-sprite.gif"  width="0" height="1" alt="stars"><br />
<input type="radio" name="movie" value="5_5"> 5/5</label><br />
</fieldset>
</form>
</form>
<h4>Not relying on image alone to display information</h4>
<p>It&#8217;s important to offer an alternative to the display of stars in case images are not available. This is because  labels and  radio buttons are styled to be on top of each other. A simple solution is to move <code>input</code> and text off-screen (i.e. using <code>text-indent:-999em</code>) and apply a background color to the labels.</p>
<h5>Markup</h5>
<p>No change</p>
<h5>CSS</h5>
<pre><code>br {
  display:none;
}
label {
  position:relative;
  height:1.4em;
  width:1.4em;
  overflow:hidden;
  float:left;
  <ins>background:teal;
</ins>  <ins>margin-right:1px;
</ins>  <ins>text-indent:-999em;</ins>
}
img {
  width:2.8em;
  height:1.4em;
  position:absolute;
  top:0;
  left:0;
}
<ins>.no_star {
</ins>  <ins>background:#ccc;
}</ins>
.no_star img {
  left:-1.4em;
}</code></pre>
<p><em>Note</em>:</p>
<ul>
<li><code>text-indent</code> also fixes a upwards jump of the image each time the  controls get focus.</li>
<li>the right margin is to make sure background colors create squares and not rectangles (which would happen with adjacent labels  sharing the same background color).</li>
</ul>
<h5>Result</h5>
<form class="tjk-form" id="tjk-six" name="tjk-six" action="" method="post">
<fieldset>
<legend>Rating</legend>
<p><label><img src="http://yuiblog.com/assets/starTutorial/small-sprite.gif"  width="0" height="1" alt="stars"><br />
<input type="radio" name="movie" value="1_5"> 1/5</label><br />
<label><img src="http://yuiblog.com/assets/starTutorial/small-sprite.gif"  width="0" height="1" alt="stars"><br />
<input type="radio" name="movie" value="2_5"> 2/5</label><br />
<label><img src="http://yuiblog.com/assets/starTutorial/small-sprite.gif"  width="0" height="1" alt="stars"><br />
<input type="radio" name="movie" value="3_5"> 3/5</label><br />
<label class="no_star"><img src="http://yuiblog.com/assets/starTutorial/small-sprite.gif"  width="0" height="1" alt="stars"><br />
<input type="radio" name="movie" value="4_5"> 4/5</label><br />
<label class="no_star"><img src="http://yuiblog.com/assets/starTutorial/small-sprite.gif"  width="0" height="1" alt="stars"><br />
<input type="radio" name="movie" value="5_5"> 5/5</label><br />
</fieldset>
</form>
<h4>Finishing touch</h4>
<ul>
<li>We use the pseudo-class <code>:hover</code> to create some rollover effect,</li>
<li>We hide the fieldset border,</li>
<li>We hide the legend,</li>
<li>We style the cursor.</li>
</ul>
<h5>Markup</h5>
<p>Unchanged</p>
<h5>CSS</h5>
<pre><code>br {
  display:none;
}
label {
  position:relative;
  height:1.4em;
  width:1.4em;
  overflow:hidden;
  float:left;
  background:teal;
  margin-right:1px;
  text-indent:-999em;
}
input {
  position:absolute;
  left:-999em;
  top:.5em;
}
img {
  width:2.8em;
  height:1.4em;
  position:absolute;
  top:0;
  left:0;
  <ins>cursor: pointer;</ins>
}
.no_star {
  background:#ccc;
}
.no_star img {
  left:-1.4em;
}
<ins>label:hover {
</ins>  <ins>opacity:.5;
</ins>  <ins>filter:alpha(opacity=50);
}
fieldset {
</ins>  <ins>border:0;
}
legend {
</ins>  <ins>text-indent:-999em;
}</ins></code></pre>
<p><em>Note</em>: <code>label:hover</code> is ignored by IE6 and in Opera the background color bleeds through the images. In the <a href="http://tjkdesign.com/lab/stars/demo.html">demo page</a>, instead of using <code>opacity</code>, I am using a <a href="http://yuiblog.com/assets/starTutorial/star-sprite.gif">different sprite</a> that shows four states.</p>
<h4>Result</h4>
<form class="tjk-form" id="tjk-sept" name="tjk-sept" action="" method="post">
<fieldset>
<legend>Rating</legend>
<p><label><img src="http://yuiblog.com/assets/starTutorial/small-sprite.gif"  width="0" height="1" alt="stars"><br />
<input type="radio" name="movie" value="1_5"> 1/5</label><br />
<label><img src="http://yuiblog.com/assets/starTutorial/small-sprite.gif"  width="0" height="1" alt="stars"><br />
<input type="radio" name="movie" value="2_5"> 2/5</label><br />
<label><img src="http://yuiblog.com/assets/starTutorial/small-sprite.gif"  width="0" height="1" alt="stars"><br />
<input type="radio" name="movie" value="3_5"> 3/5</label><br />
<label class="no_star"><img src="http://yuiblog.com/assets/starTutorial/small-sprite.gif"  width="0" height="1" alt="stars"><br />
<input type="radio" name="movie" value="4_5"> 4/5</label><br />
<label class="no_star"><img src="http://yuiblog.com/assets/starTutorial/small-sprite.gif"  width="0" height="1" alt="stars"><br />
<input type="radio" name="movie" value="5_5"> 5/5</label><br />
</fieldset>
</form>
<h3>Displaying the ratings without allowing user interaction</h3>
<p>We can make the ratings &quot;read-only&quot; by adding <code>disabled</code> and <code>checked</code> attributes in the appropriate <code>input</code> fields. </p>
<h4>Markup</h4>
<pre><code>&lt;fieldset&gt;
  &lt;legend&gt;Rating&lt;/legend&gt;
  &lt;label&gt;&lt;img src=&quot;img/small-sprite.gif&quot;  width=&quot;0&quot; height=&quot;1&quot; alt=&quot;stars&quot;&gt;&lt;input type=&quot;radio&quot; name=&quot;movie&quot; value=&quot;1_5&quot; <ins>disabled</ins>&gt; 1/5&lt;/label&gt;&lt;br&gt;
  &lt;label&gt;&lt;img src=&quot;img/small-sprite.gif&quot;  width=&quot;0&quot; height=&quot;1&quot; alt=&quot;stars&quot;&gt;&lt;input type=&quot;radio&quot; name=&quot;movie&quot; value=&quot;2_5&quot; <ins>disabled</ins>&gt; 2/5&lt;/label&gt;&lt;br&gt;
  &lt;label&gt;&lt;img src=&quot;img/small-sprite.gif&quot;  width=&quot;0&quot; height=&quot;1&quot; alt=&quot;stars&quot;&gt;&lt;input type=&quot;radio&quot; name=&quot;movie&quot; value=&quot;3_5&quot; checked=&quot;<ins>checked</ins>&quot;&gt; 3/5&lt;/label&gt;&lt;br&gt;
  &lt;label class=&quot;no_star&quot;&gt;&lt;img src=&quot;img/small-sprite.gif&quot;  width=&quot;0&quot; height=&quot;1&quot; alt=&quot;stars&quot;&gt;&lt;input type=&quot;radio&quot; name=&quot;movie&quot; value=&quot;4_5&quot; <ins>disabled</ins>&gt; 4/5&lt;/label&gt;&lt;br&gt;
  &lt;label class=&quot;no_star&quot;&gt;&lt;img src=&quot;img/small-sprite.gif&quot;  width=&quot;0&quot; height=&quot;1&quot; alt=&quot;stars&quot;&gt;&lt;input type=&quot;radio&quot; name=&quot;movie&quot; value=&quot;5_5&quot; <ins>disabled</ins>&gt; 5/5&lt;/label&gt;
&lt;/fieldset&gt; </code></pre>
<h4>CSS</h4>
<p>The rule using <code>:hover</code> has been removed</p>
<p>h4>Result</h4>
<form class="tjk-form" id="tjk-huit" name="tjk-huit" action="" method="post">
<fieldset>
<legend>Rating</legend>
<p><label><img src="http://yuiblog.com/assets/starTutorial/small-sprite.gif"  width="0" height="1" alt="stars"><br />
<input type="radio" name="movie" value="1_5" disabled> 1/5</label><br />
<label><img src="http://yuiblog.com/assets/starTutorial/small-sprite.gif"  width="0" height="1" alt="stars"><br />
<input type="radio" name="movie" value="2_5" disabled> 2/5</label><br />
<label><img src="http://yuiblog.com/assets/starTutorial/small-sprite.gif"  width="0" height="1" alt="stars"><br />
<input type="radio" name="movie" value="3_5" checked="checked"> 3/5</label><br />
<label class="no_star"><img src="http://yuiblog.com/assets/starTutorial/small-sprite.gif"  width="0" height="1" alt="stars"><br />
<input type="radio" name="movie" value="4_5" disabled> 4/5</label><br />
<label class="no_star"><img src="http://yuiblog.com/assets/starTutorial/small-sprite.gif"  width="0" height="1" alt="stars"><br />
<input type="radio" name="movie" value="5_5" disabled> 5/5</label><br />
</fieldset>
</form>
<h3>Giving more thought to the process</h3>
<p>At this point, it is possible to cast votes without script support, but sighted users have no clue about their selection. So we use JavaScript to:</p>
<ul>
<li>give feedback to the user regarding his selection,</li>
<li>give keyboard users a visual clue while they navigate through the radio buttons. </li>
</ul>
<p>At the same time, we take advantage of using a script to insert <code>title</code> attributes that will create &quot;tooltips&quot; when users hover over the labels/stars.</p>
<p>Because of  the lack of feedback regarding selection without JavaScript, we style labels and form controls <em>only</em> if there is script support. To do so we use JavaScript to set a flag on the <code>html</code> element and then we create a rule based on descendant selectors containing that hook. If the flag is missing, that rule does not apply and elements are <strong>not</strong> styled.</p>
<p class="next">This is the <a href="http://tjkdesign.com/lab/stars/demo.html">demo page</a>, the final product.  To see how this solution behaves according to various settings, you may want to use your favorite developer tools to increase text-size, break image paths, disable JavaScript, turn CSS off, and more&#8230;</p>
<h3>Wrap up</h3>
<p>Coming up with a &quot;acceptable&quot; solution requires  to identify  users&#8217; needs, User Agents&#8217; peculiarities, User Agents&#8217; settings and  more &#8211; which means extensive testing. </p>
<p>In this process, users&#8217; feedback is essential because  following best practices is not always a sure thing. For example, as mentioned earlier, setting no value for the <code>alt</code> attribute of the images within the labels seem to be the safe thing to do, but it turns out that it creates issues with at least two screenreaders (see <a title="TITLE and ALT create issues in JAWS and NVDA" href="http://public.yahoo.com/~thierryk/test-case/implicit-labeling-and-attributes.html">test case</a>). </p>
<p>Also, feedback from assistive devices&#8217; users allows to ignore some validation error messages &#8211; as the one that the Firefox Accessibility Toolbar   reports (according to <a href="http://bestpractices.cita.uiuc.edu/html/nav/form/">http://bestpractices.cita.uiuc.edu/html/nav/form/</a>). </p>
<p>The goal here was not to fix everything, though. Being able to cast votes without a pointing device was one of my priorities, but  improving the look and feel of the solution in Opera when images are disabled is not something I consider essential.</p>
<p>The most interesting part of this &quot;journey&quot; was to make the solution accessible to many users under various conditions, addressing issues such as:</p>
<ul>
<li>images off,</li>
<li>javascript off,</li>
<li> CSS off,</li>
<li>a combination of the above.</li>
</ul>
<p>It is also nice to know that this technique relies on <code>img</code> elements rather than background images, which allows the stars to:</p>
<ul>
<li> resize themselves according to the user&#8217;s settings, </li>
<li>show in high contrast mode,</li>
<li>be printed by default (unlike background images). </li>
</ul>
<p>All of this comes without sacrificing performance, as this solution relies on this single sprite: <img alt="stars" src="http://yuiblog.com/assets/starTutorial/star-sprite.gif"></p>
<h3>Late finding</h3>
<p>I recently discovered the system Amazon has built for its voting page. It is quite interesting as they serve a <em>different</em> solution depending on script support. If there is script support, they use an image <code>&lt;map&gt;</code> (interesting approach), if there is no script support they use <strong>radio buttons</strong>. In both cases, the solution is <em>accessible</em> to keyboard users, and this helps to maximize access to a feature that is a core differentiator for the Amazon platform.</p>
<p>Note that they do not use JavaScript to <em>replace</em> the radio buttons with a image <code>&lt;map&gt;</code>; instead, they use <code>noscript</code> elements in which table markup contains radio buttons.</p>
<h3>&quot;Out of the box&quot; solutions</h3>
<dl>
<dt>Dreamweaver<sup>&reg;</sup></dt>
<dd><a href="http://labs.adobe.com/technologies/spry/samples/rating/RatingSample.html">Spry Rating Widget</a></dd>
<dt>YUI</dt>
<dd><a href="http://www.unessa.net/en/hoyci/projects/yui-star-rating/">Star Rating Script for YUI</a></dd>
<dd><a href="http://www.devseo.co.uk/blog/view/star-rating-script-with-yui">Star Rating script with YUI</a></dd>
<dt>JQuery</dt>
<dd><a href="http://www.javascriptplugins.com/framework/jquery/half-star-rating-plugin/">Half-Star Rating Plugin</a></dd>
<dd><a href="http://www.javascriptplugins.com/framework/jquery/jquery-ajax-rater/">jQuery Ajax Rater</a></dd>
<dd><a href="http://www.javascriptplugins.com/framework/jquery/simple-star-rating-system/">Simple Star Rating System</a></dd>
<dd><a href="http://webtint.net/tutorials/5-star-rating-system-in-php-mysql-and-jquery/">5 star rating system in PHP, MySQL and jQuery</a></dd>
<dt>Wordpress</dt>
<dd><a href="http://www.blogperfume.com/plugin-gd-star-rating-system-for-wordpress/">GD Star Rating System for WordPress</a></dd>
<dd><a href="http://wordpress.org/extend/plugins/gd-star-rating/">GD Star Rating</a></dd>
<dd><a href="http://www.channel-ai.com/blog/plugins/star-rating/">Star Rating for Reviews</a></dd>
<dt>Flash</dt>
<dd><a href="http://www.adriantnt.com/products/rating_system/?gclid=CL3Vm--7oKECFSCjiQodwxTvyA">5 Star rating system component</a></dd>
<dt>Misc.</dt>
<dd><a href="http://nofunc.org/AJAX_Star_Rating/">How a star rating should be</a></dd>
<dd><a href="http://www.duarte.com/starry/">Starry widget 2</a></dd>
</dl>
<h3>Special thanks</h3>
<p>Special thanks to Victor Tsaran and Todd Kloots for their valuable feedback.</p>
<div class="feedflare">
<a href="http://feeds.yuiblog.com/~ff/YahooUserInterfaceBlog?a=Vsez1L425Ak:GRdtN_EdkN4:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/YahooUserInterfaceBlog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.yuiblog.com/~ff/YahooUserInterfaceBlog?a=Vsez1L425Ak:GRdtN_EdkN4:dnMXMwOfBR0"><img src="http://feeds.feedburner.com/~ff/YahooUserInterfaceBlog?d=dnMXMwOfBR0" border="0"></img></a> <a href="http://feeds.yuiblog.com/~ff/YahooUserInterfaceBlog?a=Vsez1L425Ak:GRdtN_EdkN4:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/YahooUserInterfaceBlog?i=Vsez1L425Ak:GRdtN_EdkN4:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.yuiblog.com/~ff/YahooUserInterfaceBlog?a=Vsez1L425Ak:GRdtN_EdkN4:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/YahooUserInterfaceBlog?i=Vsez1L425Ak:GRdtN_EdkN4:F7zBnMyn0Lo" border="0"></img></a>
</div>]]></content:encoded>
			<wfw:commentRss>http://www.yuiblog.com/blog/2010/08/24/developing-an-accessible-star-ratings-widget/feed/</wfw:commentRss>
		<slash:comments>13</slash:comments>
		<feedburner:origLink>http://www.yuiblog.com/blog/2010/08/24/developing-an-accessible-star-ratings-widget/</feedburner:origLink></item>
		<item>
		<title>Announcing YUI 3.1.2: Critical Security Update for All YUI 3.1.x/3.2.0pr1 Users</title>
		<link>http://feeds.yuiblog.com/~r/YahooUserInterfaceBlog/~3/o9pRUm0CrHU/</link>
		<comments>http://www.yuiblog.com/blog/2010/08/19/yui-3-1-2/#comments</comments>
		<pubDate>Thu, 19 Aug 2010 20:35:17 +0000</pubDate>
		<dc:creator>Eric Miraglia</dc:creator>
				<category><![CDATA[Development]]></category>

		<guid isPermaLink="false">http://www.yuiblog.com/blog/?p=2487</guid>
		<description><![CDATA[The YUI team released YUI 3.1.2 today.  This is an important security update for all users of YUI 3.1.x and 3.2.0pr1.  If you are hosting YUI 3.1.x or 3.2.0pr1 on your site, or if you use YUI 3.1.x/3.2.0pr1 IO&#8217;s cross-domain functionality, you are affected.
XDR in YUI&#8217;s IO utility implements a Flash transport as [...]]]></description>
			<content:encoded><![CDATA[<p>The YUI team <a href="http://yuilibrary.com/downloads/">released YUI 3.1.2</a> today.  <strong>This is an important security update for all users of YUI 3.1.x and 3.2.0pr1</strong>.  If you are hosting YUI 3.1.x or 3.2.0pr1 on your site, or if you use YUI 3.1.x/3.2.0pr1 IO&#8217;s cross-domain functionality, you are affected.</a></p>
<p>XDR in YUI&#8217;s IO utility implements a Flash transport as a fallback for browsers that don&#8217;t support native XDR.  An error in our implementation of the Flash fallback in YUI versions 3.1.x and 3.2.0pr1 allows the <code>io.swf</code> file to operate unsafely whether served from the Yahoo! CDN or from your own server.  The remedy for this problem is twofold:</p>
<ul>
<li>If you have deployed the full YUI 3.1.x/3.2.0pr1 build directory to your server, replace <code>build/io/io.swf</code> in the affected version with the version included in YUI 3.1.2.  <strong>Do so whether or not you are using the IO utility or its XDR feature.</strong></li>
<li>If you are using IO&#8217;s XDR feature, upgrading to the 3.1.2 version of <code>io-swf</code> addresses the security problem.  Host version 3.1.2 of <code>io.swf</code> on your own server (this file cannot operate safely from a CDN; it is not included on the CDN as of 3.1.2).  If you have been drawing <code>io.swf</code> from <code>http://yui.yahooapis.com</code>, remove this domain from your <code>crossdomain.xml</code> file.</li>
</ul>
<p>More details about this issue can be found in <a href="http://developer.yahoo.com/yui/3/io/#security">the IO utility documentation</a>.</p>
<div class="feedflare">
<a href="http://feeds.yuiblog.com/~ff/YahooUserInterfaceBlog?a=o9pRUm0CrHU:-GIv15cqMAo:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/YahooUserInterfaceBlog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.yuiblog.com/~ff/YahooUserInterfaceBlog?a=o9pRUm0CrHU:-GIv15cqMAo:dnMXMwOfBR0"><img src="http://feeds.feedburner.com/~ff/YahooUserInterfaceBlog?d=dnMXMwOfBR0" border="0"></img></a> <a href="http://feeds.yuiblog.com/~ff/YahooUserInterfaceBlog?a=o9pRUm0CrHU:-GIv15cqMAo:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/YahooUserInterfaceBlog?i=o9pRUm0CrHU:-GIv15cqMAo:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.yuiblog.com/~ff/YahooUserInterfaceBlog?a=o9pRUm0CrHU:-GIv15cqMAo:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/YahooUserInterfaceBlog?i=o9pRUm0CrHU:-GIv15cqMAo:F7zBnMyn0Lo" border="0"></img></a>
</div>]]></content:encoded>
			<wfw:commentRss>http://www.yuiblog.com/blog/2010/08/19/yui-3-1-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.yuiblog.com/blog/2010/08/19/yui-3-1-2/</feedburner:origLink></item>
		<item>
		<title>Quick Edit mode for YUI 2 DataTable</title>
		<link>http://feeds.yuiblog.com/~r/YahooUserInterfaceBlog/~3/xAaVIlfcA-k/</link>
		<comments>http://www.yuiblog.com/blog/2010/08/19/quickedit-datatable/#comments</comments>
		<pubDate>Thu, 19 Aug 2010 16:42:03 +0000</pubDate>
		<dc:creator>John Lindal</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[datatable]]></category>
		<category><![CDATA[inline editing]]></category>
		<category><![CDATA[jafl]]></category>
		<category><![CDATA[yui 2]]></category>

		<guid isPermaLink="false">http://www.yuiblog.com/blog/?p=2466</guid>
		<description><![CDATA[YUI 2 DataTable provides slick inline editing.  When disableBtns is turned on in the column configuration, editing simple values like strings or numbers feels just like Excel.  However, the experience cannot be as responsive as a desktop application because each change typically requires an XHR call to the server to store (or reject!) [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://developer.yahoo.com/yui/datatable/">YUI 2 DataTable</a> provides slick inline editing.  When <code>disableBtns</code> is turned on in the column configuration, editing simple values like strings or numbers feels just like Excel.  However, the experience cannot be as responsive as a desktop application because each change typically requires an XHR call to the server to store (or reject!) the new value.  If the user needs to change many of the displayed values, it can be a slow and frustrating experience.  To solve this, I developed QuickEditDataTable.  This extends DataTable to add <q>Quick Edit</q> mode, which allows all editable values to be changed in one bulk operation:</p>
<p><a href="http://jafl.github.com/yui2/quickedit/"><img src="http://jafl.github.com/yui2/quickedit/example.png"></a></p>
<p>(<a href="http://jafl.github.com/yui2/quickedit/" title="YUI 2 Quick Edit Example">Click the screenshot to play with this example</a>.)</p>
<h3>Overview</h3>
<p>To enter Quick Edit mode, call <code>startQuickEdit()</code>.  To exit Quick Edit mode, call <code>cancelQuickEdit()</code>.</p>
<p>It is your responsibility to save the changes before calling <code>cancelQuickEdit()</code>.  To simplify this task, QuickEditDataTable provides <code>getQuickEditChanges()</code>.  This returns an array of objects, one for each row.  Each object contains only the values that were changed in that row, keyed off the column id&#8217;s.  For example, if the table has 4 columns (title, author, year, quantity), and the user only changed the quantity in one row to 20, then the object for that row would be <code>{quantity:20}</code>. The other values would be omitted.</p>
<p>QuickEditDataTable can easily extend <code>YAHOO.widget.ScrollingDataTable</code> if you need that functionality.  If you need this, simply make a copy of the source file and change the base class.</p>
<h3>Configuration</h3>
<p>Quick Edit is a modal state in which the cell formatters for editable columns are swapped out and replaced with special formatters that generate <code>input</code>, <code>textarea</code>, or <code>select</code> elements.  Only columns that have <code>quickEdit</code> configuration will be editable.  The configuration options are:</p>
<dl>
<dt><code>copyDown</code></dt>
<dd>
<p style="margin:0">If true, the top cell in the column will have a button to copy the value down to the rest of the rows.</p>
</dd>
<dt><code>formatter</code></dt>
<dd>
<p style="margin:0">The cell formatter which will render an appropriate form field: &lt;input type=&#8221;text&#8221;&gt;, &lt;textarea&gt;, or &lt;select&gt;.  By default, the cell formatter <code>YAHOO.widget.QuickEditDataTable.textQuickEditFormatter</code> is used for all cells to produce input elements.  To get a <code>textarea</code> element, configure a column to use <code>YAHOO.widget.QuickEditDataTable.textareaQuickEditFormatter</code> instead.  You can also write a custom quick edit formatter &#8212; see below.</p>
</dd>
<dt><code>validation</code></dt>
<dd>
<p style="margin:0">Validation configuration for every field in the column.</p>
<dl>
<dt><code>css</code></dt>
<dd>
<p style="margin:0">CSS classes encoding basic validation rules:</p>
<dl>
<dt><code>yiv-required</code></dt>
<dd>
<p style="margin:0">Value must not be empty.</p>
<dt><code>yiv-length:[x,y]</code></dt>
<dd>
<p style="margin:0">String must be at least <code>x</code> characters and at most <code>y</code> characters. At least one of x and y must be specified.</p>
<dt><code>yiv-integer:[x,y]</code></dt>
<dd>
<p style="margin:0">The integer value must be at least <code>x</code> and at most <code>y</code>. <code>x</code> and <code>y</code> are both optional.</p>
<dt><code>yiv-decimal:[x,y]</code></dt>
<dd>
<p style="margin:0">The decimal value must be at least <code>x</code> and at most <code>y</code>. Exponents are not allowed. <code>x</code> and <code>y</code> are both optional.</p>
</dl>
</dd>
<dt><code>fn</code></dt>
<dd>
<p style="margin:0">A function that will be called with the DataTable as its scope and the cell&#8217;s form element as the argument. Return true if the value is valid. Otherwise, call <code>this.displayQuickEditMessage(...)</code> to display an error and then return false.</p>
</dd>
<dt><code>msg</code></dt>
<dd>
<p style="margin:0">A map of types to messages that will be displayed when a basic or regex validation rule fails. The valid types are: <code>required</code>, <code>min_length</code>, <code>max_length</code>, <code>integer</code>, <code>decimal</code>, and <code>regex</code>.  There is no default for type <code>regex</code>, so you must specify a message if you configure a regex validation.  The default error messages for the other types are stored in <code>YAHOO.widget.QuickEditDataTable.Strings</code> and can be overridden and/or localized.</p>
</dd>
<dt><code>regex</code></dt>
<dd>
<p style="margin:0">Regular expression that the value must satisfy in order to be considered valid.</p>
</dd>
</dl>
</dd>
</dl>
<p>Sometimes, a non-editable column must be rendered differently during Quick Edit mode.  The best example is a column containing a link, since navigating away from the page while in Quick Edit mode can be disastrous.  To remove the link during Quick Edit, configure <code>qeFormatter</code> for the column to be <code>YAHOO.widget.QuickEditDataTable.readonlyLinkQuickEditFormatter</code>.  For email addresses, use <code>YAHOO.widget.QuickEditDataTable.readonlyEmailQuickEditFormatter</code>.  You can also write you own custom, read-only formatter.  Simply follow the normal rules for constructing a DataTable cell formatter.</p>
<h3>Custom Quick Edit Formatters</h3>
<p>To write a custom cell formatter for QuickEdit mode, you must structure the function as follows:</p>
<pre>
function myQuickEditFormatter(el, oRecord, oColumn, oData) {
  var markup =
    '&lt;input type="text" class="{yiv} yui-quick-edit yui-quick-edit-key:{key}"/&gt;' +
    YAHOO.widget.QuickEditDataTable.MARKUP_QE_ERROR_DISPLAY;
    el.innerHTML = lang.substitute(markup, {
      key: oColumn.key,
      yiv: oColumn.quickEdit.validation ? (oColumn.quickEdit.validation.css || '') : ''
    });
    el.firstChild.value = extractMyEditableValue(oData);
    YAHOO.widget.QuickEditDataTable.copyDownFormatter.apply(this, arguments);
};
</pre>
<p>You can use <code>textarea</code> or <code>select</code> instead of <code>input</code>, but you can only create a single field.</p>
<p><code>extractMyEditableValue()</code> does not have to be a separate function nor must it be limited to using only <code>oData</code>. The work should normally be done inline in the formatter function, but the name of the sample function makes the point clear.</p>
<p class="authorbox"><em><strong>About the author:</strong> <a href="http://jafl.my.speedingbits.com/blog/">John Lindal</a> (<a href="http://twitter.com/jafl5272/">@jafl5272</a> on Twitter) is one of the lead engineers constructing the foundation on which <a href="http://apt.yahoo.com/">Yahoo! APT</a> is built.  Previously, he worked on the Yahoo! Publisher Network.</em></p>
<div class="feedflare">
<a href="http://feeds.yuiblog.com/~ff/YahooUserInterfaceBlog?a=xAaVIlfcA-k:U6ZlguiGM-M:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/YahooUserInterfaceBlog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.yuiblog.com/~ff/YahooUserInterfaceBlog?a=xAaVIlfcA-k:U6ZlguiGM-M:dnMXMwOfBR0"><img src="http://feeds.feedburner.com/~ff/YahooUserInterfaceBlog?d=dnMXMwOfBR0" border="0"></img></a> <a href="http://feeds.yuiblog.com/~ff/YahooUserInterfaceBlog?a=xAaVIlfcA-k:U6ZlguiGM-M:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/YahooUserInterfaceBlog?i=xAaVIlfcA-k:U6ZlguiGM-M:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.yuiblog.com/~ff/YahooUserInterfaceBlog?a=xAaVIlfcA-k:U6ZlguiGM-M:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/YahooUserInterfaceBlog?i=xAaVIlfcA-k:U6ZlguiGM-M:F7zBnMyn0Lo" border="0"></img></a>
</div>]]></content:encoded>
			<wfw:commentRss>http://www.yuiblog.com/blog/2010/08/19/quickedit-datatable/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		<feedburner:origLink>http://www.yuiblog.com/blog/2010/08/19/quickedit-datatable/</feedburner:origLink></item>
		<item>
		<title>Using YUI 2 on the DuckDuckGo Search Engine</title>
		<link>http://feeds.yuiblog.com/~r/YahooUserInterfaceBlog/~3/E34Y4BlEX4g/</link>
		<comments>http://www.yuiblog.com/blog/2010/08/19/yui-at-duckduckgo/#comments</comments>
		<pubDate>Thu, 19 Aug 2010 13:41:07 +0000</pubDate>
		<dc:creator>Gabriel Weinberg</dc:creator>
				<category><![CDATA[YUI Implementations]]></category>

		<guid isPermaLink="false">http://www.yuiblog.com/blog/?p=2459</guid>
		<description><![CDATA[
DuckDuckGo is a search engine that uses YUI extensively. Here&#8217;s what it uses in particular:


ImageLoader. Matt Mlinac&#8217;s YUI 2 ImageLoader was the first thing I implemented and what originally hooked me on YUI for this project. DuckDuckGo has favicons next to results and often has &#8220;Zero-click Info&#8221; above results that usually includes an image. I [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://duckduckgo.com/?q=yui+library"><img src="http://yuiblog.com/assets/duckduckgo-20100817-115307.jpg"></a></p>
<p><a href="http://duckduckgo.com/">DuckDuckGo</a> is a search engine that uses YUI extensively. Here&#8217;s what it uses in particular:</p>
<ul>
<li>
<p><i>ImageLoader</i>. Matt Mlinac&#8217;s <a href="http://developer.yahoo.com/yui/imageloader/" title="YUI 2: ImageLoader">YUI 2 ImageLoader</a> was the first thing I implemented and what originally hooked me on YUI for this project. DuckDuckGo has favicons next to results and often has &#8220;Zero-click Info&#8221; above results that usually includes <a href="http://duckduckgo.com/Yahoo!_UI_Library">an image</a>. I didn&#8217;t want these images to compete with the results when loading, as getting results as fast as possible is the ultimate goal.</p>
<p>The ImageLoader Utility handles this well by loading the images after the page load. DDG also auto-loads the next page of results when you scroll down. Sometimes the favicons icons are therefore hidden, and with ImageLoader their load is delayed (sometimes indefinitely) until necessary. To accomplish this, there is a different image group per (internal) page, each with its own custom trigger.</p>
<pre>div.event=new YAHOO.util.CustomEvent('it');
    var ig1=new YAHOO.util.ImageLoader.group(div,'click');
    ig1.addCustomTrigger(div.event);
    div.ig = ig1;
</pre>
<li>
<p><i>Cookie</i>. DuckDuckGo has a lot of <a href="http://duckduckgo.com/settings.html">settings</a>, which are stored via cookies and alternately via  <a href="http://duckduckgo.com/params.html">URL params</a>. When cookies are used, I use Nicholas Zakas&#8217;s <a href="http://developer.yahoo.com/yui/cookie/" title="YUI 2: Cookie Utility">YUI 2 Cookie Utility</a> to easily get and set the values.</p>
<pre>  YAHOO.util.Cookie.set(cookie, value, { expires: new Date("January 12, 2025") });
  x=ki||YAHOO.util.Cookie.get("i");</pre>
<li>
<p><i>StyleSheet</i>. Some DDG settings change the look and feel of the site. These changes are actually accomplished after page load via Luke Smith&#8217;s <a href="http://developer.yahoo.com/yui/stylesheet/" title="YUI 2: StyleSheet Utility">YUI 2 StyleSheet Utility</a>. Some of these changes are straightforward and I can just use the <a href="http://developer.yahoo.com/yui/docs/YAHOO.util.Dom.html#method_setStyle" title="API: dom  YAHOO.util.Dom   (YUI Library)"><code>setStyle</code> Dom function</a>.</p>
<pre>YAHOO.util.Dom.setStyle('b2','display','block');</pre>
<p>Others require actual class changes, which I use the utility to do.</p>
<pre>YAHOO.util.StyleSheet('DDG').set('.ci', {display: "block"}).
            set('.cid', {display: "block"}).
            set('.ci2', {display: "block"}).
            enable();</pre>
<li>
<p><i>Dom</i>. I also use some functions in Matt Sweeney&#8217;s base <a href="http://developer.yahoo.com/yui/dom/" title="YUI 2: Dom Collection">YUI 2 Dom component</a>. I referenced <code>setStyle</code> above, and I also use the related <code>getStyle</code>, <code>addClass</code> and <code>removeClass</code> functions. In addition, I find the <code>getViewportHeight</code>, <code>getViewportWidth</code>, <code>getX</code> and <code>getY</code> functions to be incredibly useful to make things work cross-browser, and now on mobile screens as well.</p>
<li>
<p><i>KeyListener</i>. DDG has a bunch of <a href="http://duckduckgo.com/goodies.html">keyboard shortcuts</a> that let you navigate results without the mouse. I use the <a href="http://developer.yahoo.com/yui/docs/YAHOO.util.KeyListener.html" title="API: event  YAHOO.util.KeyListener   (YUI Library)">YUI 2 KeyListener</a> component to enable these shortcuts.</p>
<pre>kl14 = new YAHOO.util.KeyListener(document, { keys:[70] }, { fn:not } );kl14.enable();</pre>
<li>
<p><i>AutoComplete</i>. I&#8217;m currently working on adding search suggestions to the search engine, and will be using Jenny Donnelly&#8217;s <a href="http://developer.yahoo.com/yui/autocomplete/" title="YUI 2: AutoComplete">YUI 2 AutoComplete</a> component for the front-end. I understand that AutoComplete is getting introduced in YUI 3 soon. Everything else I use has already been introduced in YUI 3, though I still use YUI 2. However, I will be exploring the migration to YUI 3 soon.</p>
</ul>
<p class="authorbox"><em><img align="right" hspace="20" vspace="5" src="http://yuiblog.com/assets/gabrielweinberg.jpg"><strong>About the author:</strong> Gabriel Weinberg is the founder of the DuckDuckGo search engine, based out of Valley Forge, PA. Gabriel has been a startup founder for over ten years, and his last company was sold in 2006. Gabriel holds degrees from MIT in Physics and the Technology and Policy Program.</em></p>
<div class="feedflare">
<a href="http://feeds.yuiblog.com/~ff/YahooUserInterfaceBlog?a=E34Y4BlEX4g:nANLq2bCw04:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/YahooUserInterfaceBlog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.yuiblog.com/~ff/YahooUserInterfaceBlog?a=E34Y4BlEX4g:nANLq2bCw04:dnMXMwOfBR0"><img src="http://feeds.feedburner.com/~ff/YahooUserInterfaceBlog?d=dnMXMwOfBR0" border="0"></img></a> <a href="http://feeds.yuiblog.com/~ff/YahooUserInterfaceBlog?a=E34Y4BlEX4g:nANLq2bCw04:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/YahooUserInterfaceBlog?i=E34Y4BlEX4g:nANLq2bCw04:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.yuiblog.com/~ff/YahooUserInterfaceBlog?a=E34Y4BlEX4g:nANLq2bCw04:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/YahooUserInterfaceBlog?i=E34Y4BlEX4g:nANLq2bCw04:F7zBnMyn0Lo" border="0"></img></a>
</div>]]></content:encoded>
			<wfw:commentRss>http://www.yuiblog.com/blog/2010/08/19/yui-at-duckduckgo/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<feedburner:origLink>http://www.yuiblog.com/blog/2010/08/19/yui-at-duckduckgo/</feedburner:origLink></item>
		<item>
		<title>Implementing YUI on the Assembla.com Agile Planner</title>
		<link>http://feeds.yuiblog.com/~r/YahooUserInterfaceBlog/~3/Wjc5Js2GSY0/</link>
		<comments>http://www.yuiblog.com/blog/2010/08/18/yui3-at-assembla/#comments</comments>
		<pubDate>Wed, 18 Aug 2010 14:35:45 +0000</pubDate>
		<dc:creator>Joachim Larsen</dc:creator>
				<category><![CDATA[YUI Implementations]]></category>

		<guid isPermaLink="false">http://www.yuiblog.com/blog/?p=2453</guid>
		<description><![CDATA[
Fast and fun – that was the user requirement for the new Assembla.com Agile Planner – an AJAX interface for adding development tasks, building story/feature outlines, and scheduling them into releases.  We were lucky to have YUI 3 to make it fast and fun to implement as well.
I had used YUI 2 for a [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://blog.assembla.com/assemblablog/tabid/12618/bid/12940/Upgraded-Agile-Planner-for-Fast-Fun-Roadmapping.aspx" title="Upgraded Agile Planner for Fast, Fun Roadmapping"><img src="http://yuiblog.com/assets/assembla-20100818-072550.jpg"></a></p>
<p>Fast and fun – that was the user requirement for <a href="http://blog.assembla.com/assemblablog/tabid/12618/bid/12940/Upgraded-Agile-Planner-for-Fast-Fun-Roadmapping.aspx" title="Upgraded Agile Planner for Fast, Fun Roadmapping">the new Assembla.com Agile Planner</a> – an AJAX interface for adding development tasks, building story/feature outlines, and scheduling them into releases.  We were lucky to have <a href="http://developer.yahoo.com/yui/3/" title="YUI 3 &mdash; Yahoo! User Interface Library">YUI 3</a> to make it fast and fun to implement as well.</p>
<p>I had used <a href="http://developer.yahoo.com/yui/2/" title="YUI 2 &mdash; Yahoo! User Interface Library">YUI 2</a> for a number of prior projects and I had been impressed by the engineering of the UI components and the underlying library infrastructure. I wanted to learn more about YUI 3, with its compact syntax and deeper focus on DOM manipulation and CSS3-style selectors.   This project, with a low dependence on &#8216;prebuilt widgets,&#8217; was a perfect opportunity to get my feet wet with YUI 3.  The facilities for &#8216;large app&#8217; implementation via custom modules and integration with YUI loader made it a natural choice.</p>
<p>The Agile Planner supports a number of drag and drop user interactions with multiple interaction groups and context based behaviors. At the same time, it handles a complex set of interactions with the server, including merging in new data from the server, and propagating changes to the server.</p>
<p>We improved on the existing Planner which was based on Rails handlers and Prototype.js.  YUI&#8217;s sandbox philosophy and strong OOP facilities made coexisting with Prototype.js a breeze.</p>
<p>We used a large number of YUI components, including:</p>
<ul>
<li>Async-Queue to offer a responsive experience on a page that can involve 1000+ simultaneous tickets</li>
<li>Drag and Drop with interaction groups. </li>
<li>IO as a connection manager to queue and massage server interaction.</li>
<li>Event-delegate to allow simply hydrating html templates and forgetting about them.</li>
<li>Event-key for keyboard interaction and navigation.</li>
<li>Collection for giving us a consistent implementation experience across browsers.</li>
<li>Cookie for easy short-term UI persistence. </li>
<li>Profiler to find the biggest speed gains</li>
<li>YUI Doc to leave information for the rest of the team</li>
</ul>
<p>Working with YUI 3 on an app like this has been fun, and I am looking forward to hearing what our users will urge us to do next!</p>
<p class="authorbox"><em><strong>About the author:</strong> Joachim Larsen is a frontend engineer with Assembla.com.</em></p>
<div class="feedflare">
<a href="http://feeds.yuiblog.com/~ff/YahooUserInterfaceBlog?a=Wjc5Js2GSY0:iSPEB-RzAmc:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/YahooUserInterfaceBlog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.yuiblog.com/~ff/YahooUserInterfaceBlog?a=Wjc5Js2GSY0:iSPEB-RzAmc:dnMXMwOfBR0"><img src="http://feeds.feedburner.com/~ff/YahooUserInterfaceBlog?d=dnMXMwOfBR0" border="0"></img></a> <a href="http://feeds.yuiblog.com/~ff/YahooUserInterfaceBlog?a=Wjc5Js2GSY0:iSPEB-RzAmc:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/YahooUserInterfaceBlog?i=Wjc5Js2GSY0:iSPEB-RzAmc:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.yuiblog.com/~ff/YahooUserInterfaceBlog?a=Wjc5Js2GSY0:iSPEB-RzAmc:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/YahooUserInterfaceBlog?i=Wjc5Js2GSY0:iSPEB-RzAmc:F7zBnMyn0Lo" border="0"></img></a>
</div>]]></content:encoded>
			<wfw:commentRss>http://www.yuiblog.com/blog/2010/08/18/yui3-at-assembla/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.yuiblog.com/blog/2010/08/18/yui3-at-assembla/</feedburner:origLink></item>
		<item>
		<title>Ask Satyam: Row Expansion and Nested DataTables</title>
		<link>http://feeds.yuiblog.com/~r/YahooUserInterfaceBlog/~3/_kIw0JdfBYs/</link>
		<comments>http://www.yuiblog.com/blog/2010/08/17/ask-satyam-row-expansion/#comments</comments>
		<pubDate>Tue, 17 Aug 2010 19:06:04 +0000</pubDate>
		<dc:creator>Satyam</dc:creator>
				<category><![CDATA[Development]]></category>

		<guid isPermaLink="false">http://www.yuiblog.com/blog/?p=2446</guid>
		<description><![CDATA[
Satyam (a.k.a Daniel Barreiro) is a long-time YUI contributor and one of the most prolific, generous experts in the YUI forums.  He is also the author of a new book on YUI 2.8.0, YUI 2.8.0: Learning the Library. This article in the &#8220;Ask Satyam&#8221; series was suggested by Alberto Santini and Bryan Kane.  [...]]]></description>
			<content:encoded><![CDATA[<blockquote>
<p><a href="https://www.packtpub.com/yahoo-user-interface-yui-2-8-learning-library/book"><img align="right" src="http://yuiblog.com/assets/satyam-book-small-20100809-120823.jpg" width="106" height="132" hspace=20></a>Satyam (a.k.a Daniel Barreiro) is a long-time YUI contributor and one of the most prolific, generous experts in the <a href="http://yuilibrary.com/forum/" title="YUI Library :: Forums :: Index page">YUI forums</a>.  He is also <a href="https://www.packtpub.com/yahoo-user-interface-yui-2-8-learning-library/book">the author of a new book on YUI 2.8.0, <em>YUI 2.8.0: Learning the Library</em></a>. This article in the &#8220;Ask Satyam&#8221; series <a href="http://www.yuiblog.com/blog/2010/07/29/ask-satyam/#comment-593036" title="Ask Satyam — and Be Eligible for a Free Copy of the New YUI 2.8 Book from Packt &raquo; Yahoo! User Interface Blog (YUIBlog)">was suggested by Alberto Santini</a> and <a href="http://www.yuiblog.com/blog/2010/07/29/ask-satyam/#comment-593069">Bryan Kane</a>.  Satyam will be answering several additional questions in the coming weeks here on YUIBlog as part of the series.</p>
</blockquote>
<p><P>It&#8217;s a common problem in information architecture: You have tabular data, and you have the need to display additional information about the entity represented by each table row. Usually, this is accomplished by &#8220;expanding&#8221; the row on click or selection and showing the data directly below the chosen row. Sometimes this additional data comes in the way of a further DataTable which might also have its rows expandable into more nested levels. We call the basic use case of drilling down into a single row <em>row expansion</em>; when the use case involves further tabular data associated with each row, the feature is described as a <em>nested DataTable.</em></p>
<p>So far there have been several serious attempts to support these features with the <a href="http://developer.yahoo.com/yui/datatable/" title="YUI 2: DataTable">YUI 2 DataTable</a>. DataTable&#8217;s own list of examples includes a <A HREF="http://developer.yahoo.com/yui/examples/datatable/dt_rowexp_basic.html">Row Expansion</A> example by Eric Gelinas. John Lindal presented his <A HREF="http://jafl.github.com/yui2/treeble/">Treeble</A> in a <A HREF="http://www.yuiblog.com/blog/2010/04/14/treeble-using-nested-yui-2-datasources-for-row-expansion/">YUI Blog article</A> some time ago with a <A HREF="http://yuilibrary.com/gallery/show/treeble">version for YUI 3</A> in the Gallery. I also tried with my <A HREF="http://www.satyam.com.ar/yui/#NestedDataTables">NestedDataTable</A> project.</P></p>
<p><P>None of these solutions is completely satisfactory and the question tends to recur; in fact, two people asked for it as part of the <A HREF="http://www.yuiblog.com/blog/2010/07/29/ask-satyam/">Ask Satyam</A> series.  In this article, I&#8217;ll dive in again and explore strategies and options for dealing with row expansion and nested DataTables.</p>
<p><H3>A few issues to consider</H3></p>
<p><P>DataTable makes a couple of very basic assumptions. One is that the ordinal position of a Record corresponds to the same ordinal position of a row within the <CODE>&lt;tbody&gt;</CODE> of the markup. I count 17 occurrences of property <CODE>sectionRowIndex</CODE> which returns the ordinal number of a <CODE>&lt;tr&gt;</CODE> element within its section (that is, within the <CODE>&lt;tbody&gt;</CODE>, <CODE>&lt;thead&gt;</CODE> or <CODE>&lt;tfoot&gt;</CODE> where it is contained). There are also many occurrences where the ordinal position of the Record is used as an index into the array of rows. For example, in the <A HREF="http://developer.yahoo.com/yui/examples/datatable/dt_rowexp_basic_clean.html">Row Expansion</A> example, let it run to the end, expand any row about half-way to the table and then execute the following two statements in the debugging console:</P></p>
<p><PRE>YAHOO.example.Basic.oDT.getTrEl(YAHOO.example.Basic.oDT.getRecord(17)).id<br />
YAHOO.example.Basic.oDT.get<B>Next</B>TrEl(YAHOO.example.Basic.oDT.getRecord(17)).id </PRE></p>
<p><P> Both will show <CODE>yui-rec17</CODE> as the answer, which is not correct since the second statement should return the id of the next row. If you do the same with no rows expanded or using a low number, before the expanded row, the second statement will correctly return <CODE>yui-rec18</CODE>.</P></p>
<p><P>Obviously, all examples listed work in their basic form; the &#8220;bug&#8221; demonstrated above amounts to a trick. If your application stays within safe boundaries, you are fine.  However, a feature added to your application at a later time, one that makes deeper assumptions about how the DataTable&#8217;s internals are operating, may lead to some problems. (For what it&#8217;s worth, DataTable is very robust. Its built-in functionality is pretty safe, it sorts columns, edits cells, selects and highlights fine, as far as I tested it. I wasn&#8217;t able to break its most popular built-in features.)</P></p>
<p><P>Nesting structures within the DataTable also invites some other complications: Clicking somewhere in an expanded custom row might be troublesome, since the click on the nested element would bubble up to the containing DataTable where it can mess things up. You must remember this point when listening to events such as <CODE>cellClickEvent</CODE> on the parent table since the target cell reported might not belong to the parent table but to a nested one — so fetching the Record or Column for that cell may fail.</P></p>
<p><P>Finally, there are some conceptual issues. What does <CODE>getNextTrEl</CODE> mean? Does it include only DataTable&#8217;s own rows (let&#8217;s call them <em>data rows</em>), or should it include custom rows added later, as it does now? I would say it should only include data rows since the custom rows, being nested, are not siblings to the data rows but children and, though the HTML markup forces us to use plain <CODE>&lt;TR&gt;</CODE> elements, conceptually, they are not siblings. Then, if you somehow reach a <CODE>&lt;TR&gt;</CODE> element and ask for its corresponding Record in the containing DataTable using method <CODE>getRecord()</CODE>, if that <CODE>&lt;TR&gt;</CODE> is a custom row, what would be its corresponding Record: <CODE>null</CODE> (since it doesn&#8217;t belong to the containing DataTable but to the nested one) or the Record of the data row it is a child of? </P></p>
<p><P>How do you want the stripes on the rows? Currently, the striping logic takes even and odd rows indiscriminately, whether data rows or custom rows but perhaps you would like the custom rows to carry on the same background color as the data row to which they belong.</P></p>
<p><P>These are some of the aspects of row-expansion functionality that require definition; any decision on them, as long as it is consistent and predictable, would likely accommodate most use cases.</P></p>
<p><H3>Nesting without DataTable</H3></p>
<p><P><a href="http://jafl.github.com/yui3-gallery/treeble/"><img align="right" src="http://yuiblog.com/assets/treebleexample-20100817-095427.jpg" hspace=20 vspace=5></a>Of course, we might live with these restrictions, but then what is the point of bothering with the DataTable at all? If all you want is to display some nested information in a tabular form, you could simply use a regular <CODE>&lt;table&gt;</CODE> element or something like it. In fact, <a href="http://yuilibrary.com/gallery/show/treeble" title="YUI Library :: Gallery :: Treeble">the YUI 3 version of Treeble</a> (<a href="http://jafl.github.com/yui3-gallery/treeble/" title="YUI 3 Treeble Example">example</a>) does exactly that; since there is not yet a YUI 3 version of DataTable to be used, it uses none at all. You might as well do the very same thing with YUI 2, like in <A HREF="http://satyam.com.ar/yui/2.8.0/RowExpansionPlainHTML.html">this example</A>, where no DataTable nor HTML <CODE>&lt;table&gt;</CODE> was used at all. Admittedly, the example is very simple and visually ugly (I&#8217;ve never claimed to be artistic); but, if that is all you care for, at least you know it won&#8217;t fail you. Just an idea you might want to evaluate when you decide on what to do.</P></p>
<p><H3>Row Expansion with DataTable</H3></p>
<p><P><a href="http://developer.yahoo.com/yui/examples/datatable/dt_rowexp_basic.html"><img align="right" src="http://yuiblog.com/assets/rowexpansion-20100817-100641.jpg" hspace=20 vspace=5></a>Finally, you might really want to use a DataTable.  If so, how do we go about that? Both Treeble and my own <A HREF="http://satyam.com.ar/yui/2.8.0/nested1.html">NestedDataTable</A> nest two DataTables. Eric Gelinas, in contrast, has used another approach, which I find more flexible. He does not make any assumptions on what is going to be in the expansion. It uses a <CODE>rowExpansionTemplate</CODE> configuration attribute that can take either a string template or a reference to a function. The string template is processed via <CODE><a href="http://developer.yahoo.com/yui/docs/YAHOO.lang.html#method_substitute" title="API: yahoo  YAHOO.lang   (YUI Library)">YAHOO.lang.substitute</a></CODE> along with the data from the Record object for the row about to be expanded. We already know that we don&#8217;t need to define in the column definitions array all the fields we have read with the DataSource; we can keep extra DataSource fields in reserve for later use. The template mechanism lets us display in the expansion row those other fields for which we didn&#8217;t have space in the regular row. The <A HREF="http://developer.yahoo.com/yui/examples/datatable/dt_rowexp_basic.html">example</A> lists picture names from Flickr, images which might be too big to show in the main DataTable. The fields to assemble the URL pointing to those pictures are loaded from the start. Upon row expansion, those bits are put together to built an <CODE>&lt;img&gt;</CODE> tag and the thumbnail for the picture is shown in the expansion row.</P></p>
<p><P>Now, if we set <CODE>rowExpansionTemplate</CODE> to a function reference, then we get all the flexibility we might possibly want. I used that idea in <A HREF="http://satyam.com.ar/yui/2.8.0/nestedRowExpansion.html">this example</A>, but with a somewhat different <A HREF="http://www.satyam.com.ar/yui/2.8.0/rowexpansion.js">rowexpansion.js</A> file. The original was more focused on using the string template and it assumed that the expansion row could be destroyed and rebuilt at any time at no cost. That is not the case when the expansion is something more complex, such as a DataTable with further DataTables nested within. Every time a column is sorted, DataTable will delete all rows and start anew, which would be tremendously expensive with complex content if it was to be deleted and reconstructed as well. Instead, what I do is to keep a reference to the expansion row in the expansion state object (see description in method <CODE><A HREF="http://www.satyam.com.ar/yui/2.8.0/rowExpansionDocs/YAHOO.widget.RowExpansionDataTable.html#method_getExpansionState">getExpansionState</A></CODE>), which is stored in the Record of the parent table.  This data is not deleted, and as a result it&#8217;s much more efficient to restore that same row whenever the parent row is re-rendered. </P></p>
<p><a href="http://satyam.com.ar/yui/2.8.0/nestedRowExpansion.html" title="Nested DataTables using RowExpansionDataTable"><img src="http://yuiblog.com/assets/nesteddatatables-20100817-101227.jpg"></a></p>
<p><P>Sometimes, however, the parent rows will be deleted on purpose or the parent DataTable reloaded from the server with, possibly, different data which requires the children to be refreshed. I need to explicitly delete the child rows in such cases because otherwise they would remain in memory as zombies. Thus, I override several methods (<CODE>deleteRow</CODE>, <CODE>deleteRows</CODE>, <CODE>initializeTable</CODE>, <CODE>destroy</CODE>) and delete the nested content before its parent Record is deleted. Deleting, however, is not enough, since the content might need some more elaborate means of disposal. As with many complex components, DataTable has a <CODE>destroy</CODE> method which needs to be called to fully clear the instance and its associated events. I added the <CODE>rowExpansionDestroyEvent</CODE> event to signal that the nested row is about to be destroyed, thus allowing the developer to handle the content as required. For example:</P></p>
<p><PRE>albumDT.on(&#8216;rowExpansionDestroyEvent&#8217;, function (state) {<br />
    state[NESTED_DT].destroy();<br />
});</PRE></p>
<p><P>All events receive the <CODE>state</CODE> object as their single argument. Here, I use the reference to the nested DataTable which I stored under a property name of my own (<CODE>NESTED_DT</CODE>) and call its <CODE>destroy</CODE> method.</P></p>
<p><P>RowExpansionDataTable adds the column that triggers row expansion automatically and also listens to clicks on that column. You don&#8217;t need to do anything for that column to show up and be active. It always adds it on the left hand side by using this simple code in the constructor:</P></p>
<p><PRE>var REDT = function(elContainer,aColumnDefs,oDataSource,oConfigs) {</p>
<p>     aColumnDefs.unshift({<br />
          key:ROW_EXPANSION,<br />
          label:&#8221;,<br />
          className:CLASS_TRIGGER<br />
     });<br />
     REDT.superclass.constructor.call(this, elContainer,aColumnDefs,oDataSource,oConfigs);<br />
};</p>
<p>YAHOO.widget.RowExpansionDataTable = REDT;</PRE></p>
<p><P> RowExpansionDataTable also sets a listener for <CODE>cellClickEvent</CODE> and checks if the column clicked is the one with its key set to the value stored in constant <CODE>ROW_EXPANSION</CODE>; there is no need for us to respond to that event, although we may want to set up further listeners for our own purposes on other columns.</P> </p>
<p><P>Though the basis of my <A HREF="http://www.satyam.com.ar/yui/2.8.0/rowexpansion.js">rowexpansion.js</A> file is Eric Gelinas&#8217;s work, there are also many differences. I changed many of the variable, method and property names to conform to the standard naming conventions and fixed plenty of errors flagged by <A HREF="http://www.jslint.com/">JSLint</A>. In its basics, however, the code is still Eric&#8217;s.</P></p>
<p><P>The <A HREF="http://satyam.com.ar/yui/2.8.0/nestedRowExpansion.html">example</A> is well commented and described after the sample table; the <A HREF="http://www.satyam.com.ar/yui/2.8.0/rowexpansion.js">rowexpansion.js</A> file is easy to follow and its <A HREF="http://satyam.com.ar/yui/2.8.0/rowExpansionDocs/YAHOO.widget.RowExpansionDataTable.html">API Docs</A> are also available.</P></p>
<p><H3>Overlaying children</H3></p>
<p><P>Another alternative to adding rows is to make the expansion float above the parent row. I have used this in my own <A HREF="http://satyam.com.ar/yui/2.8.0/nested1.html">NestedDataTable example</A>. This example also uses YQL tables via YQLDataSource and, as I mentioned in the <A HREF="http://www.yuiblog.com/blog/2010/08/09/ask-satyam-yql-and-yui/">previous article</A>, the YQL Artists search table is somewhat clumsy to use along with <a href="http://developer.yahoo.com/yui/autocomplete/" title="YUI 2: AutoComplete">YUI 2 AutoComplete</a> as it won&#8217;t bring back partial matches with artist names.</P></p>
<p><a href="http://satyam.com.ar/yui/2.8.0/nested1.html" title="Nested DataTables"><img src="http://yuiblog.com/assets/nesteddatatables-20100817-104246.jpg"></a></P></p>
<p><P>Here, when you expand a row, a container <CODE>&lt;div&gt;</CODE> is created and appended to the document body. The container uses absolute positioning and is moved to overlap the row right below the one being expanded. The container thus covers the rows next to the one being expanded so, the height of this row is increased to make space for the overlapping container. </P></p>
<p><P>This mechanism spares us from the two issues I mentioned before: no row is added to the DataTable, the ordinal positions of Records and Rows match at all times and events can&#8217;t bubble from the containers to the DataTable since they are not descendants of the DataTable. However, maintaining the position of the containers requires paying attention to several events and recalculating their position. Sorting the main table with several children open will move all the containers to their new positions with ease, and so will resizing the browser window. </P></p>
<p><P>This <A HREF="http://satyam.com.ar/yui/2.8.0/nested1.html">example</A> uses just two levels of nesting while the RowExpansionDataTable example could be expanded to any limit. I have tried to combine the two and I see no theoretical reason why it would not succeed; however, in trying to do so I found the example got so complex that it was hard to describe in a blog article of any reasonable size. The example, as it is, has complete control over the parent and child tables, knows when they expand or contract, move, get redrawn or change in any other way, and can easily access any of them and adjust their layout. In trying to extend this to any number of levels, I found that communicating all those changes up and down the hierarchy of nested components, not all of them necessarily DataTables, was hard, with changes deep in one branch possibly affecting nested components on other branches. </P></p>
<p><P>The complexity lies not so much in communicating those changes up and down and acting on them as in trying to provide standard interfaces for generic child elements to participate. If you have full control over the several containers and their content, it should be manageable; however, it would only make sense to do so if the RowExpansionDataTable fails in some particular application, as I admit it may, and cannot be fixed easily.</P></p>
<p><H3>Conclusion</H3></p>
<p><P>You don&#8217;t always need real DataTables to have tables nested inside each other, but if you do, my RowExpansionDataTable is a good choice, besides being a good example on how to extend DataTable. It has some issues, which I described so that if you bump into them, you know where to look as you work around them. DataTable itself is amazingly robust and handles itself quite nicely. Overlapping content in the DataTable is also a possible solution, if signaling the changes in size and position of the children can be solved efficiently, though it is hard to do in a generic component — and I didn&#8217;t try to do so here. Neither did I try a version inheriting from ScrollingDataTable; it is not that I forgot, but rather that I know it is not a trivial undertaking.</P></p>
<div class="feedflare">
<a href="http://feeds.yuiblog.com/~ff/YahooUserInterfaceBlog?a=_kIw0JdfBYs:7kw0ZhGyEDg:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/YahooUserInterfaceBlog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.yuiblog.com/~ff/YahooUserInterfaceBlog?a=_kIw0JdfBYs:7kw0ZhGyEDg:dnMXMwOfBR0"><img src="http://feeds.feedburner.com/~ff/YahooUserInterfaceBlog?d=dnMXMwOfBR0" border="0"></img></a> <a href="http://feeds.yuiblog.com/~ff/YahooUserInterfaceBlog?a=_kIw0JdfBYs:7kw0ZhGyEDg:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/YahooUserInterfaceBlog?i=_kIw0JdfBYs:7kw0ZhGyEDg:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.yuiblog.com/~ff/YahooUserInterfaceBlog?a=_kIw0JdfBYs:7kw0ZhGyEDg:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/YahooUserInterfaceBlog?i=_kIw0JdfBYs:7kw0ZhGyEDg:F7zBnMyn0Lo" border="0"></img></a>
</div>]]></content:encoded>
			<wfw:commentRss>http://www.yuiblog.com/blog/2010/08/17/ask-satyam-row-expansion/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<feedburner:origLink>http://www.yuiblog.com/blog/2010/08/17/ask-satyam-row-expansion/</feedburner:origLink></item>
		<item>
		<title>YUI Theater — Nicholas Zakas and Victor Tsaran: “Accessibility on the Yahoo Homepage (58 min.)”</title>
		<link>http://feeds.yuiblog.com/~r/YahooUserInterfaceBlog/~3/NHDrRgMoMjs/</link>
		<comments>http://www.yuiblog.com/blog/2010/08/16/video-zakas-tsaran/#comments</comments>
		<pubDate>Mon, 16 Aug 2010 20:53:52 +0000</pubDate>
		<dc:creator>Allen Rabinovich</dc:creator>
				<category><![CDATA[Accessibility]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[YUI Theater]]></category>
		<category><![CDATA[bayjax]]></category>
		<category><![CDATA[nicholas zakas]]></category>
		<category><![CDATA[victor tsaran]]></category>
		<category><![CDATA[yahoo]]></category>

		<guid isPermaLink="false">http://www.yuiblog.com/blog/?p=2425</guid>
		<description><![CDATA[
In June, Yahoo! hosted a BayJax meetup dedicated to web accessibility. Nicholas Zakas, Yahoo! homepage developer, and Victor Tsaran, Yahoo! senior accessibility program manager, gave a talk on the process of making Yahoo!&#8217;s homepage highly accessible.
We apologize for the uneven audio levels: there were technical issues with the microphones that we unfortunately were only able [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://developer.yahoo.com/yui/theater/video.php?v=zakas-tsaran"><img src="http://yuiblog.com/assets/zakas-tsaran-blog-june-10.jpg" alt="Nicholas Zakas and Victor Tsaran's Talk at the BayJax event at Yahoo! in June, 2010." width="510"></a></p>
<p>In June, Yahoo! hosted a <a href="www.meetup.com/BayJax/" target="_blank">BayJax</a> meetup dedicated to web accessibility. Nicholas Zakas, Yahoo! homepage developer, and Victor Tsaran, Yahoo! senior accessibility program manager, gave a talk on the process of making Yahoo!&#8217;s homepage highly accessible.</p>
<p>We apologize for the uneven audio levels: there were technical issues with the microphones that we unfortunately were only able to adjust towards the second half of the talk.</p>
<p>If the video embed below doesn&#8217;t show up correctly in your RSS reader of choice, be sure to <a href="http://developer.yahoo.com/yui/theater/video.php?v=zakas-tsaran">click through to watch the high-resolution version of the video on YUI Theater</a>.</p>
<p>
<div><object width="510" height="287"><param name="movie" value="http://d.yimg.com/m/up/ypp/default/player.swf"></param><param name="flashVars" value="vid=20586117&#038;"></param><param name="allowfullscreen" value="true"></param><param name="wmode" value="transparent"></param><embed width="510" height="287" allowFullScreen="true" src="http://d.yimg.com/m/up/ypp/default/player.swf" type="application/x-shockwave-flash" flashvars="vid=20586117&#038;"></embed></object></div>
</p>
<ul>
<li><a href='http://yui.zenfs.com/theater/zakas-tsaran-hd.mov'>Download HD video (480p ~1.4GB)</a></li>
<li><a href="http://yui.zenfs.com/theater/zakas-tsaran.m4v">Download video (m4v)</a></li>
</ul>
<h3>Other Recent YUI Theater Videos:</h3>
<ul>
<li><a href='http://developer.yahoo.com/yui/theater/video.php?v=lembree-a11y'><strong>Dennis Lembree:</strong> Making JavaScript Accessible</a> &mdash; Dennis Lembree, an accessibility expert and the creator of AccessibleTwitter discusses the challenges of making JS-enabled sites accessible. The talk took place at the June 2010 BayJax meetup at Yahoo.</li>
<div class="feedflare">
<a href="http://feeds.yuiblog.com/~ff/YahooUserInterfaceBlog?a=NHDrRgMoMjs:Kp0Hvrfe5I0:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/YahooUserInterfaceBlog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.yuiblog.com/~ff/YahooUserInterfaceBlog?a=NHDrRgMoMjs:Kp0Hvrfe5I0:dnMXMwOfBR0"><img src="http://feeds.feedburner.com/~ff/YahooUserInterfaceBlog?d=dnMXMwOfBR0" border="0"></img></a> <a href="http://feeds.yuiblog.com/~ff/YahooUserInterfaceBlog?a=NHDrRgMoMjs:Kp0Hvrfe5I0:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/YahooUserInterfaceBlog?i=NHDrRgMoMjs:Kp0Hvrfe5I0:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.yuiblog.com/~ff/YahooUserInterfaceBlog?a=NHDrRgMoMjs:Kp0Hvrfe5I0:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/YahooUserInterfaceBlog?i=NHDrRgMoMjs:Kp0Hvrfe5I0:F7zBnMyn0Lo" border="0"></img></a>
</div>]]></content:encoded>
			<wfw:commentRss>http://www.yuiblog.com/blog/2010/08/16/video-zakas-tsaran/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		<feedburner:origLink>http://www.yuiblog.com/blog/2010/08/16/video-zakas-tsaran/</feedburner:origLink></item>
	</channel>
</rss>
