(function() { //get a reference to the password field and define a data attribute //that we can switch to indicate whether the password should be heard var password = document.getElementById('password'); password.setAttribute('data-hearing-type', 'password'); //create an assertive live region to speak the password value //and hide it with off-left positioning so it's not otherwise visible var speaker = document.createElement('span'); speaker.setAttribute('aria-live', 'assertive'); speaker.setAttribute('style', 'position:absolute;left:-1000em;'); //append the live region after the password field //n.b. this will still be part of the screenreaders normal reading order //however form navigation is usually limited to tabbing between fields //and since it's not in the tab order it won't be reached that way password.parentNode.appendChild(speaker); //bind an input event to the password field password.addEventListener('input', function() { //if the password field should be heard, copy its value to the speaker if(password.getAttribute('data-hearing-type') == 'text') { speaker.innerHTML = password.value; } }, false); //create a trigger button to switch the hear functionality on and off //defining two data attributes to store the language for each state //and then setting the initial text to match the default off state var hear = document.createElement('button'); hear.type = 'button'; hear.setAttribute('data-hearing-password', 'Hear Password'); hear.setAttribute('data-hearing-text', 'Mute Password'); hear.appendChild(document.createTextNode(hear.getAttribute('data-hearing-password'))); //append the button after the password field and speaker password.parentNode.appendChild(hear); //bind a click event to the button hear.addEventListener('click', function(e) { //invert the current hearing state between on ("text") and off ("password") var type = password.getAttribute('data-hearing-type') == 'password' ? 'text' : 'password'; //apply the new state to the password field and update the button language password.setAttribute('data-hearing-type', type); hear.firstChild.nodeValue = hear.getAttribute('data-hearing-' + type); //if the type is "text" copy the password to the speaker, else clear it speaker.innerHTML = type == 'text' ? password.value : ''; //prevent any default action (e.g. some browsers might submit the form) e.preventDefault(); }, false); })();
<form action="#"> <fieldset> <legend>Login information</legend> <p> <label for="username">Username:</label> <input name="username" id="username" type="text" value="" /> </p> <p> <label for="password">Password:</label> <input name="password" id="password" type="password" value="" /> </p> </fieldset> </form>