Checkbox group using IMG elements for visual state

Description

Simple example of a checkbox group using inline images to display the visual state of the boxes in the group.

Keyboard Support

  • Tab: Move between button items and text area.
  • Enter or space: Toggle aria-checked state of checkbox with keyboard focus.

Example Start

Sandwich Condiments

Example End

Roles

  • checkbox
  • group
  • presentation

Properties

  • aria-checked
  • aria-describedby
  • aria-labelledby

HTML Source Code


<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>

<div role="application">

<h3 id="id_cond">Sandwich Condiments</h3>


<ul id="id_cb1" class="checkboxes" role="group" aria-labelledby="cond">

    <li id="id_cb1_all"
        role="checkbox"
  class="groupbox"
        aria-checked="mixed"
        tabindex="0">
  <img src="http://dev-static.oaa-accessibility.org/examples/images/checkbox-mixed-black.png" role="presentation">
        All condiments
        
     </li>  
            <li id="id_cb1_a"
                role="checkbox"
    class="checkbox"
                aria-checked="false"
                aria-describedby="id_desc1"
                tabindex="0">
    <img src="http://dev-static.oaa-accessibility.org/examples/images/checkbox-unchecked-black.png" role="presentation">
                Lettuce
            </li>
            
            <li id="id_cb1_b"
                role="checkbox"
    class="checkbox"
                aria-checked="true"
                aria-describedby="id_desc2"
                tabindex="0">
    <img src="http://dev-static.oaa-accessibility.org/examples/images/checkbox-checked-black.png" role="presentation">
                Tomato
            </li>
            
            <li id="id_cb1_c"
                role="checkbox"
    class="checkbox"
                aria-checked="true"
                aria-describedby="id_desc3"
                tabindex="0">
    <img src="http://dev-static.oaa-accessibility.org/examples/images/checkbox-checked-black.png" role="presentation">
                Mustard
            </li>
            
            <li id="id_cb1_d"
                role="checkbox"
    class="checkbox"
                aria-checked="true"
                aria-describedby="id_desc4"
                tabindex="0">
    <img src="http://dev-static.oaa-accessibility.org/examples/images/checkbox-checked-black.png" role="presentation">
                Sprouts
            </li>            
  
</ul>

<p id="id_desc1" class="hidden">Not your average lettuce</p>
<p id="id_desc2" class="hidden">Organically grown beef steak tomatos</p>
<p id="id_desc3" class="hidden">Brown and spicy mustard</p>
<p id="id_desc4" class="hidden">Fresh alfalfa sprouts, organically grown</p>


</div>

CSS Source Code



ul.checkboxes {
   margin: 0;
   padding: 0;
}

ul.checkboxes li img {
   margin-right: .5em;
}

li.groupbox {
   margin-left: 1em;
   padding: 0;
   padding-left: .5em;
   list-style: none;
   width: 7.5em;
   border: 2px solid transparent;
}

li.checkbox {
   margin-left: 2.5em;
   padding: 0;
   padding-left: .5em;
   list-style: none;
   width: 7em;
   border: 2px solid transparent;
}

li.hover {
   border: 2px solid #777;
}

li.focus {
   border: 2px solid black;
}

.hidden {
   position: absolute;
   top: -30em;
   left: -300em;
}

Javascript Source Code


var OAA_EXAMPLES = OAA_EXAMPLES || {};
var KEY_SPACE = 32;

$(document).ready(function() {

  var checkboxGroupApp = new OAA_EXAMPLES.checkboxGroup("cb1");

}); // end ready event

/**
* Function keyCodes() is an object to contain keycodes needed for the application
*/
function keyCodes() {
  this.space = 32;
}

/**
* @constructor checkboxGroup
*
* @memberOf OAA_EXAMPLES
*
* @desc a class constructor for the implementation of a checkbox group widget.
* checkboxGroup() requires an unordered list structure, with the first list entry being the group
* checkbox and the remaining entries being the checkboxes controlled by the group. Each list entry
* must contain an image tag that will be used to display the state of the checkbox.
*
* @param {String} list - the id of the unordered list that checkboxgroup is to be bound to
*
* @return {N/A}
*/

