350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith<style scoped>
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith .yui3-js-enabled .yui3-checkboxes-loading { display: none; }
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith<div class="intro">
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith Using Progressive Enhancement to skin checkboxes with the help of the
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith <a href="../../api/Loader.html">Loader</a>,
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith <a href="../../api/module_classnamemanager.html">ClassNameManager
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith Utility</a>, and the Event Utility's <code>focus</code> and
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith <code>blur</code> events and the <code>delegate</code> method.
819e90d415ed17d59af3a247b2ad9d6feb0c21b5Luke Smith<div class="example yui3-skin-sam">
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith {{>focus-example-source}}
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith<h2>Challenges</h2>
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke SmithThere are a few challenges when trying to skin an HTML checkbox using CSS. To start, most of the
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith<a href="http://developer.yahoo.com/yui/articles/gbs/#a-grade">A-grade browsers</a> don't provide
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smithsupport for CSS properties like <code>border</code> and <code>background</code> on the
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith<code><input type="checkbox"></code> element. Additionally, IE 6 and IE 7 (Quirks Mode)
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smithlack support for attribute selectors — necessary to style the <code>checked</code> and
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith<code>disabled</code> states. Additionally, IE 6 and 7 only support the <code>:focus</code> and
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith<code>:active</code> pseudo classes on <code><a></code> elements, both of which are needed
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smithto style a checkbox when it is focused or depressed.
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith<h2>Approach</h2>
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke SmithDespite the shortcomings in CSS support, with a little extra markup and through the use of
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke SmithJavaScript it is possible to skin an <code><input type="checkbox"></code> element
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smithconsistently well in all of the
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith<a href="http://developer.yahoo.com/yui/articles/gbs/#a-grade">A-grade browsers</a>.
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith<h4>Markup</h4>
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke SmithSince CSS support for the <code><input type="checkbox"></code> element is lacking, wrap
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith<code><input type="checkbox"></code> elements in one or more inline elements to provide the
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smithnecessary hooks for styling. In this example, each <code><input type="checkbox"></code>
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smithelement is wrapped by two <code><span></code>s.
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith <input type="checkbox">
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke SmithTo style each checkbox, a class name of <code>yui3-checkbox</code> will be applied to the
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smithouttermost <code><span></code> wrapping each <code><input type="checkbox"></code>
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smithelement. An additional class will be used to represent the various states of each checkbox. The
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smithclass name for each state will follow a consistent pattern: <code>yui3-checkbox-[state]</code>.
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke SmithFor this example, the following state-based class names will be used:
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith <dt><code>yui3-checkbox-focus</code></dt>
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith <dd>The checkbox has focus</dd>
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith <dt><code>yui3-checkbox-active</code></dt>
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith <dd>The checkbox is active (pressed)</dd>
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith <dt><code>yui3-checkbox-checked</code></dt>
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith <dd>The checkbox is checked</dd>
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke SmithThe styles for each checkbox comes together as follows:
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith.yui3-checkbox {
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith display: -moz-inline-stack; /* Gecko < 1.9, since it doesn't support "inline-block" */
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith display: inline-block; /* IE, Opera and Webkit, and Gecko 1.9 */
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith width: 10px;
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith height: 10px;
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith border: inset 2px #999;
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith background-color: #fff; /* Need to set a background color or IE won't get mouse events */
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith Necessary for IE 6 (Quirks and Standards Mode) and IE 7 (Quirks Mode), since
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith they don't support use of negative margins without relative positioning.
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith _position: relative;
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith.yui3-checkbox span {
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith display: block;
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith height: 14px;
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith width: 12px;
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith overflow: hidden;
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith /* Position the checkmark for Gecko, Opera and Webkit and IE 7 (Strict Mode). */
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith margin: -5px 0 0 1px;
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith /* Position the checkmark for IE 6 (Strict and Quirks Mode) and IE 7 (Quirks Mode).*/
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith _position: absolute;
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith/* For Gecko < 1.9: Positions the checkbox on the same line as its corresponding label. */
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith.yui3-checkbox span:after {
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith content: ".";
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith visibility: hidden;
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith line-height: 2;
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith Hide the actual checkbox offscreen so that it is out of view, but still accessible via
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith the keyboard.
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith.yui3-checkbox input {
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith position: absolute;
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith left: -10000px;
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith.yui3-checkbox-focus {
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith border-color: #39f;
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith background-color: #9cf;
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith.yui3-checkbox-active {
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith background-color: #ccc;
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith.yui3-checkbox-checked span {
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith /* Draw a custom checkmark for the checked state using a background image. */
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith background: url(checkmark.png) no-repeat;
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith<h4>JavaScript</h4>
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke SmithApplication and removal of the state-based class names will be facilitated by JavaScript event
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smithhandlers. Each event handler will track the state of the
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith<code><input type="checkbox"></code> element, and apply and remove corresponding
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smithstate-based class names from its outtermost <code><span></code> —
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smithmaking it easy to style each state. And since each JavaScript is required for state management,
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smiththe stylesheet for the skinned checkboxes will only be added to the page when JavaScript is
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smithenabled. This will ensure that each checkbox works correctly with and without JavaScript enabled.
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke SmithSince there could easily be many instances of a skinned checkbox on the page, all event
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smithlisteners will be attached to the containing element for all checkboxes. Each listener will
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smithlisten for events as they bubble up from each checkbox. Relying on event bubbling will improve the
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smithoverall performance of the page by reducing the number of event handlers.
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke SmithSince the DOM <code>focus</code> and <code>blur</code> events do not bubble, use the Event Utility's
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith<a href="../../api/YUI.html#event_focus"><code>focus</code></a> and
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith<a href="../../api/YUI.html#event_focus"><code>blur</code></a> custom events, as an alternative to
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smithattaching discrete focus and blur event handlers to the <code><input type="checkbox"></code>
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smithelement of each skinned checkbox. The
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith<a href="../../api/YUI.html#event_focus"><code>focus</code></a> and
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith<a href="../../api/YUI.html#event_focus"><code>blur</code></a> custom events leverage
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smithcapture-phase DOM event listeners, making it possible to attach a single focus and blur event
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smithlistener on the containing element of each checkbox — thereby increasing the performance
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smithof the page. The complete script for the example comes together as follows:
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke SmithYUI().use("*", function(Y) {
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith sCheckboxFocusClass = getClassName("checkbox", "focus"), // Create yui3-checkbox-focus
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith sCheckboxCheckedClass = getClassName("checkbox", "checked"), // Create yui3-checkbox-checked
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith sCheckboxActiveClass = getClassName("checkbox", "active"), // Create yui3-checkbox-active
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith bKeyListenersInitialized = false,
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith bMouseListenersInitialized = false,
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith forAttr = (UA.ie && UA.ie < 8) ? "htmlFor" : "for",
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith bBlockDocumentMouseUp = false,
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith bBlockClearActive = false,
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith bBlockBlur = false,
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith oActiveCheckbox;
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith var initKeyListeners = function () {
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith this.delegate("keydown", onCheckboxKeyDown, ".yui3-checkbox");
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith this.delegate("click", onCheckboxClick, ".yui3-checkbox");
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith this.delegate("blur", onCheckboxBlur, "input[type=checkbox]");
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith bKeyListenersInitialized = true;
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith var initMouseListeners = function () {
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith this.delegate("mouseover", onCheckboxMouseOver, ".yui3-checkbox");
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith this.delegate("mouseout", onCheckboxMouseOut, ".yui3-checkbox-active");
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith this.get("ownerDocument").on("mouseup", onDocumentMouseUp);
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith bMouseListenersInitialized = true;
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith var getCheckbox = function (node) {
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith return (node.hasClass("yui3-checkbox") ? node : node.ancestor(".yui3-checkbox"));
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith var getCheckboxForLabel = function (label) {
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith var sID = label.getAttribute(forAttr),
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith oInput = Y.one("#" + sID);
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith if (oInput) {
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith oCheckbox = getCheckbox(oInput);
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith return oCheckbox;
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith var updateCheckedState = function (input) {
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith var oCheckbox = getCheckbox(input);
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith if (input.get("checked")) {
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith oCheckbox.addClass(sCheckboxCheckedClass);
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith oCheckbox.removeClass(sCheckboxCheckedClass);
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith var setActiveCheckbox = function (checkbox) {
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith checkbox.addClass(sCheckboxActiveClass);
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith oActiveCheckbox = checkbox;
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith var clearActiveCheckbox = function () {
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith if (oActiveCheckbox) {
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith oActiveCheckbox.removeClass(sCheckboxActiveClass);
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith oActiveCheckbox = null;
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith var onCheckboxMouseOver = function (event, matchedEl) {
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith if (oActiveCheckbox && oActiveCheckbox.compareTo(this)) {
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith oActiveCheckbox.addClass(sCheckboxActiveClass);
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith var onCheckboxMouseOut = function (event) {
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith this.removeClass(sCheckboxActiveClass);
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith var onDocumentMouseUp = function (event) {
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith var oCheckbox;
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith if (!bBlockDocumentMouseUp) {
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith oCheckbox = getCheckbox(event.target);
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith if ((oCheckbox && !oCheckbox.compareTo(oActiveCheckbox)) || !oCheckbox) {
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith clearActiveCheckbox();
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith bBlockDocumentMouseUp = false;
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith var onCheckboxFocus = function (event) {
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith // Remove the focus style from any checkbox that might still have it
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith var oCheckbox = Y.one("#checkboxes").one(".yui3-checkbox-focus");
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith if (oCheckbox) {
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith oCheckbox.removeClass(sCheckboxFocusClass);
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith // Defer adding key-related and click event listeners until
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith // one of the checkboxes is initially focused.
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith if (!bKeyListenersInitialized) {
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith var oCheckbox = getCheckbox(this);
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith oCheckbox.addClass(sCheckboxFocusClass);
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith var onCheckboxBlur = function (event) {
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith if (bBlockBlur) {
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith bBlockBlur = false;
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith var oCheckbox = getCheckbox(this);
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith oCheckbox.removeClass(sCheckboxFocusClass);
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith if (!bBlockClearActive && oCheckbox.compareTo(oActiveCheckbox)) {
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith clearActiveCheckbox();
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith bBlockClearActive = false;
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith var onCheckboxMouseDown = function (event) {
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith // Defer adding mouse-related and click event listeners until
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith // the user mouses down on one of the checkboxes.
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith if (!bMouseListenersInitialized) {
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith var oCheckbox,
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith if (this.get("nodeName").toLowerCase() === "label") {
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith // If the target of the event was the checkbox's label element, the
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith // label will dispatch a click event to the checkbox, meaning the
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith // "onCheckboxClick" handler will be called twice. For that reason
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith // it is necessary to block the "onDocumentMouseUp" handler from
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith // clearing the active state, so that a reference to the active
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith // checkbox still exists the second time the "onCheckboxClick"
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith // handler is called.
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith bBlockDocumentMouseUp = true;
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith // When the user clicks the label instead of the checkbox itself,
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith // the checkbox will be blurred if it has focus. Since the
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith // "onCheckboxBlur" handler clears the active state it is
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith // necessary to block the clearing of the active state when the
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith // label is clicked instead of the checkbox itself.
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith bBlockClearActive = true;
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith oCheckbox = getCheckboxForLabel(this);
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith oCheckbox = this;
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith // Need to focus the input manually for two reasons:
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith // 1) Mousing down on a label in Webkit doesn't focus its
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith // associated checkbox
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith // 2) By default checkboxes are focused when the user mouses
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith // down on them. However, since the actually checkbox is
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith // obscurred by the two span elements that are used to
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith // style it, the checkbox wont' receive focus as it was
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith // never the actual target of the mousedown event.
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith oInput = oCheckbox.one("input");
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith // Calling Event.preventDefault won't block the blurring of the
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith // currently focused element in IE, so we'll use the "bBlockBlur"
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith // variable to stop the code in the blur event handler
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith // from executing.
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith setActiveCheckbox(oCheckbox);
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith // Need to call preventDefault because by default mousing down on
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith // an element will blur the element in the document that currently
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith // has focus--in this case, the input element that was
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith // just focused.
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith var onCheckboxClick = function (event) {
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith if (this.compareTo(oActiveCheckbox)) {
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith oInput = this.one("input");
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith // If the click event was fired via the mouse the checked
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith // state will have to be manually updated since the input
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith // is hidden offscreen and therefore couldn't be the
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith // target of the click.
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith updateCheckedState(oInput);
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith clearActiveCheckbox();
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith var onCheckboxKeyDown = function (event) {
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith // Style the checkbox as being active when the user presses the
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith // space bar
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith setActiveCheckbox(this);
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith Y.all("#checkboxes>div>span").addClass("yui3-checkbox");
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith // Remove the "yui3-checkboxes-loading" class used to hide the
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith // checkboxes now that the checkboxes have been skinned.
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith Y.one("#checkboxes").removeClass("yui3-checkboxes-loading");
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith // Add the minimum number of event listeners needed to start, bind the
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith // rest when needed
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith Y.delegate("mousedown", onCheckboxMouseDown, "#checkboxes", ".yui3-checkbox,label");
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith Y.delegate("focus", onCheckboxFocus, "#checkboxes", "input[type=checkbox]");
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith<h4>Progressive Enhancement</h4>
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke SmithTo account for the scenario where the user has CSS enabled in their browser but JavaScript
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smithis disabled, the CSS used to style the checkboxes will be loaded via JavaScript
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smithusing the YUI instance's <a href="http://developer.yahoo.com/yui/3/yui#loader">built-in Loader</a>.
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith base: "${buildDirectory}",
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith // Load the stylesheet for the skinned checkboxes via JavaScript,
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith // since without JavaScript skinning of the checkboxes wouldn't
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith // be possible.
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith "checkboxcss": {
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith type: "css",
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith fullpath: "${assetsDirectory}checkbox.css"
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith "checkboxjs": {
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith fullpath: "${assetsDirectory}checkbox.js",
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith requires: ["classnamemanager", "event-focus", "node-event-delegate", "checkboxcss"]
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith}).use("checkboxjs");
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke SmithTo prevent the user from seeing a flash unstyled content when JavaScript is enabled,
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smitha style rule is created using YUI's <code>yui3-js-enabled</code> class name that will temporarily
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smithhide the markup while the JavaScript and CSS are in the process of loading. For more on using the
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith<code>yui3-js-enabled</code> class name, see the
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith<a href="../../widget/#progressive">HIDING PROGRESSIVELY ENHANCED MARKUP</a> section of the
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith<a href="../../widget/">YUI Widget landing page</a>.
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith.yui3-js-enabled .yui3-checkboxes-loading {
350964af8dfc6ab5df9d3fb0f274f7d018986c5bLuke Smith display: none;