Focus / Blur Test: Input [button] Elements

This page provides several tests to illustrate how a browser handles the focus and blur events for <input> elements with type=button. Each test in the grid below has uniquely defined properties. The first test is the default element with no tabindex defined. Each subsequent test specifies a value for tabindex, with the last one being an undefined (i.e. invalid) value.

onfocus and onblur event handlers have been bound to each of the test case controls. If triggered, an event handler will log the event in the Test Results box to the right of the test grid. The event handlers do not consume the event (i.e. they allow propagation). This allows the test to record the event but not affect the browser's default behavior.

The reset buttons will clear the Test Results.

Test Properties  
1  
2 tabindex = "0"
3 tabindex = "-1"
4 tabindex = ""
Test Results

Previously Found Results

Browser OS Element Properties Test Results Notes
focus blur visual focus visual blur
 

HTML Source Code

Show HTML Source Code: input-button.inc

<div id="description">
<p>This page provides several tests to illustrate how a browser handles the focus and blur events for &lt;input&gt; elements with type=button. Each test in the grid below has uniquely defined properties. The first test is the default element with no tabindex defined. Each subsequent test specifies a value for tabindex, with the last one being an undefined (i.e. invalid) value.</p>
<p>onfocus and onblur event handlers have been bound to each of the test case controls. If triggered, an event handler will log the event in the Test Results box to the right of the test grid. The event handlers do not consume the event (i.e. they allow propagation). This allows the test to record the event but not affect the browser's default behavior.</p>
<p>The reset buttons will clear the Test Results.</p>
</div>

<div class="wrapper" role="application">
<div class="test-wrapper">
<table class="tests">
<thead>
<tr>
<th>Test</th>
<th>Properties</th>
<th width="180">&nbsp;</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>&nbsp;</td>
<td>
<input id="input-button1" type="button" value="Button 1" class="test-control">
<button id="input-button-reset1" type="button" class="test-reset" role="button" aria-controls="input-button-results">
Reset<span class="offscreen">test results</span>
</button>
</td>
</tr>
<tr>
<td>2</td>
<td>tabindex = "0"</td>
<td>
<input id="input-button2" type="button" value="Button 2" class="test-control" tabindex="0">
<button id="input-button-reset2" type="button" class="test-reset" role="button" aria-controls="input-button-results">
Reset<span class="offscreen">test results</span>
</button>
</td>
</tr>
<tr>
<td>3</td>
<td>tabindex = "-1"</td>
<td>
<input id="input-button3" type="button" value="Button 3" class="test-control" tabindex="-1">
<button id="input-button-reset3" type="button" class="test-reset" role="button" aria-controls="input-button-results">
Reset<span class="offscreen">test results</span>
</button>
</td>
</tr>
<tr>
<td>4</td>
<td>tabindex = ""</td>
<td>
<input id="input-button4" type="button" value="Button 4" class="test-control" tabindex="">
<button id="input-button-reset4" type="button" class="test-reset" role="button" aria-controls="input-button-results">
Reset<span class="offscreen">test results</span>
</button>
</td>
</tr>
</tbody>
</table>
</div>

<div class="result-wrapper">
<span id="test-results-heading" class="heading">Test Results</span>
<button id="input-button-reset*" type="button" class="test-reset" role="button" aria-controls="input-button-results">
Reset<span class="offscreen">test results</span>
</button>
<div class="results">
<table id="input-button-results" class="test-results"
aria-labelledby="test-results-heading"
aria-live="polite"
aria-atomic="false"
aria-relevant="additions"
aria-channel="main"
role="log"
>
<tbody></tbody>
</table>
</div>
</div>
</div>

