Categories JS Projects

Creating a PASSWORD GENERATOR using HTML, CSS, and JavaScript

In this blog, we will create a Password Generator tool using HTML, CSS, and JavaScript. The tool will generate a password according to your specifications and will also check the strength of the generated password. You will also be able to adjust the password length and choose whether you want to include or exclude any letters, numbers, or symbols.

If you’d like to watch the video tutorial, you can do it below by pressing the YouTube play button. If you prefer going through the explanation in the blog, continue reading! The source code of the project is also available for you to download down at the end of the blog.

Laying the Foundation with HTML and CSS

We start with building the basic structure of the password generator with HTML, starting with a heading and a tagline. The password generator will have a password field to show the generated password, password status field to show the strength of the password, regenerate and copy buttons, a slider to adjust the password’s length accompanied by a plus and a minus button, and three checkboxes to include or exclude letters, numbers, and symbols. The entire thing is then wrapped around a container.

The password and pw-status divs are left empty in the HTML document, as those will be occupied by the content pushed using JavaScript.

<div class="container">
  <header>
    <h1>PASSWORD GENERATOR</h1>
    <h3>Generate a strong password!</h3>
  </header>
  <div class="pw-field" id="pw-field">
    <div class="password" id="password"></div>
    <!-- Generated Password appears here  -->
  </div>
  <div class="pw-status" id="pw-status"></div>
  <!-- Password Status i.e. strength appears here  -->
  <div class="btn" id="btn">
    <button class="secondarybtn" id="regenerate" onclick="regeneratePassword()">
      Regenerate
    </button>
    <div class="copied-msg">
      <button class="primarybtn" id="copy" onclick="copyPassword()">
        Copy
      </button>
      <span class="copied-msg-tooltip">Copied!</span>
    </div>
  </div>
  <div class="pw-length">
    <button class="pw-length-btn" id="minus-btn">-</button>
    <input
      type="range"
      name="pw-length"
      id="pw-length"
      min="8"
      max="15"
      value="12"
    />
    <button class="pw-length-btn" id="plus-btn">+</button>
    <span id="pw-length-value">12</span>
  </div>
  <div class="checkbox" id="checkbox">
    <label for="digits">
      <input type="checkbox" name="digits" id="digits" checked />
      Digits (123)
    </label>
    <label for="letters">
      <input type="checkbox" name="letters" id="letters" checked />
      Letters (Abc)
    </label>
    <label for="symbols">
      <input type="checkbox" name="symbols" id="symbols" />
      Symbols (!@?)
    </label>
  </div>
</div>

We start the CSS file with declaring five color variables – primaryColor, vStrongPw, strongPw, weakPw, and grayColor. The primaryColor, as the name suggests, is the primary color of the password generator, and the vStrongPw, strongPw, and weakPw represent the rgb color values of each of the password strengths. The grayColor is a neutral gray Color that we will be using throughout the styling process.

We assign a temporary color to primaryColor, but it will be assigned the color value of the strength of the password via JavaScript, resulting the entire theme of the password generator being inspired by the current strength of the generated password.

:root {
  --primaryColor: 255, 10, 10; /*Random color for now*/
  --vStrongPw: 23, 177, 105;
  --strongPw: 0, 168, 119;
  --weakPw: 255, 25, 75;
  --grayColor: 174, 174, 174;
}

Next up, we style the body to have the background color as the variable primaryColor using the var property. This will result in the body having the background color as the values of primaryColor and hence changing the background according to the password strength after the implementation of JavaScript. Then, we set the margin and padding for the body, declare the required font-family and set the box-sizing to border-box.

In the .container class, we set the background color to dark gray, and style it with some padding, border radius, shadow, and specified width. We center the .container across the body by setting its position to absolute, then using the top, left and transform method to center a div.

body {
  background-color: rgb(var(--primaryColor));
  margin: 10px;
  padding: 10px;
  font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
  box-sizing: border-box;
}

