Reputation: 121
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:
setInterval
. This is not the case if I simply call updatePassword
on its own.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/
Upvotes: 4
Views: 977
Reputation: 9354
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.
Upvotes: 1
Reputation: 547
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.
Upvotes: 7