Test Start

50
750

Test End

Rule References

Rule Pass IDs Fail IDs
Rule 84
  • application
  • sb1
  • sb1_up
  • sb1_up_img
  • sb1_down
  • sb1_down_img
  • sb2
  • sb2_up
  • sb2_up_img
  • sb2_down
  • sb2_down_img
none
Rule 85
  • sb1
  • sb1_up
  • sb1_down
  • sb2
  • sb2_up
  • sb2_down
none
Rule 86
  • sb1
  • sb2
none
Rule 87
  • sb1
  • sb2
none
Rule 90
  • sb1
  • sb2
none
Rule 91
  • sb1
  • sb2
none
Rule 92
  • application
  • sb1
  • sb1_up
  • sb1_up_img
  • sb1_down
  • sb1_down_img
  • sb2
  • sb2_up
  • sb2_up_img
  • sb2_down
  • sb2_down_img
none
Rule 93
  • sb1
  • sb2
none
Rule 94
  • sb1
  • sb1_up
  • sb1_down
  • sb2
  • sb2_up
  • sb2_down
none
Rule 95
  • sb1
  • sb1_up
  • sb1_down
  • sb2
  • sb2_up
  • sb2_down
none

Calculations

No calculations for test 134

Test Description

  • This test implements a simple ARIA spinbutton widget

Keyboard shortcuts (based on recommended shortcuts specified by the DHTML Style Guide Working Group):
* Right and Up Arrows: increase the value
* Left and Down Arrows decrease the value
* Home and End key: move to the maximum or minimum values
* Page Up and Page Down: incrementally increase or decrease the value
* Note: Focus should remain on the edit field

Test Markup

User Agent Implementation

No user agent implementation information.

HTML Source Code


<div id="application" role="application">
<label id="sb1_label" class="sbLabel" for="sb1">Choose a number between 0 and 100</label>
<div class="spinControl">
  <div id="sb1" class="spinbutton" role="spinbutton"
            aria-labelledby="sb1_label"
            aria-valuemin="0"
            aria-valuemax="100"
            aria-valuenow="50"
            tabindex="0">
            50
  </div>
  <div id="sb1_up" class="button" role="button" title="Increase Value">
    <img id="sb1_up_img" src="http://www.oaa-acessibility.org/media/testsuite/images/button-arrow-up.png" role="presentation">
  </div>
  <div id="sb1_down" class="button" role="button" title="Decrease Value">
    <img id="sb1_down_img" src="http://www.oaa-acessibility.org/media/testsuite/images/button-arrow-down.png" role="presentation">
  </div>
</div>

<label id="sb2_label" class="sbLabel" for="sb2">Choose a number between 500 and 1000</label>
<div class="spinControl">
  <div id="sb2" class="spinbutton" role="spinbutton"
            aria-labelledby="sb2_label"
            aria-valuemin="500"
            aria-valuemax="1000"
            aria-valuenow="750"
            tabindex="0">
            750
  </div>
  <div id="sb2_up" class="button" role="button" title="Increase Value">
    <img id="sb2_up_img" src="http://www.oaa-acessibility.org/media/testsuite/images/button-arrow-up.png" role="presentation">
  </div>
  <div id="sb2_down" class="button" role="button" title="Decrease Value">
    <img id="sb2_down_img" src="http://www.oaa-acessibility.org/media/testsuite/images/button-arrow-down.png" role="presentation">
  </div>
</div>

</div>

CSS Code


div.spinControl {
  margin: 20px;
  margin-left: 40px;
  width: 100px;
  height: 44px;
  border: 1px solid black;
}
div.spinbutton {
  float: left;
  display: inline;
  margin: 1px;
  text-align: right;
  font-weight: bold;
  font-size: 1.6em;
  padding: 7px 10px 7px 0;
  width: 65px;
  height: 28px;
  background-color: #faf7f0;
}
div.spinbutton:active,
div.spinbutton:hover,
div.spinbutton.focus {
  margin: 0;
  background-color: #faf7f0;
  border: 1px solid red;
}
div.button {
  margin: 0;
  margin-left: 77px;
  padding: 0;
  height: 22px;
}
div.button img {
  margin: 0;
  padding: 0;
  border-left: 1px solid black;
}
label.sbLabel {
  font-weight: bold;
  font-size: 1.2em;
}

Javascript Source Code


$(document).ready(function () {
  var spin1 = new spinbutton('sb1', 'sb1_up', 'sb1_down', 10);  
  var spin2 = new spinbutton('sb2', 'sb2_up', 'sb2_down', 50);  
}); // end ready

