If you were to look at PayPal's own code which you linked to, you'd see it's just a matter of calling input.focus() in a single event-listener:
function hidePassword(e) {
if (baseType === 'tel') {
$(field).addClass('tel-password');
} else {
field.setAttribute('type', 'password');
}
$(btnShow).removeClass('hide');
$(btnHide).addClass('hide');
field.focus();
e.stopPropagation();
login.logger.log({
evt: 'is_pwd_sh',
data: 'N',
instrument: true
});
login.logger.pushLogs();
}
Which can be simplified down to my example below, with some alterations:
- The default
type="" should always be password. Users expect password inputs to not suddenly show the password-they're-typing-in to everyone who can see their screens (especially when screen-sharing or projecting to a TV or giving a presentation, etc).
- I've changed the
querySelector call to embedding the target <input type="password" /> in <button data-for="" /> to avoid problems caused by running querySelector or getElementById before the DOMContentLoaded event, as is often the case with JS snippets people post around the Internet.
- The calls to
event.stopPropagation() (in PayPal's code) or event.preventDefault() (in your code) are superfluous and unnecessary. The important thing is calling input.focus() in the same event-handler that changes the HTMLInputElement.type property.
- Avoid using
onmousedown with buttons. The 'click' event works for all kinds of interactions (mouse, keyboard, touch, etc) while the 'mousedown' and 'mouseclick' events are only raised by actual mouse input (though often touch too).
function toggleVisibilityOnClick( event ) {
const button = event.currentTarget;
const input = document.getElementById( button.dataset['for'] );
if( !input ) throw new Error( "Couldn't find password input." );
if( input.type === 'text' ) {
input.type = 'password';
}
else {
input.type = 'text';
}
input.focus();
}
<input placeholder="Password" type="password" id="pwdInp" />
<button data-for="pwdInp" onclick="toggleVisibilityOnClick(event)">Show Password</button>
Microsoft's browsers have -ms-reveal, which makes your toggle redundant, so you should hide the toggle in that case:
@supports selector(::-ms-reveal) {
input[type="password"] + button[data-for] {
display: none;
}
}