Forms – Character count

An example character count for a textarea.

Screenreaders should read out useful information at appropriate times.

A useful form item description.
Characters remaining: 50

Example HTML

<div class="l-form-item-container">
  <div class="v5-form-item">
    <label for="ExampleTextArea" class="v5-form-item__label hide-element">Example text area</label>
    <textarea id="ExampleTextArea" name="ExampleTextArea" rows="12" cols="80" aria-describedby="ExampleTextAreaDescription"></textarea>
    <div id="ExampleTextAreaDescription" class="v5-form-item__description">A useful form item description.</div>
    <div id="ExampleTextAreaCharacterCount" class="v5-form-item__description">Characters remaining: <strong id="CharacterCount">50</strong><span aria-live="polite" id="CharacterCountAriaLive" class="hide-element"></span></div>
  </div>
</div>

Example JavaScript

document.addEventListener('designFrameworkReady', function(e) {
  var ExampleTextArea = document.getElementById('ExampleTextArea') // This is the textarea.
  var CharacterCount = document.getElementById('CharacterCount') // This is the visible character count.
  var CharacterCountAriaLive = document.getElementById('CharacterCountAriaLive') // This is a visually hidden message area that uses aria-live.
  var DebounceId // This will hold the reference to the timeout used to debounce updates.

  CharacterCount.innerText = 50 - ExampleTextArea.value.length
  
  ExampleTextArea.addEventListener('keyup', function(e) {
    // Always clear the timeout to start.
    window.clearTimeout(DebounceId)

    var remaining = 50 - ExampleTextArea.value.length
    CharacterCount.innerText = remaining

    if (ExampleTextArea.value.length > 50) {
      // Use a named timeout with a delay of some millisceonds
      // so we don't get a rush of messsages when we're listening for
      // every keystroke and the user is typing quickly.
      DebounceId = window.setTimeout(function() {
        CharacterCountAriaLive.innerText = 'Character count exceeded by: ' + (ExampleTextArea.value.length - 50)
      }, 100)
    }
    // Listen for just the enter and backspace keys to read out the character count
    // as that seems like a sensible balance.
    else if (e.key === 'Enter' || e.key === 'Backspace') {
      CharacterCountAriaLive.innerText = 'Characters remaining: ' + remaining
    } else {
      CharacterCountAriaLive.innerText = ''
    }
  })
})