//
// Function spinbutton() is a constructor for an ARIA spinbutton widget. The widget
// binds to an element with role='spinbutton'.
//
// @param (id string) id is the html id of the spinbutton element
//
// @param (upID string) upID is the html id of the spinbutton control's increase value button
//
// @param (downID string) downID is the html id of the spinbutton control's decrease value button
//
// @param (skipVal integer) skipVal is the amount to change the control by for pgUp/pgDown
// @return N/A
//
function spinbutton(id, upID, downID, skipVal) {

  // define widget attributes
  this.$id = $('#' + id);

  this.upID = upID;
  this.$upButton = $('#' + upID);
  this.downID = downID;
  this.$downButton = $('#' + downID);
  this.skipVal = skipVal;

  this.valMin = parseInt(this.$id.attr('aria-valuemin'));
  this.valMax = parseInt(this.$id.attr('aria-valuemax'));
  this.valNow = parseInt(this.$id.attr('aria-valuenow'));

  this.keys = {
    pageup:   33,
    pagedown: 34,
    end:      35,
    home:     36,
    left:     37,
    up:       38,
    right:    39,
    down:     40
  };
    
  // bind event handlers
  this.bindHandlers();
}

// Function bindHandlers() is a member function to bind event handlers for the spinbutton control
//
// @return N/A
//
spinbutton.prototype.bindHandlers = function() {

  var thisObj = this;

  //////// bind mouse event handlers to the up button //////////////
  this.$upButton.mousedown(function(e) {
    return thisObj.handleMouseDown(e, $(this));
  });

  this.$upButton.mouseup(function(e) {
    return thisObj.handleMouseUp(e, $(this));
  });

  this.$upButton.mouseenter(function(e) {
    return thisObj.handleMouseEnter(e, $(this));
  });

  this.$upButton.mouseout(function(e) {
    return thisObj.handleMouseOut(e, $(this));
  });

  this.$upButton.click(function(e) {
    return thisObj.handleClick(e, $(this));
  });

  //////// bind mouse event handlers to the down button //////////////
  this.$downButton.mousedown(function(e) {
    return thisObj.handleMouseDown(e, $(this));
  });

  this.$downButton.mouseup(function(e) {
    return thisObj.handleMouseUp(e, $(this));
  });

  this.$downButton.mouseenter(function(e) {
    return thisObj.handleMouseEnter(e, $(this));
  });

  this.$downButton.mouseout(function(e) {
    return thisObj.handleMouseOut(e, $(this));
  });

  this.$downButton.focus(function(e) {
    return thisObj.handleFocus(e, $(this));
  });

  this.$downButton.click(function(e) {
    return thisObj.handleClick(e, $(this));
  });

  //////// bind event handlers to the spinbutton //////////////
  this.$id.keydown(function(e) {
    return thisObj.handleKeyDown(e);
  });

  this.$id.keypress(function(e) {
    return thisObj.handleKeyPress(e);
  });

  this.$id.focus(function(e) {
    return thisObj.handleFocus(e);
  });

  this.$id.blur(function(e) {
    return thisObj.handleBlur(e);
  });

  this.$id.parent().focusout(function(e) {
    return thisObj.handleBlur(e);
  });

} // end bindHandlers()

//
// Function handleClick() is a member function to handle click events for the control
// buttons
//
// @param (e object) e is the event object
//
// @param ($button object) $button is the jQuery object of the button clicked
//
// @return (boolean) Returns false
//
spinbutton.prototype.handleClick = function(e, $button) {


  if ($button.attr('id') == this.upID) {

    // if valuemax isn't met, increment valnow
    if (this.valNow < this.valMax) {
      this.valNow++;

      this.$id.text(this.valNow);
      this.$id.attr('aria-valuenow', this.valNow);
    }
  }
  else {

    // if valuemax isn't met, decrement valnow
    if (this.valNow > this.valMin) {
      this.valNow--;

      this.$id.text(this.valNow);
      this.$id.attr('aria-valuenow', this.valNow);
    }
  }

  // set focus on the spinbutton
  this.$id.focus();
    
  e.stopPropagation();
  return false;

} // end handleClick()

//
// Function handleMouseDown() is a member function to handle mousedown events for the control
// buttons
//
// @param (e object) e is the event object
//
// @param ($button object) $button is the jQuery object of the button clicked
//
// @return (boolean) Returns false
//
spinbutton.prototype.handleMouseDown = function(e, $button) {

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

  if ($button.attr('id') == this.upID) {
    $img.attr('src', "http://www.oaa-acessibility.org/media/testsuite/images/button-arrow-up-pressed-hl.png");
  }
  else {
    $img.attr('src', "http://www.oaa-acessibility.org/media/testsuite/images/button-arrow-down-pressed-hl.png");
  }

  e.stopPropagation();
  return false;

} // end handleMouseDown()

//
// Function handleMouseUp() is a member function to handle mouseup events for the control
// buttons
//
// @param (e object) e is the event object
//
// @param ($button object) $button is the jQuery object of the button clicked
//
// @return (boolean) Returns false
//
spinbutton.prototype.handleMouseUp = function(e, $button) {

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

  if ($button.attr('id') == this.upID) {
    $img.attr('src', "http://www.oaa-acessibility.org/media/testsuite/images/button-arrow-up-hl.png");
  }
  else {
    $img.attr('src', "http://www.oaa-acessibility.org/media/testsuite/images/button-arrow-down-hl.png");
  }

  e.stopPropagation();
  return false;

} // end handleMouseUp()