<h2 id="previous-results-header">Previously Found Results</h2>
<table id="results" border="1px" aria-labelledby="previous-results-header">
<thead>
<tr>
<th rowspan="2" class="result-header sort-alpha">Browser</th>
<th rowspan="2" class="result-header sort-alpha">OS</th>
<th rowspan="2" class="result-header sort-alpha">Element Properties</th>
<th rowspan="2" class="result-header sort-alpha">Test</th>
<th colspan="4" class="result-header">Results</th>
<th id="notes" rowspan="2" class="result-header">Notes</th>
</tr>
<tr>
<th class="result-header sort-alpha">focus</th>
<th class="result-header sort-alpha">blur</th>
<th class="result-header sort-alpha">visual focus</th>
<th class="result-header sort-alpha">visual blur</th>
</tr>
</thead>
<tbody>
</tbody>
</table>

Javascript Source Code

Show Javascript Source Code: input-button.js

<script type="text/javascript">
$(document).ready(function () {

/*-------------- Input [button] tests ---------------------------*/

// bind the input [button] test focus handlers
$('#input-button1, #input-button2, #input-button3, #input button4').focus(function (e) {
return handleEvent(e, this, 'focus', 'input-button-results', true, false);
});

// bind the input [button] test blur handlers
$('#input-button1, #input-button2, #input-button3, #input button4').blur(function (e) {
return handleEvent(e, this, 'blur', 'input-button-results', true, false);
});

// load the result database
$.getJSON('data/input-button.json', function(data, rslt) {

if (rslt=='success') {
loadData(data);
makeSortable('#results');
}
});

/*
* function handleEvent() appends a row to the target table. Called by event handlers to process focus events
*
* @param (e object) The browser.event object
*
* @param (id object) The element that fired the event
*
* @param (event string) The name of the event that called the handler
*
* @param (target string) The id of the target table where results will be appended
*
* @param (propagate boolean) true if allowing propagation, false if consuming event
*
* @param (preventDefault boolean) true if preventing default behavior, false otherwise
*
* @return true if propagating, false if consuming event
*/
function handleEvent(e, id, event, target, propagate, preventDefault) {

// append the result row to the target table
$('#' + target + ' > tbody').append('<tr><td class="event">' + event + ':</td><td>' + $(id).attr('id') + '</td></td>');

// if preventDefault is true, set the preventDefault property of the event
if (preventDefault == true) {
e.preventDefault = true;
}

// if propagate is false, set the stopPropagation property of the even and return false
if (propagate == false) {
e.stopPropagation = false;
return false;
}

return true;

} // end handleEvent()

//
// reset button click handler
//
$('.test-reset').click(function () {
var resultId = '#' + $(this).attr('id').substr(0,$(this).attr('id').length - 7) + '-results';
$(resultId + ' tr').remove();
}); // end reset button click handler

/*
* function loadData() is a callback for a getJSON call to load the result data from a json file
* and append it to a table in the html page
*
* @param (data array) data is the array of data records loaded from the file
*
* @param (rslt string) rslt is the result of the load operations.
*
* @return N/A
*/
function loadData(data) {
var row;
var invalid = "";

$.each(data.tests, function(ndx, record) {

if (record.valid == false) {
invalid = ' class="invalid"';
}
else
{
invalid = '';
}

row = '<tr>';
row += '<th class="browser-col' + (record.valid == false ? ' invalid"' : '"') + '>' + record.browser + '</th>';
row += '<td' + invalid + '>' + record.os + '</td>';
row += '<td class="align-left' + (record.valid == false ? ' invalid"' : '"') + '>' + record.properties + '</td>';
row += '<td' + invalid + '>' + record.test+ '</td>';

if (record.valid == false) {
// This test was not valid, enter '-' in the results cells
row += '<td' + invalid + '>-</td><td' + invalid + '>-</td><td' + invalid + '>-</td><td' + invalid + '>-</td>';
}
else {
if (record.focus == true) {
row += '<td>X</td>';
}
else {
row += '<td>&nbsp;</td>';
}

if (record.blur == true) {
row += '<td>X</td>';
}
else {
row += '<td>&nbsp;</td>';
}

if (record.visual_focus == true) {
row += '<td>X</td>';
}
else {
row += '<td>&nbsp;</td>';
}

if (record.visual_blur == true) {
row += '<td>X</td>';
}
else {
row += '<td>&nbsp;</td>';
}
}

row += '<td class="align-left' + (record.valid == false ? ' invalid"' : '"') + '>' + record.notes + '</td></tr>';

$('#results > tbody').append(row);
});

$('#results tbody tr:odd').addClass('odd');
}

// Define a small jQuery extension to alternate table row colors
jQuery.fn.alternateRowColors = function() {
$('tbody tr:odd', this).removeClass('even').addClass('odd');
$('tbody tr:even', this).removeClass('odd').addClass('even');

return this;
};

/*
* function makeSortable() applies properties to a table necessary to allow for alpha-numeric sorting
*
* @param (id object) id is the table object to make sortable
*
* @return N/A
*/
function makeSortable(id) {
// iterate through the table elements
$(id).each(function () {
var $table = $(this);

$table.alternateRowColors();

// iterate through the table header
$('.result-header', $table).each(function(column) {

var $header = $(this);
var findSortKey;

// adjust column to handle the overlapping header rows
// (i.e. don't count Results and Notes header)
// Note: This adjust must be modified if the header format changes.
//       Remove it if single-line header
//
if (column > 5) {
column -= 2;
}

// if the cell has the sort-alpha class, find the sort keys
if ($header.is('.sort-alpha')) {
findSortKey = function ($cell) {
return $cell.find('.sort-key').text().toUpperCase()
+ ' ' + $cell.text().toUpperCase();
};
}

if (findSortKey) {
// Define the header highlighting handler
$header.find('.sort-button').addClass('clickable');
$header.hover(function() {
$header.addClass('hover');
}, function() {
$header.removeClass('hover');
});

$header.append('<button class="sort-button" role="button" type="button" tabindex="0">'
+ '<img class="sort-image" src="images/sortable.png">'
+ '<span class="offscreen">Sort ' + $header.text() + ' column</span>'
+ '</button>');

//
// bind a focus handler to the sort buttons
//
$('.sort-button').focus(function () {
$(this).css('background-color', 'black');
});

//
// bind a blur handler to the sort buttons
//
$('.sort-button').blur(function () {
$(this).css('background-color', 'transparent');
});

// Define the header click handler to perform table
// sorting when the header is clicked
$header.find('.sort-button').click(function(e) {
var sortDirection = 1;

// if the table is sorted in ascending order, reverse
// the sort order flag
if ($header.is('.sorted-asc')) {
sortDirection = -1;
}

// create an array of the table rows
var rows = $table.find('tbody >tr').get();

// iterate through the table rows and store the sort keys for each one
// in an attached expando
$.each(rows, function (index, row) {
var $cell

// The first column is marked as a header row for screen readers
// Need to treat it seperately
if (column == 0) {
$cell = $(row).children('th').eq(column);
}
else {
$cell = $(row).children('td').eq(column - 1);
}

row.sortKey = findSortKey($cell);
});

// sort the rows
rows.sort(function(a, b) {

if (a.sortKey < b.sortKey) {
// move row a before row b
// (according to the sort direction)
return -sortDirection;
}
else if (a.sortKey > b.sortKey) {
// move row a after row b
return sortDirection;
}

// the rows are equal--do nothing
return 0;
});

// iterate through the rows array and reinsert them into the table
// according to the new sort order
$.each(rows, function(index, row) {

// insert the row
$table.children('tbody').append(row);

// remove the expando
row.sortKey = null;
});

// remove the previous sorted class and reset the button images
$table.find('.result-header')
.removeClass('sorted-asc')
.removeClass('sorted-desc')
.not(this).find('img').attr('src', 'images/sortable.png');

if (sortDirection == 1) {
$header.addClass('sorted-asc');
$header.find('img').attr('src', 'images/sorted-asc.png');
}
else {
$header.addClass('sorted-desc');
$header.find('img').attr('src', 'images/sorted-desc.png');
}

// reapply the sorted class
// the first column is th for screen readers
if (column == 0) {
// remove the sorted class from the other columns
$table.find('td').removeClass('sorted')

// apply the class to the browser column
$('.browser-col').addClass('sorted');
}
else {
// remove the class from the browser column
$('.browser-col').removeClass('sorted')

// apply the class to the appropriate column
$table.find('td').removeClass('sorted')
.filter(':nth-child(' + (column + 1) + ')')
.addClass('sorted');
}

// reapply the row striping
$table.alternateRowColors();

e.preventDefault;
return false;
}); // end header click

} // end if
}); // end each
}); // end each
} // end makeSortable();
}); // end document ready
</script>

