4

I have built a kind of password generator that should display a new password whenever the countdown expires. Unfortunately, I have only managed to figure out how to run my code once. The countdown consists of a simple CSS transition, which I would like to keep, because it is much smoother than my other attempts, wherein i tried to repeatedly update the width using JavaScript.

var dictionary = {
	"adverbs": [
  	"always",
  	"usually",
    "probably"
  ],
  "adjectives": [
  	"useful",
  	"popular",
   	"accurate"
  ],
  "nouns": [
  	"computer",
    "software",
    "terminal"
  ]
};

function capitalizeFirst(string) {
	return string.charAt(0).toUpperCase() + string.slice(1);
}

function randomIndex(object) {
	return object[Math.floor(Math.random() * object.length)];
}

function generatePassword() {
	var category = ["adverbs", "adjectives", "nouns"];
	var password = [];
  
  for (i = 0; i < category.length; i++) {
  	password.push(capitalizeFirst(randomIndex(dictionary[category[i]])));
  }
  
  password.push(Math.floor(Math.random() * 8999) + 1000);
  return password.join("");
}

function updatePassword() {
	document.getElementById("countdown-fill").style.width = 100 + '%';
	document.getElementById("text-field").value = generatePassword();
	document.getElementById("countdown-fill").style.width = 0 + '%';
}

setInterval(updatePassword, 5000);
@import url('https://fonts.googleapis.com/css?family=Nunito&display=swap');

body {
  margin: 0;
  width: 100%;
  height: 100%;
  background-color: #f8f8f8;
}

.container {
  max-width: 400px;
  margin: 0 auto;
}

#text-field {
  font-size: 15px;
  font-weight: 400;
  font-family: 'Nunito', sans-serif;
  margin-top: 100px;
  margin-bottom: 10px;
  width: 100%;
  height: 30px;
  padding: 10px;
  box-sizing: border-box;
  border: 1px solid  #e5e5e5;
  background-color: #ffffff;
}

#countdown-background {
  width: 100%;
  height: 10px;
  box-sizing: border-box;
  border: 1px solid  #e5e5e5;
  background-color: #ffffff;
}
#countdown-fill {
  width: 100%;
  height: 100%;
  transition: width 5s;
  transition-timing-function: linear;
  background-color: #1e87f0;
}
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>Password Generator</title>
  </head>
  <body>
    <div class="container">
      <input id="text-field" type="text" spellcheck="false">
      <div id="countdown-background">
        <div id="countdown-fill"></div>
      </div>
    </div>
  </body>
</html>

Currently, I have two apparent issues with my code:

  1. The transition becomes delayed due to setInterval. This is not the case if I simply call updatePassword on its own.
  2. The CSS transition only animates once. I would like to reset the animation every time i call updatePassword.

I came across a few jQuery solutions for my problem, but I am not very interested in those, as I want to rely on standard JavaScript as much as possible. However, I am okay with alternative CSS tools like keyframes, which seem to work well:

#countdown-fill {
  width: 100%;
  height: 100%;
  animation: refresh 5s infinite;
  background-color: #1e87f0;
}

@keyframes refresh {
    from {
        width: 100%;
    }
    to {
        width: 0;
    }
}

Although, I do worry about synchronization issues as the animation is not coupled with updatePassword in any way.

Question: Is there a way to have updatePassword reset the animation each time I call the function, and remove the initial delay?

JSFiddle: https://jsfiddle.net/MajesticPixel/fxkng013/

2 Answers 2

7

I've modified your JSFiddle, here's the explanation.

#countdown-fill {
  width: 100%;
  height: 100%;
  transform-origin: left;
  background-color: #1e87f0;
}
.reset {
  transition: transform 5s linear;
  transform: scaleX(0);
}

The trick is to bind the transition to a class, and when you want to reset it you just remove the class (reset the transition to the initial status) and add it again (restart it).

But there are a few gotchas: the most important is that instantly removing and adding the class will be optimized by the browser, which will just merge the actions and no transition at all will happen. The trick is to wrap the calls in a nested rAF call, which will force the browser to execute, render, and then execute again.

window.requestAnimationFrame(function() {
  document.getElementById("countdown-fill").classList.remove('reset');   
  window.requestAnimationFrame(function() {
    document.getElementById("countdown-fill").classList.add('reset');
  });
});

The second is related to transitions: to optimize browser rendering, avoid transitioning properties like width or height, and try to limit to transforms and opacity. I've changed your width transition into a transform transition: same effect, more performance.

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you NevNein! This is exactly what I was after :)
This is the best answer. Worked like a charm!
1

I second what NevNein has posted, and would also like to add that if you want to couple the transition with updatePassword so that they have a linked relationship and not just matched timeouts, you should replace setInterval(updatePassword, 5000) with:

updatePassword();
document.getElementById('countdown-fill').addEventListener("transitionend", updatePassword)

The countdown and password change will now run at any speed you set in the CSS.

2 Comments

Hey, Lawrence. I tried to follow your advice but I could not make it work. Do you mean that I should specify transition-duration: 5s; within the styling for #countdown-fill? Let me know where I messed up, because your solution sounds interesting. Thank you.
I should have added, you will also need to call updatePassword() by itself to start the chain. I have tested this myself on the JSFiddle and it works fine. Keep the transition duration however it was in Nev's working version.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.