web-dev-qa-db-fra.com

Obtenir un lecteur d'écran pour lire le nouveau contenu ajouté avec JavaScript

Lorsqu'une page Web est chargée, les lecteurs d'écran (comme celui fourni avec OS X ou JAWS sous Windows) liront le contenu de la page entière. Mais disons que votre page est dynamique et que lorsque les utilisateurs effectuent une action, un nouveau contenu est ajouté à la page. Par souci de simplicité, disons que vous affichez un message quelque part dans un <span>. Comment pouvez-vous amener le lecteur d'écran à lire ce nouveau message?

29
avernet

La spécification WAI-ARIA définit plusieurs façons par lesquelles les lecteurs d'écran peuvent "regarder" un élément DOM. La meilleure méthode prise en charge est la aria-live attribut. Il a les modes off, polite, assertive et rude. Plus le niveau d'assertivité est élevé, plus il est probable d'interrompre ce qui est actuellement prononcé par le lecteur d'écran.

Ce qui suit a été testé avec NVDA sous Firefox 3 et Firefox 4.0b9:

<!DOCTYPE html>
<html>
<head>
  <script src="js/jquery-1.4.2.min.js"></script>
</head>
<body>
  <button onclick="$('#statusbar').html(new Date().toString())">Update</button>
  <div id="statusbar" aria-live="assertive"></div>
</body>

La même chose peut être accomplie avec WAI-ARIA rôlesrole="status" et role="alert". J'ai eu des rapports d'incompatibilité, mais je n'ai pas pu les reproduire.

<div id="statusbar" role="status">...</div>
31

Voici un exemple adapté du monde réel - ce balisage de haut niveau a déjà été converti d'une liste non ordonnée avec des liens en un menu de sélection via JS. Le vrai code est beaucoup plus complexe et ne peut évidemment pas être inclus dans son intégralité, alors rappelez-vous que cela devra être repensé pour une utilisation en production. Pour que le menu de sélection soit rendu accessible au clavier, nous avons enregistré les événements keypress & onchange et déclenché l'appel AJAX lorsque les utilisateurs ont quitté la liste) (méfiez-vous des différences de navigateur dans la synchronisation de l'événement onchange) C'était un PITA sérieux à rendre accessible, mais il IS possible.

  //  HTML

  <!-- select element with content URL -->
  <label for="select_element">State</label>
  <select id="select_element">
     <option value="#URL_TO_CONTENT_PAGE#" rel="alabama">Alabama</option>
  </select>
  <p id="loading_element">Content Loading</p>

  <!-- AJAX content loads into this container -->
  <div id="results_container"></div>


  // JAVASCRIPT (abstracted from a Prototype class, DO NOT use as-is)

  var selectMenu = $('select_element');
  var loadingElement = $('loading_element');
  var resultsContainer = $('results_container');

 // listen for keypress event (omitted other listeners and support test logic)
  this.selectMenu.addEventListener('keypress', this.__keyPressDetector, false);


 /* event callbacks */

 // Keypress listener

  __keyPressDetector:function(e){

    // if we are arrowing through the select, enable the loading element
    if(e.keyCode === 40 || e.keyCode === 38){
        if(e.target.id === 'select_element'){
            this.loadingElement.setAttribute('tabIndex','0');
        }
    }
    // if we tab off of the select, send focus to the loading element
    //  while it is fetching data
     else if(e.keyCode === 9){
        if(targ.id === 'select_element' && targ.options[targ.selectedIndex].value !== ''){            
            this.__changeStateDetector(e);

            this.loadingElement.focus();

        }   
    }
}

// content changer (also used for clicks)
__changeStateDetector:function(e){

    // only execute if there is a state change
    if(this.selectedState !== e.target.options[e.target.selectedIndex].rel){

       // get state name and file path
       var stateName = e.target.options[e.target.selectedIndex].rel;
       var stateFile = e.target.options[e.target.selectedIndex].value;

       // get the state file
       this.getStateFile(stateFile);

       this.selectedState = stateName;

    }
}

getStateFile:function(stateFile){
    new Ajax.Request(stateFile, {
        method: 'get',
        onSuccess:function(transport){      

            // insert markup into container
            var markup = transport.responseText;

            // NOTE: select which part of the fetched page you want to insert, 
            // this code was written to grab the whole page and sort later

            this.resultsContainer.update(markup);

            var timeout = setTimeout(function(){

                // focus on new content
               this.resultsContainer.focus();

            }.bind(this), 150);

        }.bind(this)
    });
}
1
Marcy Sutton