//
// Function handleMouseEnter() is a member function to handle mouseenter events for the control
// buttons
//
// @param (e object) e is the event object
//
// @param ($button object) $button is the jQuery object of the button
//
// @return (boolean) Returns false
//
spinbutton.prototype.handleMouseEnter = function(e, $button) {

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

  if ($button.attr('id') == this.upID) {
    $img.attr('src', "http://www.oaa-acessibility.org/media/testsuite/images/button-arrow-up-hl.png");
  }
  else {
    $img.attr('src', "http://www.oaa-acessibility.org/media/testsuite/images/button-arrow-down-hl.png");
  }

  e.stopPropagation();
  return false;

} // end handleMouseOutEnter()

//
// Function handleMouseOut() is a member function to handle mouseout events for the control
// buttons
//
// @param (e object) e is the event object
//
// @param ($button object) $button is the jQuery object of the button
//
// @return (boolean) Returns false
//
spinbutton.prototype.handleMouseOut = function(e, $button) {

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

  if ($button.attr('id') == this.upID) {
    $img.attr('src', "http://www.oaa-acessibility.org/media/testsuite/images/button-arrow-up.png");
  }
  else {
    $img.attr('src', "http://www.oaa-acessibility.org/media/testsuite/images/button-arrow-down.png");
  }

  e.stopPropagation();
  return false;

} // end handleMouseOutUp()

//
// Function handleKeyDown() is a member function to handle keydown events for the control.
//
// @param (e object) e is the event object
//
// @return (boolean) Returns false if consuming; true if propagating
//
spinbutton.prototype.handleKeyDown = function(e) {

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

  switch(e.keyCode) {
    case this.keys.pageup: {

      if (this.valNow < this.valMax) {

        // if valnow is small enough, increase by the skipVal,
        // otherwise just set to valmax
        if (this.valNow < this.valMax - this.skipVal) {
          this.valNow += this.skipVal;
        }  
        else {
          this.valNow = this.valMax;
        }

        // update the control
        this.$id.attr('aria-valuenow', this.valNow);
        this.$id.html(this.valNow);
      }

      e.stopPropagation();
      return false;
    }
    case this.keys.pagedown: {

      if (this.valNow > this.valMin) {

        // if valNow is big enough, decrease by the skipVal,
        // otherwise just set to valmin
        if (this.valNow > this.valMin + this.skipVal) {
          this.valNow -= this.skipVal;
        }  
        else {
          this.valNow = this.valMin;
        }

        // update the control
        this.$id.attr('aria-valuenow', this.valNow);
        this.$id.html(this.valNow);
      }

      e.stopPropagation();
      return false;
    }
    case this.keys.home: {

      if (this.valNow < this.valMax) {
        this.valNow = this.valMax;
        this.$id.attr('aria-valuenow', this.valNow);
        this.$id.html(this.valNow);
      }

      e.stopPropagation();
      return false;
    }
    case this.keys.end: {

      if (this.valNow > this.valMin) {
        this.valNow = this.valMin;
        this.$id.attr('aria-valuenow', this.valNow);
        this.$id.html(this.valNow);
      }

      e.stopPropagation();
      return false;
    }
    case this.keys.right:
    case this.keys.up: {

      // if valuemin isn't met, increment valnow
      if (this.valNow < this.valMax) {
        this.valNow++;

        this.$id.text(this.valNow);
        this.$id.attr('aria-valuenow', this.valNow);
      }

      e.stopPropagation();
      return false;
    }
    case this.keys.left:
    case this.keys.down: {

      // if valuemax isn't met, decrement valnow
      if (this.valNow > this.valMin) {
        this.valNow--;

        this.$id.text(this.valNow);
        this.$id.attr('aria-valuenow', this.valNow);
      }

      e.stopPropagation();
      return false;
    }
  }
  return true;

} // end handleKeyDown()

//
// Function handleKeyPress() is a member function to handle keypress events for the control.
// This function is required to prevent browser that manipulate the window on keypress (such as Opera)
// from performing unwanted scrolling.
//
// @param (e object) e is the event object
//
// @return (boolean) Returns false if consuming; true if propagating
//
spinbutton.prototype.handleKeyPress = function(e) {


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

  switch(e.keyCode) {
    case this.keys.pageup:
    case this.keys.pagedown:
    case this.keys.home:
    case this.keys.end:
    case this.keys.left:
    case this.keys.up:
    case this.keys.right:
    case this.keys.down: {
      // consume the event
      e.stopPropagation();
      return false;
    }
  }
  return true;

} // end handleKeyPress()

//
// Function handleFocus() is a member function to handle focus events for the control.
//
// @param (e object) e is the event object
//
// @return (boolean) Returns true
//
spinbutton.prototype.handleFocus = function(e) {

  // add the focus styling class to the control
  this.$id.addClass('focus');

  return true;

} // end handleFocus()

//
// Function handleBlur() is a member function to handle blur events for the control.
//
// @param (e object) e is the event object
//
// @return (boolean) Returns true
//
spinbutton.prototype.handleBlur = function(e) {

  // Remove the focus styling class from the control
  this.$id.removeClass('focus');

  return true;

} // end handleBlur()