.container {
  background-color: rgb(30, 30, 30);
  padding: 30px;
  border-radius: 15px;
  box-shadow: 8px 10px 10px rgb(var(--primaryColor), 0.5);
  position: absolute;
  width: 500px;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

Then, we style the contents inside the container that make up the password generator. In the code below, we set the display for .pw-field and .pw-status to flex and set justify-content property to center to center their child elements across the .container.

header {
  color: white;
  text-align: center;
}

header h1,
h3 {
  margin: 0 0 5px 0;
}

.pw-field {
  display: flex;
  justify-content: center;
}

.pw-field .password {
  padding: 5px;
  width: 250px;
  border-bottom: 2px solid white;
  color: white;
  font-size: 20px;
  font-weight: 500;
  text-align: center;
}

.pw-status {
  height: 10px;
  display: flex;
  justify-content: center;
  color: rgb(var(--primaryColor));
  font-weight: 600;
  padding: 10px 0 5px 0;
}

After that, we move onto styling the buttons. The .btn div is converted into a flexbox to center its child elements – the Regenerate and Copy buttons. The Regenerate button, being a .secondarybtn, is styled differently from the rest of the buttons, having been given a transparent background. The rest of the buttons are filled with primaryColor, along with a border of 1px solid primaryColor. All buttons have their opacities reduced to 0.85 on hover, and translate 2px on the y-axis when pressed.

.btn {
  display: flex;
  justify-content: center;
  margin: auto;
  padding: 25px;
  gap: 5px;
}

button {
  padding: 5px 10px;
  border-radius: 5px;
  font-size: 15px;
  cursor: pointer;
  transition: all ease 200ms;
}

.primarybtn,
.pw-length-btn {
  background-color: rgb(var(--primaryColor));
  border: 1px solid rgb(var(--primaryColor));
  color: white;
}

.primarybtn,
.secondarybtn {
  width: 100px;
}

.secondarybtn {
  background-color: transparent;
  border: 1px solid rgb(var(--primaryColor));
  color: rgb(var(--primaryColor));
}

button:hover {
  background-color: rgb(var(--primaryColor), 0.85);
  border: 1px solid rgb(var(--primaryColor), 0.85);
  color: white;
}

button:active {
  transform: translateY(2px);
}

While we are at the buttons, we also style the tooltip. Remember we had .primarybtn and the tooltip wrapped around another div .copied-msg? That made the buttons looked slightly off in the layout. Well, setting .copied-msg to inline-block and giving .copied-msg-tooltip an absolute position fixes that. Moreover, we also give the tooltip a distinct look, give it a nice arrow pointer towards the left so it points at the Copy button, and set the visibility to hidden. We will make it visible when the Copy button is clicked later with JavaScript.

.copied-msg {
  display: inline-block;
  gap: 5px;
}

.copied-msg-tooltip {
  position: absolute;
  color: rgb(var(--primaryColor));
  font-weight: 600;
  background-color: white;
  padding: 5px 10px;
  border-radius: 5px;
  width: 120px;
  text-align: center;
  z-index: 1;
  visibility: hidden;
  opacity: 0;
  transition: visibility 0s, opacity 0.5s linear;
}

.copied-msg .copied-msg-tooltip::after {
  content: "";
  position: absolute;
  top: 50%;
  right: 100%;
  margin-top: -5px;
  border-width: 5px;
  border-style: solid;
  border-color: transparent white transparent transparent;
}

We are almost done with styling. We style the slider and checkboxes by removing the default HTML design and give them our own custom look. For that, we set the appearance of both the slider input[type=range] and checkbox input[type=checkbox] to none. We have also added -webkit- and -moz- prefixes to accompany the new properties in CSS3 that might not be available to Chrome and Firefox respectively.

To style the slider thumb, we use the pseudo elements ::-webkit-slider-thumb for Chrome, and ::-moz-range-thumb for Firefox. The rest of the styling are the same, but are done twice to accompany both browsers.

.pw-length {
  display: flex;
  justify-content: center;
  align-items: center;
  margin: 0 0 20px 0;
  gap: 10px;
}

.pw-length span {
  padding: 5px;
  border: none;
  border-radius: 5px;
  background-color: white;
  font-size: 16px;
  font-weight: 500;
  text-align: center;
  width: 25px;
}

.pw-length-btn {
  width: 30px;
}

input[type="range"] {
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  outline: none;
  height: 5px;
  border-radius: 2.5px;
  cursor: grab;
  transition: all ease 100ms;
}

input[type="range"]::-webkit-slider-thumb {
  appearance: none;
  -webkit-appearance: none;
  height: 15px;
  width: 15px;
  background-color: rgb(var(--primaryColor));
  border-radius: 50%;
}

input[type="range"]::-moz-range-thumb {
  appearance: none;
  -moz-appearance: none;
  height: 15px;
  width: 15px;
  background-color: rgb(var(--primaryColor));
  border-radius: 50%;
}

input[type="range"]:hover {
  opacity: 0.85;
}

input[type="range"]:active {
  cursor: grabbing;
}

.checkbox {
  display: flex;
  justify-content: center;
  gap: 10px;
}

.checkbox label {
  color: white;
  display: flex;
  gap: 2px;
  align-items: center;
}

input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  height: 20px;
  width: 20px;
  border-radius: 50%;
  background-color: rgb(var(--grayColor));
  cursor: pointer;
  transition: all ease 200ms;
}

