Is it possible to prevent moving the cursor to the start of an input field when type changes?

Viewed 301

I'm currently creating a Password field with an option to hide or display the password in React/Ionic.

  • I want to keep focus on the password field when you change the type of the input. (like PayPay Login page does) I'm doing it with event.preventDefault() on mousedown-Event.

const input = document.querySelector('input')

function toggleVisibility() {
  if (input.type === "text")
    input.type = "password"
  else
    input.type = "text"
}

function keepFocus(event) {
  event.preventDefault()
}
<input placeholder="Password" type="text">
<button onmousedown="keepFocus(event)" onclick="toggleVisibility()">Show Password</button>

Problem

  • When I change the type of the input field the cursor flips back to the start of the input field. I would like to keep the cursor where it was before. (Again like on PayPal)
1 Answers

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;
    }
}
Related