Checkbox group using ARIA CSS selectors for visual state

Description

Simple example of a checkbox group using ARIA CSS selectors to display the visual state of the boxes in the group. Background images are typically not displayed in high contrast mode. For this reason, background images should not be relied upon to display essential visual information.

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

  • group

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="cond">Sandwich Condiments</h3>


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

    <li id="cb1all"
        role="checkbox"
        class="groupbox"
        aria-checked="mixed"
        tabindex="0">
        All condiments
        
     </li>  
            <li id="cb1a"
                role="checkbox"
                class="checkbox"
                aria-checked="false"
                aria-describedby="desc1"
                tabindex="0">
                Lettuce
            </li>
            
            <li id="cb1b"
                role="checkbox"
                class="checkbox"
                aria-checked="true"
                aria-describedby="desc2"
                tabindex="0">
                Tomato
            </li>
            
            <li id="cb1c"
                role="checkbox"
                class="checkbox"
                aria-checked="true"
                aria-describedby="desc3"
                tabindex="0">
                Mustard
            </li>
            
            <li id="cb1d"
                role="checkbox"
                class="checkbox"
                aria-checked="true"
                aria-describedby="desc4"
                tabindex="0">
                Sprouts
            </li>            
  
</ul>

<p id="desc1" class="hidden">Not your average lettuce</p>
<p id="desc2" class="hidden">Organically grown beef steak tomatos</p>
<p id="desc3" class="hidden">Brown and spicy mustard</p>
<p id="desc4" class="hidden">Fresh alfalfa sprouts, organically grown</p>


</div>

CSS Source Code


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

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

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

li[aria-checked='false'] {
   background: url('http://dev-static.oaa-accessibility.org/examples/images/checkbox-unchecked-black.png') no-repeat .5em center;
}

li[aria-checked='true'] {
   background: url('http://dev-static.oaa-accessibility.org/examples/images/checkbox-checked-black.png') no-repeat .5em center;;
}

li[aria-checked='mixed'] {
   background: url('http://dev-static.oaa-accessibility.org/examples/images/checkbox-mixed-black.png') no-repeat .5em center;
}
  
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.
*
* This implementation of checkboxGroup() uses background images to represent the check state of the
* group. This version will not work in high contrast modes, where background images are not displayed.
*
* @param {string} list - the id of the unordered list that checkboxgroup is to be bound to
*
* @return {N/A}
*/

/**
* @private
* @constructor Internal Properties
*
* @property {object} id - the jquery id object
* @property {object} keys - assigns keycodes
*
* @property {integer} unchecked - (set to 0) corresponds to an unchecked box
* @property {integer} checked - (set to 1) corresponds to a checked box
* @property {integer} mixed - (set to 2) corresponds to a mixed box
* @property {integer} 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 {object} $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) {

  switch (state) {
    case this.unchecked: {
      $boxID.attr('aria-checked', 'false');

      break;
    }
    case this.checked: {
      $boxID.attr('aria-checked', 'true');
      break;
    }
    case this.mixed: {
      $boxID.attr('aria-checked', 'mixed');
      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(inc boolean) inc is 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()


/**
* @method handleGroupboxClick
*
* @memberOf OAA_EXAMPLES
*
* @desc a member function to handle click events for group checkbox
*
* @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.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 {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.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 - 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 - 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 - 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()