/**
* @private
* @constructor Internal Properties
*
* @property {obj} $id - JQuery id object
* @property {obj} keys - assigns a list of keycodes
*
* @property {integer} unchecked - (set to 0) value which corresponds to an unchecked checkbox
* @property {integer} checked - (set to 1) value which corresponds to a checked checkbox
* @property {integer} mixed - (set to 2) value which corresponds to a mixed checkbox
*
* @property {obj} $groupBox - JQuery object for a group box
* @property {obj} $checkboxes - JQuery object for a check box
* @property {obj} checkedCount - set to the number of checkboxes that are checked
*/
OAA_EXAMPLES.checkboxGroup = function(list) {

  // define object properties
  this.$id = $('#' + list);
  this.keys = new keyCodes();

  this.unchecked = 0;
  this.checked = 1;
  this.mixed = 2;

  this.$groupBox = this.$id.find('li').first();
  this.$checkboxes = this.$groupBox.siblings();
  this.checkedCount = 0;
  // initialize the checkboxGroup object
  this.init();

  // bind event handlers
  this.bindHandlers();

} // end checkboxGroup() constructor

/**
* @method init
*
* @memberOf OAA_EXAMPLES
*
* @desc a member function to initialize the checkboxGroup object. Initial checkbox
* states are set according to the aria-checked property of the checkboxes in the group.
*
* return {N/A}
*/

OAA_EXAMPLES.checkboxGroup.prototype.init = function() {

  var thisObj = this;

  this.$checkboxes.each(function() {
    if ($(this).attr('aria-checked') == 'true') {
      thisObj.adjCheckedCount(true);
    }
  });

} // end init()

/**
* @method bindHandlers
*
* @memberOf OAA_EXAMPLES
*
* @desc a member function to bind event handlers to the checkboxes in the checkbox group.
*
* @return {N/A}
*/

OAA_EXAMPLES.checkboxGroup.prototype.bindHandlers = function() {

  var thisObj = this;

  /////////// Bind groupbox handlers ////////////////
  
  // bind a click handler
  this.$groupBox.click(function(e) {
    return thisObj.handleGroupboxClick($(this), e);
  });

  // bind a keydown handler
  this.$groupBox.keydown(function(e) {
    return thisObj.handleGroupboxKeyDown($(this), e);
  });

  // bind a keypress handler
  this.$groupBox.keypress(function(e) {
    return thisObj.handleBoxKeyPress($(this), e);
  });

  // bind a mouseover handler
  this.$groupBox.mouseover(function(e) {
    return thisObj.handleBoxMouseOver($(this), e);
  });

  // bind a mouseout handler
  this.$groupBox.mouseout(function(e) {
    return thisObj.handleBoxMouseOut($(this), e);
  });

  // bind a focus handler
  this.$groupBox.focus(function(e) {
    return thisObj.handleBoxFocus($(this), e);
  });

  // bind a blur handler
  this.$groupBox.blur(function(e) {
    return thisObj.handleBoxBlur($(this), e);
  });

  /////////// Bind checkbox handlers ////////////////
  
  // bind a click handler
  this.$checkboxes.click(function(e) {
    return thisObj.handleCheckboxClick($(this), e);
  });

  // bind a keydown handler
  this.$checkboxes.keydown(function(e) {
    return thisObj.handleCheckboxKeyDown($(this), e);
  });

  // bind a keypress handler
  this.$checkboxes.keypress(function(e) {
    return thisObj.handleBoxKeyPress($(this), e);
  });

  // bind a mouseover handler
  this.$checkboxes.mouseover(function(e) {
    return thisObj.handleBoxMouseOver($(this), e);
  });

  // bind a mouseout handler
  this.$checkboxes.mouseout(function(e) {
    return thisObj.handleBoxMouseOut($(this), e);
  });

  // bind a focus handler
  this.$checkboxes.focus(function(e) {
    return thisObj.handleBoxFocus($(this), e);
  });

  // bind a blur handler
  this.$checkboxes.blur(function(e) {
    return thisObj.handleBoxBlur($(this), e);
  });

} // end bindHandlers()

/**
* @method setBoxState
*
* @memberOf OAA_EXAMPLES
*
* @desc a member function to set a checkbox state. This function sets the
* aria-checked property to the passed state value and changes the box image to display the new
* box state.
*
* @param {obj} $boxID - the jquery object of the checkbox to manipulate
*
* @param {integer} state - the check state to set the box
*
* @return {N/A}
*/

OAA_EXAMPLES.checkboxGroup.prototype.setBoxState = function($boxID, state) {

  var $img = $boxID.find('img');

  switch (state) {
    case this.unchecked: {
      $boxID.attr('aria-checked', 'false');
      $img.attr('src','http://dev-static.oaa-accessibility.org/examples/images/checkbox-unchecked-black.png');

      break;
    }
    case this.checked: {
      $boxID.attr('aria-checked', 'true');
      $img.attr('src','http://dev-static.oaa-accessibility.org/examples/images/checkbox-checked-black.png');
      break;
    }
    case this.mixed: {
      $boxID.attr('aria-checked', 'mixed');
      $img.attr('src','http://dev-static.oaa-accessibility.org/examples/images/checkbox-mixed-black.png');
      break;
    }
  } // end switch

} // end setBoxState()

