Live Region Playground

Live Region Playground

Turn on a screen reader to experience this example in action.

Page Contents:

Configuration Options

How do you want to configure your live region?
(values for aria-live, aria-atomic and aria-relevant will be auto-populated)
Trigger content change:
Content change will alternate between additions and modifications with the exception of content removal every 10th iteration.

Live Region Output:

Static Content

The changing content will take place below.

Reference:

Mapping of Roles to Live Region Attributes
Role aria-live aria-atomic aria-relevant
Alert assertive true Not dictated by spec (defaults to "text additions").
log polite Not dictated by spec (defaults to "false"). Not dictated by spec (logically it should be "additions").
marquee off Not dictated by spec (defaults to "false"). Not dictated by spec (defaults to "text additions").
status polite true Not dictated by spec (defaults to "text additions").
timer off Not dictated by spec (defaults to "false"). Not dictated by spec (defaults to "text additions").

HTML Source Code

<div class="liveRegionPlayground">
    <div id="loading" tabindex="-1" style="display: none;">Please wait while options are configured...</div>
    <fieldset id="live-config">
      <legend>How do you want to configure your live region?</legend>
      <div class="form">
        <div>
          <input type="radio" id="default" value="default" name="cus-def" aria-describedby="role-help">
          <label for="default">Use role defaults</label>
          <span id="role-help" class="helper">(values for aria-live, aria-atomic and aria-relevant will be auto-populated)</span>
        </div>
        <div>
          <input type="radio" id="custom" value="custom" name="cus-def" checked="checked">
          <label for="custom">Create custom live region</label>
        </div>
      </div>
      <div class="form">
        <label for="role">role:</label>
        <select id="role">
          <option selected="selected">status</option>
          <option>alert</option>
          <option>log</option>
          <option>marquee</option>
        </select>
      </div>
      <div class="field">
        <label for="aria-live">aria-live value:</label>
        <select id="aria-live">
          <option selected="selected">polite</option>
          <option>assertive</option>
          <option>off</option>
        </select>
      </div>
      <div class="field">
        <label for="aria-atomic">aria-atomic value:</label>
        <select id="aria-atomic">
          <option selected="selected">true</option>
          <option>false</option>
        </select>
      </div>
      <div class="field">
        <label for="aria-relevant">aria-relevant value:</label>
        <select id="aria-relevant">
          <option selected="selected">text</option>
          <option>additions</option>
          <option>removals</option>
          <option>all</option>
        </select>
      </div>
      <div class="field">
        <div id="trigger">Trigger content change:</div>
        <ul role="list" aria-labelledby="trigger">
          <li role="listitem">
            <input type="radio" name="trigger-type" aria-describedby="content-change-help" id="once" value="once" checked="checked">
            <label for="once">Once</label>
          </li>
          <li role="listitem">
            <input type="radio" name="trigger-type" aria-describedby="content-change-help" id="five" value="five">
            <label for="five">Every 5 seconds</label>
          </li>
          <li role="listitem">
            <input type="radio" name="trigger-type" aria-describedby="content-change-help" id="ten" value="ten">
            <label for="ten">Every 10 seconds</label>
          </li>
        </ul>
        <div id="content-change-help" class="helper">
          Content change will alternate between additions and modifications with the exception of content removal every 10th iteration.
        </div>
      </div>
      <button type="button" id="submit">Submit</button>
      <button type="button" id="clear">Clear added content</button>
      <button type="button" disabled="disabled" id="stop">Stop adding content</button>
    </fieldset>
    <h2 id="liveRegionOutput">Live Region Output:</h2>
    <div id="fixture">
      <div>
        <div id="static">
          <h3>Static Content</h3>
          <div>The changing content will take place below.</div>
        </div>
      </div>
      <div id="update"></div>
    </div>
</div>

JavaScript Source Code

Dependencies:

  • JQuery
'use strict';

/**
 * Live region playground
 *
 * @author Harris Schneiderman
 */

/* global $*/
var i = 1;
var contentChanges = 1;
var interval;
var $role = $('#role');
var $stop = $('#stop');
var $update = $('#update');
var $fixture = $('#fixture');
var $ariaLive = $('#aria-live');
var $ariaAtomic = $('#aria-atomic');
var $ariaRelevant = $('#aria-relevant');

configureRegion();

// apply attrs based on fields
$('#submit').on('click', configureRegion);