CSS Source Code

Show CSS Source Code: focus.css

<style type="text/css">
body {
padding: 20px;
}

div#description {
margin: 10px;
padding: 10px;
}

div.wrapper {
margin: 20px;
padding: 20px;
width: 90%;
overflow: hidden;
}

div.test-wrapper {
margin: 0;
padding: 0;
float: left;
display: inline;
}

div.result-wrapper {
margin: 0 5px 5px 40px;
padding: 0;
float: left;
display: inline;
overflow: visible;
width: 318px;
height: 250px;
border: 1px solid black;
text-align: right;
}

span.heading  {
margin: 0;
padding:  0 15px;
width: 60%;
text-align: center;
font-weight: bold;
float: left;
}

.test-reset {
margin: 0 5px;
padding: 0 5px;
}
.offscreen {
position: absolute;
top: -30em;
left: -300em;
}

div.test-results {
width: 100%;
height: 225px;
border-top: 1px solid black;
background-color: #ffe;
overflow: auto;
}
div.test-results span {
margin-right: 15px;
font-weight: bold;
}
p.test-result {
margin: 0;
padding: 2px 5px;
width: 96%;
border-bottom: 1px dotted #ccc;
}

h2.test-title {
margin: 5px;
padding: 0;
}

table.tests {
margin: 0 5px;
padding: 0;
width: 100%;
border-collapse: collapse;
border-spacing: 1px;
}

