When I went through the Vanilla JS Academy, my first project was a script that allows users to toggle the visibility of a password field. I then refactored it to support multiple fields, and finally multiple forms. Here’s how I did it.
This does a few things. First and foremost, it lets me keep my code out of the global scope to avoid naming conflicts with other scripts. For instance, if another script also used a variable called toggle, we’d run into trouble. This prevents that headache.
You’ll also notice I’ve declared d as a parameter of the IIFE, and passed in the document as my argument. This isn’t necessary, but it’s a nice little way of minifying commonly-used variables, such as globals. It lets me refer to the document as just d for short.
The "use strict" statement opts the function into strict mode, first introduced in ES5. As explained in the MDN Web Docs:
Prohibits some syntax likely to be defined in future versions of ECMAScript.
This isn’t required, but it can make the script a little more bulletproof, so I like to do it. The same goes for the leading semicolon on my IIFE; it can prevent errors when concatenating scripts.
Inside my IIFE, I declared my variables:
Here, I simply got a reference to two elements in my HTML. The toggle variable refers to input#show-password, while the password variable refers to input#password.
I then created a function, togglePassword:
This togglePassword function lets me toggle the type attribute of the password field between text and password. I used this function within an event listener attached to the toggle element, so this refers to the toggle element in this context.
If the toggle is checked, the type attribute of the password element will be set to text (visible); otherwise, it will be set to password (concealed).
If you haven’t seen a ternary operator before, it’s just a shorthand if/else statement, best used for simple conditions such as this. In other words, it’s just a shorter way of writing the following:
Finally, I initialized my script.
At the time of writing, Firefox caches checkbox state. That is, if you activate a checkbox and then reload the page, it will stay checked. Therefore, I am simply checking if the toggle element is checked, and if so, setting the type attribute of the password element to text:
Last of all, I set up my event listener to execute the togglePassword function when the toggle element fires the change event:
And that completes the first part of this project. Here’s the complete script:
To start, I adjusted the HTML a little. This time, when activated, the checkbox should toggle the visibility of both password fields simultaneously:
Once again, I created an IIFE to contain my code:
Within my IIFE, I created my variables:
The differences here are:
The ID attribute is now plural (just for accuracy): #show-passwords.
I added a data-password attribute to both password fields. I am using it as my selector within the .querySelectorAll() method to get both fields.
I then created two functions.
First up, the togglePassword function:
The togglePassword function is the same as before, except it now has a single parameter: field. This allows me to pass in a field as my argument, which I will do later using the NodeList.prototype.forEach() method.
Next, the toggleAllPasswords function:
The toggleAllPasswords function is mainly for readability, but it calls the togglePassword function for each of the elements stored inside the passwords variable.
This works because the NodeList.prototype.forEach() method automatically passes the current item in the loop into the togglePassword function.
Finally, I initialized my script:
This is virtually the same as last time, except I’m using the toggleAllPasswords function instead of toggling just a single field.
Here’s the complete script:
That concludes part two!
Multiple fields across multiple forms
Once again, I updated my HTML before making any changes to my script. There are now two forms with their own password fields and toggles:
Surprise… Once again, I started with an IIFE!
I only created one variable this time: toggles. I added a data-toggle attribute to both of the checkboxes in my HTML, so I use it here as my selector string:
Next, I created three functions: togglePassword, toggleAllPasswords, and resetToggle. The first two are slightly modified functions from the previous section; the third is a new one I created for the sake of readability.
First, the togglePassword function:
The togglePassword function is the same as before, except I’m testing the checked property on the event.target instead: that is, the element which fired the change event. I’m using event delegation; this will make more sense when you see the initialization.
Next, the toggleAllPasswords function:
The toggleAllPasswords function is the most different. First, I check to see which element fired the change event. If it was anything other than one of the toggles, I just quit the function and do nothing else.
Next, I get a reference to all password fields inside the current form. The event.target is the toggle that was activated, so I use the Element.prototype.closest() method to traverse the DOM and find that toggle’s containing form element.
I then use the Element.prototype.querySelectorAll() method to find all password fields inside that form (to all of which I have added the data-password attribute). Finally, I toggle each of these passwords by passing my togglePassword function into the NodeList.prototype.forEach() method.
Last but not least, the resetToggle function:
The resetToggle function will allow me to loop through all the toggles and reset them to their default state. This is only necessary because of Firefox’s caching behaviour.
Ulimately, I once again initialize my script:
The main difference is that I added my event listener to the body element because I used event delegation. This allowed me to keep my code DRY (Don’t Repeat Yourself) by adding the event listener only once.