1

WebSpeechAPI to make a site accessible but it first starts with the whole document and then the hovered element again.

How to resolve this?

I referenced most of the code from WebSpeechAPI MDN page.

All I want is for the browser to output the text of the tag that I am hovering over.

But it does that after speaking out all the contents of the document first. I think it does that because it catches the document first before I can reach the element.

            var synth = window.speechSynthesis;
            var inputForm = document.querySelector('form');
            var inputTxt = document.querySelector('.txt');
            var voiceSelect = document.querySelector('select');
            var title = document.querySelector('#title');
            var pitch = document.querySelector('#pitch');
            var pitchValue = document.querySelector('.pitch-value');
            var rate = document.querySelector('#rate');
            var rateValue = document.querySelector('.rate-value'); 
 
          var voices = [];  //creat aan array to get thev voices  

       function populateVoiceList() {
         voices = synth.getVoices(); // get the voices form the browser

         for (i = 0; i < voices.length; i++) {
          var option = document.createElement('option');    //create an element named option
          option.textContent = voices[i].name + ' (' + voices[i].lang + ')';   //get all the info about the voice from the device and store in the text of the option tag
          if (voices[i].default) {
           option.textContent += ' -- DEFAULT';
           }
           option.setAttribute('data-lang', voices[i].lang); //set attributes of the option tag
           option.setAttribute('data-name', voices[i].name);
           voiceSelect.appendChild(option);
         }
       }
        populateVoiceList();
        if (speechSynthesis.onvoiceschanged !== undefined) {    // this handler gets fired when the list returned by the getVoices function get changed
             speechSynthesis.onvoiceschanged = populateVoiceList;   //requires a function to handle the change in the list
         }
       document.onmouseover = function(e)  {
         var targ;
        event.preventDefault();    //prevent default actions of the browser 
        
        if (e.target) targ = e.target;

         var utterThis = new    SpeechSynthesisUtterance(targ.textContent); //The SpeechSynthesisUtterance interface of the Web Speech API represents a speech request. 
          var selectedOption = voiceSelect.selectedOptions[0].getAttribute('data-name');  //get the data-name attribute of the selected option 

      for (i = 0; i < voices.length; i++) {
         if (voices[i].name === selectedOption) {
            utterThis.voice = voices[i];     //. We set the matching voice object to be the value of the  SpeechSynthesisUtterance.voice property.
        }
     }

       utterThis.pitch = pitch.value;
      utterThis.rate = rate.value;
      synth.speak(utterThis);
       pitch.onchange = function() {
         pitchValue.textContent = pitch.value;
      }

      rate.onchange = function() {
         rateValue.textContent = rate.value;
       }
    }
<!DOCTYPE html>
<html lang="en">
    <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title>Document</title>
    </head>

    <body>
     <h1 id="Speech sYNTHESIZER" >Speech synthesiser</h1>

     <p>Enter some text in the input below and press return to hear it. change voices using the dropdown menu.</p>

     <form>
      <input type="text" class="txt">
          <div>
            <label for="rate">Rate</label><input type="range" min="0.5" max="2" value="1" step="0.1" id="rate">
            <div class="rate-value">1</div>
            <div class="clearfix"></div>
     </div>
      <div>
        <label for="pitch">Pitch</label><input type="range" min="0" max="2" value="1" step="0.1" id="pitch">
        <div class="pitch-value">1</div>
        <div class="clearfix"></div>
      </div>
      <select></select>
       </form>    
   
    </body>
 </html>
Not A Bot
  • 2,474
  • 2
  • 16
  • 33
Deepak Pawade
  • 150
  • 1
  • 13

2 Answers2

3

All I want is for the browser to output the text of the tag that I am hovering over.

You are starting from a legitimately good intention, but in fact except if you are making a special game or an innovative interface, it's a bad idea. Accessibility on the web simply doesn't work like this. You'd better try to conform to standards like WCAG to make your site accessible.

Several reasons for this, at least two big ones:

  • Finding elements to be spoken with the mouse is a possibility, but isn't the common way to navigate on the web.
    • Blind people generally don't use a mouse because they don't care about the placement of elements on the screen, can quickly get lost or miss important information that way. They just need to have them appear logically when navigating with tab, heading by heading or by another mean provided by the screen reader.
    • For partially sighted users, using the mouse to read elements below the cursor is of help or not depending on their vision, but for the same reasons as blind users, it's often just a complementary help; and screen reader software have the feature built-in.
  • Screen reader users have their preferences about language, voice, rate, pitch, etc. fortunately they don't need to set them for each site they visit

So, unless you are making something really special or new, prefer stick to largely used means to access your site.

But it does that after speaking out all the contents of the document first. I think it does that because it catches the document first before I can reach the element.

This is probably because of event bubbling.

QuentinC
  • 12,311
  • 4
  • 24
  • 37
  • so it's better to use tabs to navigate and simultaneously read the contents on the website. – Deepak Pawade Mar 12 '20 at 05:45
  • 2
    you missed the point of @QuentinC's answer, don't try and implement this at all. People who need assistive devices will use them (NVDA, VoiceOver etc.). Trying to implement them yourself will make things 100 times worse rather than better. Instead learn WCAG, semantic HTML etc. as those are the things that will really make your site accessible. – GrahamTheDev Mar 12 '20 at 08:31
  • @GrahamRitchie I saw most of the websites using NVDA and thought of integrating the feature within the website. This might save the hassle of installing the software. – Deepak Pawade Mar 12 '20 at 08:33
  • @GrahamRitchie I have decided to first make the website accessible as per rules of WCGA 2.1 and leave this problem as the last thing to do , if at all possible . – Deepak Pawade Mar 12 '20 at 08:42
  • Perfect, once you have done everything else you may find you don't need this, and if you do then you will have a much easier time. Good luck with the site! – GrahamTheDev Mar 12 '20 at 11:48
  • 1
    This might save the hassle of installing the software => Wrong. Jaws or NVDA re certainly already installed on the computer of a partially sighted user anyway – QuentinC Mar 12 '20 at 16:04
  • VoiceOver is preinstalled on all Mac/iOS devices, and Narrator is bundled with Windows 10, whether the user is partially sighted or not. NVDA is a painless download/install The 'hassle' of installing a screen reader is a non-problem. – brennanyoung Mar 13 '20 at 10:42
  • There is a use case for WebSpeech - Audio Descriptions driven by .vtt – brennanyoung Mar 13 '20 at 10:43
  • There is a use case for WebSpeech - Audio Descriptions driven by .vtt => Maybe; but it can also be done using aria-live. I already saw videos on youtube doing it with aria-live. – QuentinC Mar 13 '20 at 16:45
0

I am not qualified to comment on the correctness of your accessibility attempt. Other answers are best for that.

Since you mentioned that the whole document is being read, I think it is because you are attaching mouseover event to the document at:

document.onmouseover = function(e)  { ... };

With my knowledge and my ES6 syntax, I have written down the following code to actually select all individual tags instead of document.

const tags = document.body.querySelectorAll();

//Adds mouseover event listener to each tags in tags
for(let i=0; i<tags.length(); i++){
    let text = tags[i].textContent;
    tags[i].addEventListener('mouseover', function(text){
        //add code to speak the 'text' variable here
    });
}

Basically, I used querySelectorAll to get all the tags to tags array. Next, looped over each tag to extract the textContent for each tag. Finally added event listeners to each tag in tags to run the speaking function whenever mouseover is trigerred.

Hope it helps!

Get all elements in the body tag using pure javascript

Yaksha
  • 153
  • 1
  • 6