/**
* @method adjCheckedCount
*
* @memberOf OAA_EXAMPLES
*
* @desc a member function to increment or decrement the count of checked
* boxes. The function modifies the checkes state of the group box accordingly.
*
* @param {boolean} inc - true if incrementing the checked count, false if decrementing
*
* @return {N/A}
*/

OAA_EXAMPLES.checkboxGroup.prototype.adjCheckedCount = function(inc) {

  // increment or decrement the count
  if (inc == true) {
    this.checkedCount++;
  }
  else {
    this.checkedCount--;
  }

  // modify the group box state
  if (this.checkedCount == this.$checkboxes.length) {
    // all the boxes are checked
    this.setBoxState(this.$groupBox, this.checked);
  }
  else if (this.checkedCount > 0) {
    // some of the boxes are checked
    this.setBoxState(this.$groupBox, this.mixed);
  }
  else {
    // all boxes are unchecked
    this.setBoxState(this.$groupBox, this.unchecked);
  }

} // end adjCheckedCount()


/** Groupbox event handlers
*
*
* Function handleGroupboxClick() is a member function to handle click events for group checkbox
*
* @param ($id object) $id is the jquery object of the checkbox
*
* @param (e object) e is the event object associated with the keydown event
*
* @return (boolean) Returns false if processing; true of doing nothing
*/
OAA_EXAMPLES.checkboxGroup.prototype.handleGroupboxClick = function($id, e) {
    
  var thisObj = this;

  if (e.altkey || e.ctrlKey || e.shiftKey) {
    // do nothing;
    return true;
  }

  switch ($id.attr('aria-checked')) {
    case 'true' : {
      // uncheck the group

      // clear the groupbox
      this.setBoxState($id, this.unchecked);

      // clear all the checkboxes in the group
      this.$checkboxes.each(function() {

        // clear the groupbox
        thisObj.setBoxState($(this), thisObj.unchecked);
      });

      // reset the checked count
      this.checkedCount = 0;

      break;
    }
    case 'mixed' :
    case 'false' : {
      // check the group

      // set the groupbox to checked
      this.setBoxState($id, this.checked);

      // check all the checkboxes in the group
      this.$checkboxes.each(function() {

        // clear the groupbox
        thisObj.setBoxState($(this), thisObj.checked);
      });

      // set the checked count
      this.checkedCount = this.$checkboxes.length;

      break;
    }
  } // end switch

  e.stopPropagation();
  return false;
  
} // end handleGroupboxClick()
  
/**
* @method handleGroupboxKeyDown
*
* @memberOf OAA_EXAMPLES
*
* @desc a member function to handle keydown events for the group checkbox
*
* @param {obj} $id - the jquery object of the checkbox
*
* @param {obj} e - the event object associated with the keydown event
*
* @return {boolean} Returns false if processing; true of doing nothing
*/

OAA_EXAMPLES.checkboxGroup.prototype.handleGroupboxKeyDown = function($id, e) {
    
  var thisObj = this;

  if (e.altkey || e.ctrlKey || e.shiftKey) {
    // do nothing;
    return true;
  }

  if( e.keyCode == this.keys.space ) {

    switch ($id.attr('aria-checked')) {
      case 'true' : {
        // uncheck the group
  
        // clear the groupbox
        this.setBoxState($id, this.unchecked);
  
        // clear all the checkboxes in the group
        this.$checkboxes.each(function() {
  
          // clear the groupbox
          thisObj.setBoxState($(this), thisObj.unchecked);
        });
  
        // reset the checked count
        this.checkedCount = 0;
  
        break;
      }
      case 'mixed' :
      case 'false' : {
        // check the group
  
        // set the groupbox to checked
        this.setBoxState($id, this.checked);
  
        // check all the checkboxes in the group
        this.$checkboxes.each(function() {
  
          // clear the groupbox
          thisObj.setBoxState($(this), thisObj.checked);
        });
  
        // set the checked count
        this.checkedCount = this.$checkboxes.length;
  
        break;
      }
    } // end switch

    e.stopPropagation();
    return false;
  } // endif

  return true;
  
} // end handleGroupboxKeyDown()
  

/**
* @method handleCheckboxClick
*
* @memberOf OAA_EXAMPLES
*
* @desc a member function to handle click events for checkboxes
*
* @param {object} $id - the jquery object of the checkbox
*
* @param {object} e - the event object associated with the keydown event
*
* @return {boolean} Returns false if processing; true of doing nothing
*/

