Mask and unmask a password input
Tags: javascript, html
Published: 2021-06-30 10:09:49

I recently worked on a feature to be able to mask and unmask a password using some vanilla javascript, and I thought I'd share how I did this.  

![Password Mask](https://user-images.githubusercontent.com/2786819/123863212-96b0fc80-d929-11eb-92ed-7f70e27a1c85.gif)

If you want to jump ahead and just see the code without the walkthrough, you can view the code on [Codepen](https://codepen.io/Ridhwana/pen/vYmBrbx).

# Step 1:

THE HTML: 

Let's put some HTML together for a password field. In most instances that will form as part of a `form` but in this case I'm just going to go ahead and only show the `div`  for the password.

```html
<div>
  <label>Password</label>
  <div class="password-input-container">
    <span class="eye-container js-password-visibility-toggle">
      <span class="js-eye">
        <svg width="22" height="18" viewBox="0 0 22 18" fill="none" xmlns="http://www.w3.org/2000/svg">
          <path d="M11 0c5.392 0 9.878 3.88 10.819 9-.94 5.12-5.427 9-10.82 9C5.609 18 1.123 14.12.182 9 1.12 3.88 5.608 0 11 0zm0 16a9.005 9.005 0 0 0 8.777-7A9.005 9.005 0 0 0 2.223 9 9.005 9.005 0 0 0 11 16zm0-2.5a4.5 4.5 0 1 1 0-9 4.5 4.5 0 0 1 0 9zm0-2a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5z" fill="#64707D"/>
        </svg>
      </span>
      <span class="js-eye-off hidden">
        <svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
          <path d="M16.882 18.297A10.95 10.95 0 0 1 11 20C5.608 20 1.122 16.12.18 11a10.982 10.982 0 0 1 3.34-6.066L.393 1.808 1.807.393l19.799 19.8-1.415 1.414-3.31-3.31zM4.935 6.35A8.965 8.965 0 0 0 2.223 11a9.006 9.006 0 0 0 13.2 5.838l-2.027-2.028A4.5 4.5 0 0 1 7.19 8.604L4.935 6.35zm6.979 6.978-3.242-3.242a2.5 2.5 0 0 0 3.24 3.241l.002.001zm7.893 2.264-1.431-1.43a8.936 8.936 0 0 0 1.4-3.162A9.006 9.006 0 0 0 8.553 4.338L6.974 2.76C8.22 2.27 9.58 2 11 2c5.392 0 9.878 3.88 10.819 9a10.95 10.95 0 0 1-2.012 4.592zm-9.084-9.084a4.5 4.5 0 0 1 4.769 4.77l-4.77-4.77z" fill="#64707D"/>
        </svg>
      </span>  
    </span>
    <input class="js-password" type="password"/>
  </div> 
</div>
```

The  main element to take note of is the `password-input-container`. It contains two elements: 

- The `eye-container` with two spans that each contain an SVG (an `eye` and an `eye-off` SVG). The `eye-off` SVG will be hidden by default because the password is masked in its default state.
- An input field with type `password`.

I tend to still follow [BEM Notation](https://csswizardry.com/2015/03/more-transparent-ui-code-with-namespaces/) for any classnames that I write. Hence, you will see that some classnames have a JavaScript namespace, and are therefore prepended with `js-` . This is a verbose indicator that this piece of the DOM has some behaviour acting upon it, and that JavaScript binds onto it to provide that behaviour. Hence, it reduces the risk of someone editing or removing the classname mistakenly without realizing that some javascript code depends on it. 

Take note of the `js-` prepended classnames as this will allow us to bind the correct masking and unmasking behaviour in the JavaScript code. 

# Step 2:

THE JAVASCRIPT

```jsx
let visible = false;
const eyeIcon = document.getElementsByClassName('js-eye')[0];
const eyeOffIcon = document.getElementsByClassName('js-eye-off')[0];
const passwordField = document.getElementsByClassName('js-password')[0];
const visibilityToggle = document.getElementsByClassName(
  'js-password-visibility-toggle',
)[0];
visibilityToggle.addEventListener('click', togglePasswordMask);

function togglePasswordMask() {
  visible = !visible;
  togglePasswordType(visible)
  toggleEyeIcons(visible);
}

function togglePasswordType(visible)  {
  const passwordType = visible ? 'text' : 'password';
  passwordField.type = passwordType;
}

function toggleEyeIcons(visible) {
  eyeOffIcon.classList.toggle("hidden", !visible );
  eyeIcon.classList.toggle("hidden", visible );
}
```

Let's walkthrough the code:

```jsx
const visibilityToggle = document.getElementsByClassName(
  'js-password-visibility-toggle',
)[0];
visibilityToggle.addEventListener('click', togglePasswordMask);
```

We first search the DOM for the classname `js-password-visibility-toggle`. `js-password-visibility-toggle` is the container that contains the `eye` icons (one hidden and one not). 

We then use add `addEventListener` to listen for a  `click` on the element. When the user clicks on the element the function `togglePasswordMask` will be called.

The reason we do not add event listeners on the individual SVG `span` is because then we'll need to add two eventListeners to the DOM (one for the `eye` and another for the `eye-off`) and each of the callbacks will be doing something similar. Instead, we allow the trigger on the container and use a "sort of state" variable to figure out whether we're masking or unmasking.

```jsx
let visible = false;
function togglePasswordMask() {
  visible = !visible;
  togglePasswordType(visible)
  toggleEyeIcons(visible);
}
```

The first time that we load the form the password is not visible, hence we set `visible` to `false` initially. 

Each time we click on the `eye` icon, we toggle visible to negate its current value using `visible = !visible`.

When we click we want 

a) the password to be revealed, i.e. `togglePasswordType`, and 

b) the icon we clicked on to change - i.e. `toggleEyeIcons`.

```jsx
const passwordField = document.getElementsByClassName('js-password')[0];

function togglePasswordType(visible)  {
  const passwordType = visible ? 'text' : 'password';
  passwordField.type = passwordType;
}
```

`togglePasswordType` simply sets the input type to `text` or `password`  depending if we want to mask or unmask the password. 

```jsx
const eyeIcon = document.getElementsByClassName('js-eye')[0];
const eyeOffIcon = document.getElementsByClassName('js-eye-off')[0];

function toggleEyeIcons(visible) {
  eyeOffIcon.classList.toggle("hidden", !visible );
  eyeIcon.classList.toggle("hidden", visible );
}
```

`toggleEyeIcons` add and remove the `hidden` class name depending on whether the password is visible or not.

![](https://media.giphy.com/media/lD76yTC5zxZPG/giphy.gif)

That wraps it up for the code, I hope that was useful and easy to follow. Feel free to drop comments or questions below. 👇🏽