input[type="checkbox"]:hover {
  background-color: white;
  opacity: 0.85;
  border: 5px solid rgb(var(--primaryColor), 0.5);
}

input[type="checkbox"]:checked {
  background-color: white;
  border: 5px solid rgb(var(--primaryColor));
}

input[type="checkbox"]:disabled,
input[type="checkbox"]:disabled:hover {
  background-color: white;
  border: 5px solid rgb(var(--grayColor));
  cursor: not-allowed;
}

Adding Functionality with JavaScript

After we are done styling, we move on to some programming with JavaScript.

We begin with collecting data and storing them in variables.

var password = document.getElementById("password");
var pwStatus = document.getElementById("pw-status");
var regenerate = document.getElementById("regenerate");
var copy = document.getElementById("copy");
var minusBtn = document.getElementById("minus-btn");
var plusBtn = document.getElementById("plus-btn");
var slider = document.getElementById("pw-length");
var sliderValue = document.getElementById("pw-length-value");
var digits = document.getElementById("digits");
var letters = document.getElementById("letters");
var symbols = document.getElementById("symbols");
var checkboxes = document.querySelectorAll('input[type="checkbox"]');
var checkedCount = 0;
var root = document.documentElement;
var vStrongPw = getComputedStyle(root).getPropertyValue("--vStrongPw");
var strongPw = getComputedStyle(root).getPropertyValue("--strongPw");
var weakPw = getComputedStyle(root).getPropertyValue("--weakPw");

Here, we also collect the values of the color variables we declared in CSS and store them in their respective JS variable, along with the root element which is stored in var root.

We begin by adding an event listener to the document, that runs the specified functions on loading the page. Inside the event listener, we introduce a checkNdisable() function. This function checks if the number of checked checkboxes is 1, and if that is the case, disables the checked checkbox so that it cannot be unchecked. This maintains at least 1 checked checkbox in any case, which will prevent the password generator from breaking down.

On loading the page, it first runs a forEach() method to checkboxes, that checks if each of the checkboxes are checked, and updates the checkedCount. We add a event listener to checkboxes, that updates the checkedCount whenever the checkboxes are toggled. Then, at last we call the checkNdisable() function we introduced earlier, along with the generatePassword() function which will be introduced later.

Please note that the checkNdisable() function has been referred to as updateCheckedCount() in the video, which is just a naming error, and the function works the same as we explained here in the blog.

document.addEventListener("DOMContentLoaded", function () {
  function checkNdisable() {
    checkboxes.forEach(function (checkbox) {
      if (checkedCount == 1 && checkbox.checked) {
        checkbox.disabled = true;
      } else {
        checkbox.disabled = false;
      }
    });
  }
  checkboxes.forEach(function (checkbox) {
    if (checkbox.checked) {
      checkedCount++;
    }
    checkbox.addEventListener("change", function () {
      if (this.checked) {
        checkedCount++;
      } else {
        checkedCount--;
      }
      checkNdisable(); //Checks how many checkboxes are checked and in case of only 1, disables the checked checkbox
      generatePassword(); //Generates a new password upon toggling checkboxes
    });
  });
  generatePassword(); //Generates a new password on loading the page
});

Before introducing the generatePassword() function, let’s first go through pwMsg(), that checks the strength of the generated passwords and pushes the output to the pw-status div. For that, we use the if....else statements.

The function reads, if the slider value is 14 or above AND two or more checkboxes are checked, the password is “Very Strong”. Else, if the slider value is between 10 and 14 AND still two or more checkboxes are checked, the password is “Strong”. And if the slider value is less than 10 OR only one checkbox is checked, the password is “Weak”.

We also use the setProperty() method to the root element to change the value of primaryColor to the respective color values of the password strengths in CSS. This step makes the color change to the strength appropriate colors we specified in CSS.