OAA_EXAMPLES.checkboxGroup.prototype.handleCheckboxClick = function($id, e) {
    
  if (e.altKey || e.ctrlKey || e.shiftKey) {
    // do nothing;
    return true;
  }

  // toggle the checkbox state

  if($id.attr('aria-checked') == 'true') {
    this.setBoxState($id, this.unchecked);
    this.adjCheckedCount(false);
  } else {
    this.setBoxState($id, this.checked);
    this.adjCheckedCount(true);
  }  // endif

  e.stopPropagation();
  return false;
  
} // end handleCheckboxClick()
  
/**
* @method handleCheckboxKeyDown
*
* @memberOf OAA_EXAMPLES
*
* @desc a member function to handle keydown events for checkboxes
*
* @param {object} $id - the jquery object of the checkbox
*
* @param {object} e - the event object associated with the keydown event
*
* @return {boolean} Returns false if processing; true of doing nothing
*/

OAA_EXAMPLES.checkboxGroup.prototype.handleCheckboxKeyDown = function($id, e) {
    
  if (e.altkey || e.ctrlKey || e.shiftKey) {
    // do nothing;
    return true;
  }

  if( e.keyCode == this.keys.space ) {

    // toggle the checkbox state

    if($id.attr('aria-checked') == 'true') {
      this.setBoxState($id, this.unchecked);
      this.adjCheckedCount(false);
    } else {
      this.setBoxState($id, this.checked);
      this.adjCheckedCount(true);
    }  // endif

    e.stopPropagation();
    return false;
  } // endif

  return true;
  
} // end handleCheckboxKeyDown()
  
/**
* @method handleBoxKeyPress
*
* @memberOf OAA_EXAMPLES
*
* @desc a member function to handle keypress events for checkboxes.
* This function is needed to consume events for browsers, such as Opera, that perform window
* manipulation on keypress events.
*
* @param {object} $id is the jquery object of the checkbox
*
* @param {object} e - the event object associated with the keydown event
*
* @return {boolean} Returns false if processing; true of doing nothing
*/

OAA_EXAMPLES.checkboxGroup.prototype.handleBoxKeyPress = function($id, e) {
    
  if (e.altkey || e.ctrlKey || e.shiftKey) {
    // do nothing;
    return true;
  }

  if( e.keyCode == this.keys.space ) {
    // consume the event
    e.stopPropagation();
    return false;
  } // endif

  return true;
  
} // end handleBoxKeyPress()

/**
* @method handleBoxMouseOver
*
* @memberOf OAA_EXAMPLES
*
* @desc a member function to handle mouseover events for checkboxes
*
* @param {object} $id - the jquery object of the checkbox
*
* @param {object} e is the event object associated with the mouseover event
*
* @return {boolean} Returns false;
*/

OAA_EXAMPLES.checkboxGroup.prototype.handleBoxMouseOver = function($id, e) {
    
  // if the box does not have the focus class add the hover highlight
  if ($id.not('.focus')) {
    $id.addClass('hover');
  }

  e.stopPropagation();
  return false;
  
} // end handleBoxMouseOver()

/**
* @method handleBoxMouseOut
*
* @memberOf OAA_EXAMPLES
*
* @desc a member function to handle mouseout events for checkboxes
*
* @param {object} $id is the jquery object of the checkbox
*
* @param {object} e - the event object associated with the mouseout event
*
* @return {boolean} Returns false;
*/

OAA_EXAMPLES.checkboxGroup.prototype.handleBoxMouseOut = function($id, e) {
    
  $id.removeClass('hover');

  e.stopPropagation();
  return false;
  
} // end handleBoxMouseOut()

/**
* @method handleBoxFocus
*
* @memberOf OAA_EXAMPLES
*
* @desc a member function to handle focus events for checkboxes
*
* @param {object} $id - the jquery object of the checkbox
*
* @param {object} e - the event object associated with the focus event
*
* @return {boolean} Returns true;
*/

OAA_EXAMPLES.checkboxGroup.prototype.handleBoxFocus = function($id, e) {
    
  $id.addClass('focus');

  // remove the hover class if it is applied
  $id.removeClass('hover');

  return true;
  
} // end handleBoxFocus()

/**
* @method handleBoxBlur
*
* @memberOf OAA_EXAMPLES
*
* @desc  a member function to handle blur events for checkboxes
*
* @param {object} $id - the jquery object of the checkbox
*
* @param {object} e - the event object associated with the blur event
*
* @return {boolean} Returns true;
*/

OAA_EXAMPLES.checkboxGroup.prototype.handleBoxBlur = function($id, e) {
    
  $id.removeClass('focus');
  return true;
  
} // end handleBoxBlur()