// clear the live region div (except for the "static content")
$('#clear').on('click', function () {
  $update.empty();
  i = 1;
  contentChanges = 1;
});

$('input[name="cus-def"]').on('change', function () {
  if ($('#default').is(':checked')) {
    $ariaLive.attr('disabled', 'disabled').attr('aria-disabled', 'true');
    $ariaAtomic.attr('disabled', 'disabled').attr('aria-disabled', 'true');
    $ariaRelevant.attr('disabled', 'disabled').attr('aria-disabled', 'true');
  } else {
    $ariaLive.removeAttr('disabled').removeAttr('aria-disabled', 'true');
    $ariaAtomic.removeAttr('disabled').removeAttr('aria-disabled', 'true');
    $ariaRelevant.removeAttr('disabled').removeAttr('aria-disabled', 'true');
  }
});

// stop adding content
$stop.on('click', function () {
  if (interval) {
    clearInterval(interval);
  }
});

// configure vals of others based on newly selected role
$role.on('change', onroleChange);

function configureRegion(e) {
  if (interval) {
    clearInterval(interval);
  }

  // configure attributes
  $fixture
    .attr({
      'role': $role.val(),
      'aria-live': $ariaLive.val(),
      'aria-atomic': $ariaAtomic.val(),
      'aria-relevant': $ariaRelevant.val()
    });

  // configure content insertion (if submit was clicked)
  if (e) {
    configureInsertion();
  }
}

function configureInsertion() {
  var freq = $('input[name="trigger-type"]:checked').val();

  // call `insertContent` based on frequency chosen
  if (freq === 'once') {
    $stop.attr('disabled', 'disabled');
    insertContent();
  } else {
    $stop.removeAttr('disabled');
    freq = (freq === 'five') ? 5 : 10;
    interval = setInterval(insertContent, freq * 1000);
  }
}

function insertContent() {
  if (contentChanges < 10) {
    if (isOdd(contentChanges)) {
      $update.append('<div><span class="added">Added Content</span> #' + i + '</div>');
      if (contentChanges === 9) {
        $update.append('<div>Also, more <span class="added">added content</span>! Unfortunately, I will be removed next...</div>');
      }
      i++;
    } else {
      $update.find('.added').last().html('Modified Content');
    }
  } else {
    $update.children().last().remove();
    contentChanges = 1; // reset it.
  }
  contentChanges++;
}

function onroleChange() {
  if ($('#custom').is(':checked')) {
    return;
  }

  var role = $role.val();

  // update <select /> vals based on role
  if (role == 'alert') {
    $ariaLive.val('assertive');
    $ariaAtomic.val('true');
    $ariaRelevant.val('text');
  } else if (role == 'log') {
    $ariaLive.val('polite');
    $ariaAtomic.val('false');
    $ariaRelevant.val('text');
  } else if (role == 'status') {
    $ariaLive.val('polite');
    $ariaAtomic.val('true');
    $ariaRelevant.val('text');
  } else if (role == 'marquee') {
    $ariaLive.val('off');
    $ariaAtomic.val('false');
    $ariaRelevant.val('text');
  }

  // update the attributes right away
  configureRegion();
}

function isOdd(n) {
  return Math.abs(n) % 2 == 1;
}

CSS Source Code



.liveRegionPlayground label, .liveRegionPlayground #trigger, .liveRegionPlayground #custom-default {
  font-size: 1em;
}

.liveRegionPlayground .helper {
  font-style: italic;
}

.liveRegionPlayground select {
  background-color: #eee;
  font-size: 1em;
}

.liveRegionPlayground ul {
  margin: 2px;
}

.liveRegionPlayground li {
  list-style-type: none;
}

.liveRegionPlayground .inner-field {
  font-size: 1em;
  margin-left: 20px;
}

.liveRegionPlayground #fixture {
  width: 85%;
  margin: 0 auto;
  text-align: center;
  border: 5px solid #000;
  height: 300px;
  overflow: scroll;
  padding: 11px;
}

.liveRegionPlayground #loading {
  position: absolute;
  width: 500px;
  padding: 25px;
  text-align: center;
  background-color: #000;
  color: #fff;
  border: 10px double #fff;
}

.liveRegionPlayground #mean {
  position: absolute;
  clip: rect(1px 1px 1px 1px);
  clip: rect(1px, 1px, 1px, 1px);
  overflow: hidden;
  width: 1px;
  height: 1px;
}

Copy and Paste Full Page Example