function pwMsg() {
  if (slider.value >= 14 && checkedCount >= 2) {
    pwStatus.textContent = "Very Strong!";
    root.style.setProperty("--primaryColor", vStrongPw);
  } else if (slider.value >= 10 && slider.value < 14 && checkedCount >= 2) {
    pwStatus.textContent = "Strong!";
    root.style.setProperty("--primaryColor", strongPw);
  } else if (slider.value < 10 || checkedCount == 1) {
    pwStatus.textContent = "Weak!";
    root.style.setProperty("--primaryColor", weakPw);
  }
}

Then, finally we introduce the generatePassword() function, which serves as the backbone to the functionality of this project.

function generatePassword() {
  var output = "";
  if (digits.checked) output += "0123456789";
  if (letters.checked)
    output += "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
  if (symbols.checked) output += "!@#$%^&*()-_=+[]{}|;:,./?";
  pw = "";

  for (let i = 0; i < slider.value; i++) {
    const randomIndex = Math.floor(Math.random() * output.length);
    pw += output[randomIndex];
  }

  password.innerHTML = pw; //Displays the password in html
  password.value = pw; //Stores the value for copying to clipboard
  pwMsg();
}

We start by declaring a variable output and assigning it an empty string. Then, if the digits are checked, we add the digits to the value of output. Similarly, if letters are checked, we add the letters to the value of output and do the same for symbols if they are checked. At this point, depending on what checkboxes are checked, the value of output becomes a long string of digits, letters, and/or symbols. Lastly, we declare another variable pw and also assign it an empty string.

Then, we run a for loop that iterates as many times as the slider value. Inside the loop, we declare a constant variable randomIndex, which is a random number between 0 and output‘s length. Then, pw += output[randomIndex] picks the random character from the output string positioned at the index equal to randomIndex, and adds that character to the value of pw.

For example, if the user selected digits and letters, the output string will look like "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz". Then, if the random value between 0 and output‘s length came out to be 14, output[randomIndex] will be the 14th character in output, in this case, the letter “D”. The letter “D” will then be added to the string pw.

This process loops for as many times as the slider value, resulting in a string of random characters of length equal to slider value, which in fact is the desired random password of desired length.

The password i.e. value of the string pw is then displayed in the password div using .innerHTML, and also assigned as its value. The function pwMsg() is also called, so that whenever generatePassword() gets called, pwMsg() can also be ran.

Next, the copyPassword() function takes a variable textToCopy and assigns its value to the value of password. Then, using the navigator.clipboard.writeText(textToCopy) method, the password is written to the device’s clipboard. Along with that, copyPassword() also makes the tooltip visible for 2 seconds whenever the Copy button is clicked.

function copyPassword() {
  const textToCopy = password.value;
  navigator.clipboard.writeText(textToCopy);

  let tooltip = document.querySelector(".copied-msg-tooltip");
  tooltip.style.visibility = "visible";
  tooltip.style.opacity = 1;
  setTimeout(function () {
    tooltip.style.opacity = 0;
  }, 2000);
}

The regeneratePassword() function just re-calls the generatePassword() function to get a newly generated password when the button is triggered. Additionally, it also hides the tooltip in case the button is clicked immediately after copying the previous password.

function regeneratePassword() {
  generatePassword(); //Re-runs generatePassword() on clicking Regenerate button
  document.querySelector(".copied-msg-tooltip").style.visibility = "hidden"; //Hides the tooltip when regenerating new password
}

Now, the only thing remaining is to give functionality to the slider and its plus and minus buttons. For that we use the following event listeners and make the slider value counter update with the corresponding value whenever either of the slider or the buttons are triggered.

minusBtn.addEventListener("click", () => {
  slider.value--;
  generatePassword();

  slider.value < 10
    ? (sliderValue.textContent = "0" + slider.value)
    : (sliderValue.textContent = slider.value);
});

plusBtn.addEventListener("click", () => {
  slider.value++;
  generatePassword();

  slider.value < 10
    ? (sliderValue.textContent = "0" + slider.value)
    : (sliderValue.textContent = slider.value);
});

slider.addEventListener("input", () => {
  slider.value < 10
    ? (sliderValue.textContent = "0" + slider.value)
    : (sliderValue.textContent = slider.value);

  generatePassword();
});

And that’s all! We have successfully created a password generator that can generate random passwords for us to use! You can get the full source code of the project by clicking the download button below. Thank you for reading. See you in the next blog.

Leave a Reply

Your email address will not be published. Required fields are marked *