header.header
h1.header__title Password Strength Validator UI
.header__btns.btns
a.header__btn.btn(href="https://github.com/nat-davydova/password-strength" title="Check on Github" target="_blank") Check on Github
.content
.content__inner
.container
.row
.col-lg-6.m-auto
form.password-strength.form.p-4
h3.form__title.text-center.mb-4 Enter the Password
.form-group
label(for="password-input") Password
.input-group
input.password-strength__input.form-control(type="password" id="password-input" aria-describedby="passwordHelp" placeholder="Enter password")
.input-group-append
button.password-strength__visibility.btn.btn-outline-secondary(type="button")
span.password-strength__visibility-icon(data-visible='hidden')
i.fas.fa-eye-slash
span.password-strength__visibility-icon(data-visible='visible').js-hidden
i.fas.fa-eye
small.password-strength__error.text-danger.js-hidden This symbol is not allowed!
small#passwordHelp.form-text.text-muted.mt-2 Add 9 charachters or more, lowercase letters, uppercase letters, numbers and symbols to make the password really strong!
.password-strength__bar-block.progress.mb-4
.password-strength__bar.progress-bar.bg-danger(role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100")
button.password-strength__submit.btn.btn-success.d-flex.m-auto(type="button" disabled) Submit
View Compiled
@mixin transition-mix ($property: all, $duration: 0.2s, $timing: linear, $delay: 0s) {
transition-property: $property;
transition-duration: $duration;
transition-timing-function: $timing;
transition-delay: $delay;
}
@mixin position-absolute ($top: null, $left: null, $right: null, $bottom: null) {
position: absolute;
top: $top;
left: $left;
right: $right;
bottom: $bottom;
}
$theme-font-color: #2c2c2c;
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font: {
family: 'Muli', sans-serif;
size: 16px;
}
color: $theme-font-color;
a {
color: inherit;
text-decoration: none;
}
}
.header__btn {
@include transition-mix;
padding: 10px 20px;
display: inline-block;
margin-right: 10px;
background-color: #fff;
border: 1px solid $theme-font-color;
border-radius: 3px;
cursor: pointer;
outline: none;
&:last-child {
margin-right: 0;
}
&:hover,
&.js-active{
color: #fff;
background-color: $theme-font-color;
}
}
.header {
max-width: 600px;
margin: 50px auto;
text-align: center;
}
.header__title {
margin-bottom: 30px;
font: {
size: 2.1rem;
}
}
.content {
width: 95%;
margin: 0 auto 50px;
}
.password-strength {
box-shadow: 0 4px 5px 0 rgba(0,0,0,0.14),
0 1px 10px 0 rgba(0,0,0,0.12),
0 2px 4px -1px rgba(0,0,0,0.3);
}
.js-hidden {
display: none;
}
View Compiled
DOM = {
passwForm: '.password-strength',
passwErrorMsg: '.password-strength__error',
passwInput: document.querySelector('.password-strength__input'),
passwVisibilityBtn: '.password-strength__visibility',
passwVisibility_icon: '.password-strength__visibility-icon',
strengthBar: document.querySelector('.password-strength__bar'),
submitBtn: document.querySelector('.password-strength__submit'),
}
const findParentNode = (elem, parentClass) => {
parentClass = parentClass.slice(1, parentClass.length);
while (true) {
if(!elem.classList.contains(parentClass)) {
elem = elem.parentNode;
} else {
return elem;
}
}
};
const getPasswordVal = input => {
return input.value;
};
const testPasswRegexp = (passw, regexp) => {
return regexp.test(passw);
};
const testPassw = passw => {
let strength = 'none';
const moderate = /(?=.*[A-Z])(?=.*[a-z]).{5,}|(?=.*[\d])(?=.*[a-z]).{5,}|(?=.*[\d])(?=.*[A-Z])(?=.*[a-z]).{5,}/g;
const strong = /(?=.*[A-Z])(?=.*[a-z])(?=.*[\d]).{7,}|(?=.*[\!@#$%^&*()\\[\]{}\-_+=~`|:;"'<>,./?])(?=.*[a-z])(?=.*[\d]).{7,}/g;
const extraStrong = /(?=.*[A-Z])(?=.*[a-z])(?=.*[\d])(?=.*[\!@#$%^&*()\\[\]{}\-_+=~`|:;"'<>,./?]).{9,}/g;
if(testPasswRegexp(passw, extraStrong)) {
strength = 'extra';
} else if(testPasswRegexp(passw, strong)) {
strength = 'strong';
} else if(testPasswRegexp(passw, moderate)) {
strength = 'moderate';
} else if(passw.length > 0) {
strength = 'weak';
}
return strength;
};
const testPasswError = passw => {
const errorSymbols = /\s/g;
return testPasswRegexp(passw, errorSymbols);
};
const setStrengthBarValue = (bar, strength) => {
let strengthValue;
switch(strength) {
case 'weak':
strengthValue = 25;
bar.setAttribute('aria-valuenow', strengthValue);
break;
case 'moderate':
strengthValue = 50;
bar.setAttribute('aria-valuenow', strengthValue);
break;
case 'strong':
strengthValue = 75;
bar.setAttribute('aria-valuenow', strengthValue);
break;
case 'extra':
strengthValue = 100;
bar.setAttribute('aria-valuenow', strengthValue);
break;
default:
strengthValue = 0;
bar.setAttribute('aria-valuenow', 0);
}
return strengthValue;
};
const setStrengthBarStyles = (bar, strengthValue) => {
bar.style.width = `${strengthValue}%`;
bar.classList.remove('bg-success', 'bg-info', 'bg-warning');
switch(strengthValue) {
case 25:
bar.classList.add('bg-danger');
bar.textContent = 'Weak';
break;
case 50:
bar.classList.remove('bg-danger');
bar.classList.add('bg-warning');
bar.textContent = 'Moderate';
break;
case 75:
bar.classList.remove('bg-danger');
bar.classList.add('bg-info');
bar.textContent = 'Strong';
break;
case 100:
bar.classList.remove('bg-danger');
bar.classList.add('bg-success');
bar.textContent = 'Extra Strong';
break;
default:
bar.classList.add('bg-danger');
bar.textContent = '';
bar.style.width = `0`;
}
};
const setStrengthBar = (bar, strength) => {
const strengthValue = setStrengthBarValue(bar, strength);
setStrengthBarStyles(bar, strengthValue);
};
const unblockSubmitBtn = (btn, strength) => {
if(strength === 'none' || strength === 'weak') {
btn.disabled = true;
} else {
btn.disabled = false;
}
};
const findErrorMsg = input => {
const passwForm = findParentNode(input, DOM.passwForm);
return passwForm.querySelector(DOM.passwErrorMsg);
};
const showErrorMsg = input => {
const errorMsg = findErrorMsg(input);
errorMsg.classList.remove('js-hidden');
};
const hideErrorMsg = input => {
const errorMsg = findErrorMsg(input);
errorMsg.classList.add('js-hidden');
};
const passwordStrength = (input, strengthBar, btn) => {
const passw = getPasswordVal(input);
const error = testPasswError(passw);
if(error) {
showErrorMsg(input);
} else {
hideErrorMsg(input);
const strength = testPassw(passw);
setStrengthBar(strengthBar, strength);
unblockSubmitBtn(btn,strength);
}
};
const passwordVisible = passwField => {
const passwType = passwField.getAttribute('type');
let visibilityStatus;
if (passwType === 'text') {
passwField.setAttribute('type', 'password');
visibilityStatus = 'hidden';
} else {
passwField.setAttribute('type', 'text');
visibilityStatus = 'visible';
}
return visibilityStatus;
};
const changeVisibiltyBtnIcon = (btn, status) => {
const hiddenPasswIcon = btn.querySelector(`${DOM.passwVisibility_icon}[data-visible="hidden"]`);
const visibilePasswIcon = btn.querySelector(`${DOM.passwVisibility_icon}[data-visible="visible"]`);
if (status === 'visible') {
visibilePasswIcon.classList.remove('js-hidden');
hiddenPasswIcon.classList.add('js-hidden');
} else if(status === 'hidden') {
visibilePasswIcon.classList.add('js-hidden');
hiddenPasswIcon.classList.remove('js-hidden');
}
};
const passwVisibilitySwitcher = (passwField, visibilityToggler) => {
const visibilityStatus = passwordVisible(passwField);
changeVisibiltyBtnIcon(visibilityToggler, visibilityStatus);
}
DOM.passwInput.addEventListener('input', () => {
passwordStrength(DOM.passwInput, DOM.strengthBar, DOM.submitBtn);
});
const passwVisibilityBtn = document.querySelector(DOM.passwVisibilityBtn);
passwVisibilityBtn.addEventListener('click', e => {
let toggler = findParentNode(e.target, DOM.passwVisibilityBtn);
passwVisibilitySwitcher(DOM.passwInput, toggler);
});
View Compiled