Show/hide toggling for password inputs. Psswrd is a neat little script I’ve put together to aid in better user experience when users are completing your forms or actioning things inside web apps. For instance, instead of another irritating ‘confirm password’ field, we provide a ‘show password’ for them to double-check before signing up, logging in, filling out various ‘secret questions’, or whatever else you can think of.
Psswrd is a small script (1KB minified) that does exactly that. It’s also really easy to integrate. It might not be an everyday usage script, but it certainly has its place. At the minute, there are no simple to integrate scripts that are written in raw JavaScript, most are jQuery dependent - so I wanted to mix it up and go all out on raw JS.
Table of contents
Configuring Psswrd
Psswrd self-initiates when you tell it to, it doesn’t need calling like a regular plugin, it just watches for the data-init-psswrd attribute and will fire when ready. Configuring only takes a minute and you’re good to go.
You’ll probably have a form on your page to post the info back to the server, so you’ll need to init the plugin on it (though the init is limited to any element):
<form action="/" method="post" data-init-psswrd>
</form>
Then inside that, you’ll want to tell it what input fields to watch:
<input type="password" data-psswrd-toggle>
That’s it.
The structure of your form, however, is advised to be as follows to allow for optimal styling and structure:
<form action="/" method="post" data-init-psswrd>
<label>
Username:
<input type="text">
</label>
<label>
Password:
<input type="password" data-psswrd-toggle>
</label>
<button typ
</form>
The above uses the _
<form action="/" method="post" data-init-psswrd>
<label>
Username:
<input type="text">
</label>
<label>
Password:
<input type="password" data-psswrd-toggle>
<input id="data-psswrd-id-####" class="data-psswrd-checkbox" type="checkbox" data-psswrd-checkbox>
<label for="data-psswrd-id-####" class="data-psswrd-text" data-psswrd-text>Show password</label>
</label>
<button type="submit">Submit</button>
JavaScript
I’ll talk through the main parts of what’s happening in the script, let’s start at the top:
var Psswrd = function ( elem ) {
this.elem = elem;
};
Here I’m creating a constructor function for the plugin, in which I’ll tap into the prototypal inheritance features:
Psswrd.prototype = {
init : function () {
var docFrag = document.createDocumentFragment();
var random = 'data-psswrd-id-' + [ Math.floor( Math.random() * 9999 ) ];
var dataCheckbox = document.createElement( 'input' );
dataCheckbox.id = random;
dataCheckbox.className = 'data-psswrd-checkbox';
dataCheckbox.type = 'checkbox';
dataCheckbox.setAttribute( 'data-psswrd-checkbox', '' );
var dataText = document.createElement( 'label' );
dataText.setAttribute( 'for', random );
dataText.className = 'data-psswrd-text';
dataText.setAttribute( 'data-psswrd-text', '' );
dataText.innerHTML = 'Show password';
docFrag.appendChild( dataCheckbox );
docFrag.appendChild( dataText );
this.elem.parentNode.appendChild( docFrag );
}
};
The above creates all the necessary elements and appends them to the this object, which as we loop through any configured elements, it creates a new instance of the function on each object.
Free eBook
Directives, simple right? Wrong! On the outside they look simple, but even skilled Angular devs haven’t grasped every concept in this eBook.
- Observables and Async Pipe
- Identity Checking and Performance
- Web Components <ng-template> syntax
- <ng-container> and Observable Composition
- Advanced Rendering Patterns
- Setters and Getters for Styles and Class Bindings
Then it’d be wise to loop through the elements needed and create the new instance of the function on each element:
var dataInit = document.querySelectorAll( '[data-init-psswrd]' );
var psswrdNodes = document.querySelectorAll( '[data-psswrd-toggle]' );
if ( dataInit.length > 0 ) {
for ( var i = 0; i < psswrdNodes.length; i++ ) {
new Psswrd( psswrdNodes[i] ).init();
}
}
The if statement above checks if a NodeList is returned for the ‘data-init-psswrd’ selector, and if so, boots the plugin for us. The rest of this section merely loops through the ‘data-psswrd-toggle’ selector and applies our function to it, which does all the heavy lifting for us.
Next the onchange event needs to be listened to:
var changeFunction = function () {
var labelChildNodes = this.parentNode.childNodes;
for ( var z = 0; z < labelChildNodes.length; z++ ) {
var self = labelChildNodes[z];
if ( ( self.nodeName.toLowerCase() === 'input' ) && ( self.hasAttribute( 'data-psswrd-toggle' ) ) ) {
self.type = this.checked ? 'text' : 'password';
} else if ( ( self.nodeName.toLowerCase() === 'label' ) && ( self.hasAttribute( 'data-psswrd-text' ) )) {
self.innerHTML = this.checked ? 'Hide password' : 'Show password';
}
}
};
var dataCheckbox = document.querySelectorAll( '[data-psswrd-checkbox]' );
for ( var j = 0; j < dataCheckbox.length; j++ ) {
dataCheckbox[j].onchange = changeFunction;
}
This onchange event does all the clever toggling using the ternary operator. After a clever selector which grabs the siblings (parentNode.childNodes) and returns them as a NodeList, I can then loop through them and track down the correct elements. Two elements in the NodeList will be the ones I want, I make this manual safety check as whitespace actually returns as a Node inside the NodeList, a potential snag if you never knew it was coming.
We make the necessary checks and then assign the onchange handler onto the checkbox.
Putting it all together in a logical order, we can then see the bigger picture:
window.psswrd = ( function ( window, document, undefined ) {
'use strict';
/*
* Constructor function
*/
var Psswrd = function ( elem ) {
this.elem = elem;
};
/*
* Fetch the data-psswrd-toggle inputs
*/
var dataInit = document.querySelectorAll( '[data-init-psswrd]' );
var psswrdNodes = document.querySelectorAll( '[data-psswrd-toggle]' );
/*
* Prototypal setup
*/
Psswrd.prototype = {
init : function () {
var docFrag = document.createDocumentFragment();
var random = 'data-psswrd-id-' + [ Math.floor( Math.random() * 9999 ) ];
var dataCheckbox = document.createElement( 'input' );
dataCheckbox.id = random;
dataCheckbox.className = 'data-psswrd-checkbox';
dataCheckbox.type = 'checkbox';
dataCheckbox.setAttribute( 'data-psswrd-checkbox', '' );
var dataText = document.createElement( 'label' );
dataText.setAttribute( 'for', random );
dataText.className = 'data-psswrd-text';
dataText.setAttribute( 'data-psswrd-text', '' );
dataText.innerHTML = 'Show password';
docFrag.appendChild( dataCheckbox );
docFrag.appendChild( dataText );
this.elem.parentNode.appendChild( docFrag );
}
};
/*
* Change event that fires
* when an input is checked
*/
var changeFunction = function () {
var labelChildNodes = this.parentNode.childNodes;
for ( var z = 0; z 0 ) {
for ( var i = 0; i < psswrdNodes.length; i++ ) {
new Psswrd( psswrdNodes[i] ).init();
}
}
/*
* Bind onchange events to the checkboxes
*/
var dataCheckbox = document.querySelectorAll( '[data-psswrd-checkbox]' );
for ( var j = 0; j < dataCheckbox.length; j++ ) {
dataCheckbox[j].onchange = changeFunction;
}
})( window, document );
I originally went for a <div> for the text holder, but instead used the <label> element so I could assign a for attribute and id to the paired checkbox and text, so the user’s experience is enhanced again as it allows them to select the text as well to toggle the field as sometimes checkboxes alone can be a challenge to click (and more time consuming).
Multiple Psswrds
Psswrd was built so you can have as many of the fields or even forms on the page, which means the possibilities are endless for whatever you’re setting out to achieve.