table.tests th {
background-color: #eef;
border: 1px solid #ccc;
}
table.tests td {
margin:0;
padding: 0 15px;
border: 1px solid #ccc;
}

div.results {
border-top: 1px solid black;
background-color: #ffe;
width: 100%;
height: 225px;
overflow: auto;
}

table.test-results {
margin: 0;
padding: 0;
border-spacing: 0;
width: 100%;
text-align: left;
}
table.test-results th {
background-color: #eef;
border-bottom: 1px solid black;
}
table.test-results td {
border-bottom: 1px dotted #ccc;
}
table.test-results td.event {
width: 4em;
padding: 0 10px;
font-weight: bold;
}

.test-control {
margin: 0px;
padding: 3px 11px;
float: left;
}

div.test-control {
margin: 0;
padding: 2px 10px;
text-align: center;
border: 1px solid black;
background-color: #eee;
float: left;
}

.highlight {
color: white !important;
background-color: black !important;
}

.focus {
padding: 0 8px !important;
border: 3px solid red !important;
}

table#results {
text-align: center;
padding: 0;
border-collapse: collapse;
}
th.result-header {
padding: 2px 8px;
background-color: #eef;
}
th#notes {
width: 20em;
}

th.browser-col {
font-weight: normal;
padding: 2px 5px;
text-align: left;
}
table#results td {
padding: 2px 5px;
}

.odd {
background-color: #ffffee;
}
.even {
background-color: transparent;
}

.invalid {
color: #999;
}

td.align-left{
text-align: left;
}

th.hover {
background-color: #ffffff !important;
}

button.sort-button {
margin: 5px;
padding: 0px;
width: 18px;
height: 19px;
border: none;
background-color: transparent;
}
img.sort-image {
margin: 0;
padding: 0;
width: 14px;
height: 15px;
position: relative;
top: 0px;
left: -1px;
}
</style>

W3C Validation of HTML5

Text Only Options

Top of page


Text Only Options

Open the original version of this page.

Usablenet Assistive is a UsableNet product. Usablenet Assistive Main Page.