Loop through array using wheel event a increments - javascript

I want to change the colour of a <main> element using the wheel event. If the event.deltaY is negative i.e scrolling up I want to loop through the array backwards, so, if index = 2 it would go blue, purple, black, white and then to the end, so blue. And if the event.deltaY is positive i.e scrolling down, I want to loop through the array forwards, if index = 3, it would go blue, orange, white, black, etc. It should keep looping infinitely and either way whenever there is a scroll. Currently, I'm having trouble setting the index to loop at either end. Any help would be greatly appreciated.
const container = document.querySelector('main')
const classes = ['white', 'black', 'purple', 'blue', 'orange']
let index = 0
const throttle = (func, limit) => {
let inThrottle
return function() {
const args = arguments
const context = this
if (!inThrottle) {
func.apply(context, args)
inThrottle = true
setTimeout(() => inThrottle = false, limit)
}
}
}
function changeColour(event) {
if (event.deltaY < 0) {
++index
if (index === index.length - 1) {
index = 0
}
console.log(index);
container.classList = classes[index]
}
if (event.deltaY > 0) {
--index
console.log(index);
if (index === 0) {
index = index.length - 1
}
container.classList = classes[index]
}
}
container.addEventListener('wheel', throttle(changeColour, 1250))
main {
height: 50px;
width: 50px;
border: 1px solid;
}
main.white {
background-color: #FFFFFF;
}
main.black {
background-color: #101010;
}
main.purple {
background-color: #8E002E;
}
main.blue {
background-color: #002091;
}
main.orange {
background-color: #C05026;
}
<main>
</main>

Issues
index = index.length - 1 index = classes.length -1
container.classList = classes[index] container.classList.add(classes[index])
There is no statement to remove any of the other classes.
Event listeners and on-event properties do not use the parentheses of a function because it will be misinterpreted as a direct call to the function to run. Callback functions are the opposite in that they wait to be called on. If an extra parameter is needed by the callback function you'll need to wrap the callback function with an anonymous function, here's the proper syntax:
on-event property: document.querySelector('main').onwheel = scrollClass;
event listener: document.querySelector('main').addEventListener('wheel', scrollClass)
on-event property passing extra parameters:
document.querySelector('main').onwheel = function(event) {
const colors = ['gold', 'black', 'tomato', 'cyan', 'fuchsia'];
scrollClass(event, colors);
}
event listener passing extra parameters:
document.querySelector('main').addEventListener('wheel', function(event) {
const colors = ['gold', 'black', 'tomato', 'cyan', 'fuchsia'];
scrollClass(event, colors);
}
Details commented in demo
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<title>Wheel Event Demo</title>
<style>
:root {
font: 400 16px/1.2 Consolas;
}
html,
body {
width: 100%;
height: 100%;
}
body {
overflow-y: scroll;
overflow-x: hidden;
padding: 5px 0 20px;
}
main {
height: 50vh;
width: 50vw;
margin: 10px auto;
border: 1px solid #000;
}
main.gold {
background-color: #FFCC33;
transition: 0.5s;
}
main.black {
background-color: #101010;
transition: 0.5s;
}
main.tomato {
background-color: #FF6347;
transition: 0.5s;
}
main.cyan {
background-color: #E0FFFF;
transition: 0.5s;
}
main.fuchsia {
background-color: #FD3F92;
transition: 0.5s;
}
</style>
</head>
<body>
<main></main>
<script>
// Reference the main tag
const container = document.querySelector('main');
// Declare index
let index = 0;
/** scrollClass(event, array)
//# Params: event [Event Object]: Default object needed for event properties
// array [Array].......: An array of strings representing classNames
//A Pass Event Object and array of classes
//B Reference the tag bound to the wheel event (ie 'main')
//C1 if the change of wheel deltaY (vertical) is less than 0 -- increment index
//C2 else if the deltaY is greater than 0 - decrement index
//C3 else it will just be index (no change)
//D1 if index is greater than the last index of array -- reset index to 0
//D2 else if index is less than zero -- reset index yo the last index of array
//D3 else index is unchanged.
//E Remove all classes of the currentTarget ('main')
//F Find the string located at the calculated index of the array and add it as a
className to currentTarget
*/
function scrollClass(event, array) { //A
const node = event.currentTarget; //B
index = event.deltaY < 0 ? ++index : event.deltaY > 0 ? --index : index; //C1-3
index = index > array.length - 1 ? 0 : index < 0 ? array.length - 1 : index; //D1-3
node.className = ''; //E
node.classList.add(array[index]); //F
}
/*
Register wheel event to main
When an event handler (ie scrollClass()) has more than the Event Object to pass
as a parameter, you need to wrap the event handler in an anonymous function.
Also, it's less error prone if the parameter is declared within the
anonymous function.
*/
container.addEventListener('wheel', function(event) {
const colors = ['gold', 'black', 'tomato', 'cyan', 'fuchsia'];
scrollClass(event, colors);
});
</script>
</body>
</html>

Modified code to include the corrections
const container = document.querySelector('main')
const classes = ['white', 'black', 'purple', 'blue', 'orange']
let index = 0
const throttle = (func, limit) => {
let inThrottle
return function() {
const args = arguments
const context = this
if (!inThrottle) {
func.apply(context, args)
inThrottle = true
setTimeout(() => inThrottle = false, limit)
}
}
}
function changeColour(event) {
if (event.deltaY < 0) {
++index
if (index >= classes.length) { // 👈 Here
index = 0
}
console.log(index);
container.classList = classes[index]
}
if (event.deltaY > 0) {
--index
console.log(index);
if (index <= -1) { // 👈 Here
index = classes.length - 1 // 👈 Here
}
container.classList = classes[index]
}
}
container.addEventListener('wheel', throttle(changeColour, 1250))
main {
height: 50px;
width: 50px;
border: 1px solid;
}
main.white {
background-color: #FFFFFF;
}
main.black {
background-color: #101010;
}
main.purple {
background-color: #8E002E;
}
main.blue {
background-color: #002091;
}
main.orange {
background-color: #C05026;
}
<main>
</main>

Related

What is causing div.style.right = '50%'; to stop being applied when my variable becomes greater than 0?

I have a variable count that triggers a function positiveBar if the value of count is > 0. If the value of count is < 0, it triggers a function negativeBar.
positiveBar changes a div's position using
progressBar.style.left = '50%';
negativeBar changes that same div's position using
progressBar.style.right = '50%';
This gives me the result I want; however, if at any point count becomes greater than 0, the positioning on negativeBar stops working, and it uses the positioning of the positiveBar function instead.
Video to explain:
var count = 0;
// Show count on the page
document.getElementById("countDisplay").innerHTML = count;
// Update count
function updateDisplay() {
countDisplay.innerHTML = count;
};
// Change negative count to an absolute
function absCount() {
return Math.abs(count);
};
function positiveBar() {
progressBar.style.backgroundColor = "#77eb90";
progressBar.style.width = (count * 10) + 'px';
progressBar.style.left = '50%';
};
function negativeBar() {
progressBar.style.backgroundColor = "#ef5c3f";
progressBar.style.width = (absCount() * 10) + 'px';
progressBar.style.right = '50%';
};
// Count up and down when + and - buttons are clicked and edit bar
add1.addEventListener("click", () => {
count++;
updateDisplay();
if (count > 0) {
positiveBar();
} else {
negativeBar();
}
});
subtract1.addEventListener("click", () => {
count--;
updateDisplay();
if (count > 0) {
positiveBar();
} else {
negativeBar();
}
});
.progressBar__Container {
height: 10px;
margin: 20px auto;
border: 1px solid black;
position: relative;
}
#progressBar {
height: 10px;
width: 0;
position: absolute;
}
<div id="countDisplay"></div>
<button id="add1">+</button>
<button id="subtract1">-</button>
<div class="progressBar__Container">
<div id="progressBar"> </div>
</div>
I tried reordering statements. I also tried creating a condition for if count = 0, but that didn't change the result. I'm very confused because it initially works how I intend, but if count becomes greater than 0 at any point, progressBar.style.right = '50%'; stops being applied.
You aren't clearing any previously set left or right styles when you switch from negative to positive and vice versa.
I would use CSS classes to control the position and colour as it's easier to toggle them based on the state of count.
let count = 0;
const countDisplay = document.getElementById("countDisplay");
const progressBar = document.getElementById("progressBar");
// Update count
function updateDisplay() {
countDisplay.textContent = count;
progressBar.style.width = `${absCount() * 10}px`;
progressBar.classList.toggle("positive", count > 0);
progressBar.classList.toggle("negative", count < 0);
};
// Change negative count to an absolute
function absCount() {
return Math.abs(count);
};
// Count up and down when + and - buttons are clicked and edit bar
add1.addEventListener("click", () => {
count++;
updateDisplay();
});
subtract1.addEventListener("click", () => {
count--;
updateDisplay();
});
.progressBar__Container {
height: 10px;
margin: 20px auto;
border: 1px solid black;
position: relative;
}
#progressBar {
height: 10px;
width: 0;
position: absolute;
}
#progressBar.positive {
background-color: #77eb90;
left: 50%;
}
#progressBar.negative {
background-color: #ef5c3f;
right: 50%;
}
<div id="countDisplay">0</div>
<button id="add1">+</button>
<button id="subtract1">-</button>
<div class="progressBar__Container">
<div id="progressBar"> </div>
</div>
See MDN:
When both left and right are defined, if not prevented from doing so by other properties, the element will stretch to satisfy both. If the element cannot stretch to satisfy both — for example, if a width is declared — the position of the element is over-constrained. When this is the case, the left value has precedence when the container is left-to-right; the right value has precedence when the container is right-to-left.
Because you are setting style.left when you then come to set style.right the above applies - i.e. the style.right setting will get overridden.

Simulate 'backspace' in Jquery

I have a simple animation code, looks like a console input.
Originally from: https://codepen.io/atunnecliffe/pen/BaZyLR
I modified the splash screen intro into just a console input in my website:
Code:
<script>
//console
var textarea = $('.term');
var text = 'ping life';
var i = 0;
runner();
function runner() {
textarea.append(text.charAt(i));
i++;
setTimeout(
function () {
runner();
}, Math.floor(Math.random() * 1000) + 50);
}
</script>
Now the effect that I want is a bit complex, for me at least, as my knowledge about JQuery is limited. I wanted the code to enter ping life, then backspace completely, repeat infinitely. I looked up on how to simulate backspace in JQuery, using escape sequence of (8), but I am not sure how to use the escape sequence, nor implement the function into the existing recursive function, for it to repeat infinitely.
Any help would be wonderful :)
Like this?
Counting like this will give a zigzag like counting pattern. I added buffers for start and end of input, and a fixed timeout for deleting letters.
textarea.text(text.substr(0, i)) selects a substring of your text (treated as an array of letters - selecting everything between index 0 and i)
Easier than appending and deleting letters
var direction = 1;
var i = 0;
var textarea = $('.term');
var text = 'ping life';
// NOTE:
// I added the "#dev:~$ " as css:before elem, easier to write the code
function count() {
i += direction;
direction *= (((i % text.length) == 0) ? -1 : 1);
textarea.text(text.substr(0, i));
clearInterval(time);
// direction is 1 if counting up
if (direction === 1) {
if (i === 0) {
// buffer for start
time = setInterval(count, 1000);
} else {
time = setInterval(count, Math.floor(Math.random() * 1000) + 50);
}
} else {
// direction is -1 if counting down
if (i === text.length) {
time = setInterval(count, 1500);
} else {
// buffer for end
time = setInterval(count, 100);
}
}
}
// inital interval
// setTimeout doesn't work well here
var time = setInterval(count, 1000)
html,
body {
margin: 0 auto;
height: 100%;
}
pre {
padding: 0;
margin: 0;
}
pre::before {
content: "#dev:~$ ";
color: white;
}
.load {
margin: 0 auto;
min-height: 100%;
width: 100%;
background: black;
}
.term {
font-family: monospace;
color: #fff;
opacity: 0.8;
font-size: 2em;
overflow-y: auto;
overflow-x: hidden;
padding-top: 10px;
padding-left: 20px;
}
.term:after {
content: "_";
opacity: 1;
animation: cursor 1s infinite;
}
#keyframes cursor {
0% {
opacity: 0;
}
40% {
opacity: 0;
}
50% {
opacity: 1;
}
90% {
opacity: 1;
}
100% {
opacity: 0;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="load">
<pre class="term"></pre>
</div>

Javascript while with time delay

My goal I want to run loop that decrements a global variable stepwise in n ms (for Example: 200ms) time intervals.
Thanks in advance!
What i already tried
I tried to use ascy await. But in combination with css transition i run in an infinite loop (In codepen.io). But here in SO you will see that it starts not running smoothly if you keep pressing arrow up.
const procentage = document.querySelector(".procentage");
const green = engine.querySelector(".green");
let number = 0;
let decrementing = false;
window.addEventListener('keydown', (e) => {
e = e || window.event;
e.preventDefault();
if (e.keyCode == '38') {
console.log("accelerate");
actionHandler( number++ );
decrementing = false;
downLoop();
}
});
function actionHandler(num) {
procentage.innerHTML = num;
const str = num + "%"
green.style.width = str;
procentage.innerHTML = str;
}
window.addEventListener('keyup', (e) => {
e = e || window.event;
e.preventDefault();
if (e.keyCode == '38') {
console.log("decelerate");
decrementing = true;
downLoop();
}
});
async function downLoop() {
if (! decrementing) {
return false
};
const timer = ms => new Promise(res => setTimeout(res, ms));
while (number > 1) {
// how to decrement ever 200ms???
actionHandler( number-- );
await timer(200)
}
}
#engine {
background-color:black;
height: 50px;
position: relative;
}
p {
text-align: center;
}
.green {
background:green;
height: 50px;
width:0%;
transition: width 0.2s;
text-align:center;
}
.procentage {
position:absolute;
top: 50%;
left: 50%;
transform: translate(0%,-50%);
color: white;
fon-weight: bold;
font-size:28px;
}
<div id="engine">
<div><span class="procentage">0</span></div>
<div class="green"></div>
</div>
<p>press arrow Up</p>
Whenever you animate, you shouldn't rely on setInterval or setTimeout, because that means that you will update "somewhere after X milliseconds" which will often end up in the middle of a screen repaint, and will therefor cause janky animation.
Instead, you should use RequestAnimationFrame which does a calculation before every repaint. So if you got a monitor with a framerate of 60 Hz, that means that you will do 60 repaints every second. For each repaint, check if enough time have passed since the last update (shouldTriggerUpdate() below) and then check if you should update the number.
I also added the class KeyHandler to keep track of which keys that have been pressed.
I got sloppy at the end and just added a decrement as an "else" of the if statement. You will figure something out when you get there when you want to set up more keys to be pressed.
You shouldn't use KeyboardEvent.keyCode, but instead KeyboardEvent.code.
const procentage = document.querySelector(".procentage");
const green = engine.querySelector(".green");
let number = 0;
let speed = 200 // ms
let lastUpdated = 0; // ms
let animationId = 0; // use later on to pause the animation
class KeyHandler {
ArrowLeft = false
ArrowUp = false
ArrowRight = false
ArrowDown = false
#setKey(code, value) { // private method
if (typeof this[code] != undefined) {
this[code] = value;
}
}
set pressedKey(code) {
this.#setKey(code, true);
}
set releasedKey(code) {
this.#setKey(code, false);
}
}
let keyHandler = new KeyHandler();
window.addEventListener('keydown', (e) => {
e = e || window.event;
e.preventDefault();
keyHandler.pressedKey = e.code;
});
window.addEventListener('keyup', (e) => {
e.preventDefault();
keyHandler.releasedKey = e.code
});
function actionHandler(num) {
const str = num + "%"
green.style.width = str;
procentage.innerHTML = str;
}
function shouldTriggerUpdate(timeInMillis) {
let difference = timeInMillis - lastUpdated;
return difference >= speed;
}
function planeAnimation() {
let timeInMillis = new Date().getTime();
if (shouldTriggerUpdate(timeInMillis)) {
lastUpdated = timeInMillis;
if (keyHandler.ArrowUp) {
actionHandler(++number)
} else if (number > 0) {
actionHandler(--number)
}
}
animationId = requestAnimationFrame(planeAnimation)
}
animationId = requestAnimationFrame(planeAnimation);
#engine {
background-color: black;
height: 50px;
position: relative;
}
p {
text-align: center;
}
.green {
background: green;
height: 50px;
width: 0%;
transition: width 0.2s;
text-align: center;
}
.procentage {
position: absolute;
top: 50%;
left: 50%;
transform: translate(0%, -50%);
color: white;
fon-weight: bold;
font-size: 28px;
}
<div id="engine">
<div><span class="procentage">0</span></div>
<div class="green"></div>
</div>
<p>press arrow up</p>
From the above comments ...
"Instead of incrementing each time the number value push a new async timer function, set to 200 msec delay but not immediately triggered, into an array. Create an async generator from it and iterate over the latter via the for-await...of statement where one could decrement number again." – Peter Seliger
"#PeterSeliger Hi Peter! Thank you for your comment. Can you make a small example please?" – Maik Lowrey
And here the requested demonstration.
function createWait(delay) {
return async function wait () {
let settle;
const promise = new Promise((resolve) => { settle = resolve;});
setTimeout(settle, delay, { delay, state: 'ok' });
return promise;
};
}
async function* getWaitIterables(list) {
let wait;
while (wait = list.shift()) {
yield wait();
}
}
// demo for ...
// - creating an async `wait` function
// or a list of such kind.
// - creating an async generator from
// a list of async `wait` functions.
// - iterating an async generator of
// async `wait` functions.
const waitingList = [ // const waitingList = [];
2000, // waitingList.push(createWait(2000));
1000, // waitingList.push(createWait(1000));
3000, // waitingList.push(createWait(3000));
].map(createWait); // - The OP of cause needs to push into.
let number = 3; // - The incremented `number` value e.g. ... 3.
(async () => {
for await (const { delay, state } of getWaitIterables(waitingList)) {
--number;
console.log({ number, delay, state });
}
})();
console.log('... running ...', { number });
.as-console-wrapper { min-height: 100%!important; top: 0; }

I want to Add class 'your-active-class' to section when near top of viewport in JavaScript, but my code is not working

I have more than one section thats why i used for loop,
what can i do to add class to my section element?
const sections = document.querySelectorAll('section');
function sectionInViewPort(element) {
return (element.getBoundingClientRect().distance.top >= 0);
}
function AddActiveClass() {
for (s = 0; s <= sections.length; s++) {
//if section is in view port add class "your-active class"
if (sectionInViewPort(sections[s])) {
sections[s].classList.add("your-active-class");
//else remove it
}else {
sections[s].classList.remove("your-active-class");
}
}
}
document.addEventListener('scroll', AddActiveClass);
Few small problems but you're on the right track.
Firstly, try using getBoundingClientRect().top - which should give you a px distance from the top of the window to the item.
Second, you need to be calling your functions for them to work. You need to be calling AddActiveClass whenever the window is scrolled so it does it's checks whenever sections move up or down the page.
Third, you're grabbing all sections on the page but only using the first - i'm assuming this is just test code and you want to be checking all sections (in which case you'd use sections[s] in your code - the sections array using the looping s index.
finally, check your logic - your top >= 0 check is going to add the class to every section below the top of the page - you might want to do something like top > 0 && top < 100 to only hit sections near the top of the page, or add a check so that only one section ever gets the class added.
A rough idea of how your code should look, may need a couple tweaks:
// on window scroll
window.addEventListener('scroll', (e) => {
// get all sections on the page
const sections = document.querySelectorAll('section');
// loop through each section
sections.forEach( section => {
// get px distance from top
const topDistance = section.getBoundingClientRect().top;
// if the distance to the top is between 0-100px
if (topDistance > 0 && topDistance < 100) {
section.classList.add('your-active-class');
// otherwise, remove the class
} else {
section.classList.remove('your-active-class');
}
});
});
You had couple of mistakes in your code. Check the snippet, it should do the magic. And if you have any further questions, just ping me and I try to elaborate my code:
const sections = document.querySelectorAll('section');
const isInViewport = (section) => {
const { top } = section.getBoundingClientRect();
section.classList.toggle('active', top >= 0);
}
const toggleActiveClass = () => {
sections.forEach(isInViewport);
}
document.addEventListener('scroll', toggleActiveClass);
section {
background-color: red;
height: 200px;
}
section.active {
background-color: green;
}
<section>#1</section>
<section>#2</section>
<section>#3</section>
<section>#4</section>
<section>#5</section>
<section>#6</section>
<section>#7</section>
This is my approach to your problem.
Of course you have remove the CSS I used for demonstration purposes, and also you have to remove the code I use inside the AddActiveClass I used for better understanding of what I did. The code to remove is the following:
var span = section.querySelector('span');
span.innerText = distanceFromTop + ' - Greater Than Or Equal to O: ' + (distanceFromTop >= 0);
function AddActiveClass() {
const sections = document.querySelectorAll('section');
sections.forEach(
function(section) {
var distanceFromTop = section.getBoundingClientRect().top;
var span = section.querySelector('span');
span.innerText = distanceFromTop + ' - Greater Than Or Equal to O: ' + (distanceFromTop >= 0);
if( distanceFromTop >= 0) {
section.classList.add("your-active-class");
} else {
section.classList.remove("your-active-class");
}
}
);
}
document.addEventListener('scroll', AddActiveClass);
AddActiveClass();
section {
width: 100vw;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
section:nth-child(1) {background :#0AF;}
section:nth-child(2) {background :#A0F;}
section:nth-child(3) {background :#AF0;}
section:nth-child(4) {background :#FA0;}
section:nth-child(5) {background :#F0A;}
section:nth-child(6) {background :#0FA;}
section span {
font-size: 32px;
font-weight: bold;
color: #FFF;
text-shadow: 0px 0px 3px #000000,-2px -2px 3px #FF0000,2px 2px 3px #00FF00,-2px 2px 3px #0000FF;
}
section.your-active-class {
background: rgb(2,0,36);
background: linear-gradient(41deg, rgba(2,0,36,1) 0%, rgba(9,9,121,1) 35%, rgba(0,212,255,1) 100%);
}
<section><span>0</span></section>
<section><span>0</span></section>
<section><span>0</span></section>
<section><span>0</span></section>
<section><span>0</span></section>
<section><span>0</span></section>

Looping through array of colors infinite amount of times

I have a button with a default background color(black). I want to change the background color (from an array) of this button when hovering over it. I have it working at a fundamental level, but I want it to repeat the loop over and over.
this is what i have so far.
var color = ['#3e50a2', '#faa51a', '#ed1c24', '#2a9446'];
var i = -1;
document.querySelector('.customBtn').addEventListener('mouseover', function() {
i = 1 < color.length ? ++i : 0;
document.querySelector('.customBtn').style.background = color[i]
});
document.querySelector('.customBtn').addEventListener('mouseout', function() {
document.querySelector('.customBtn').style.background = '#000';
})
<a class="customBtn">Button</a>
I think you tried to reset the value of i with this i = 1 < color.length ? ++i : 0; but it doesn't. This will continuously increase the value never resetting it because 1 is always less than the length of the array.
I think you meant to increment the value and then reset if it's too big:
i = ++i < color.length ? i : 0;
Here's the complete code. I've refactored the query selector as there's no point to doing it more than once, and changed the mouseout background colour so you can read the button
var color = ['#3e50a2', '#faa51a', '#ed1c24', '#2a9446'];
var i = -1,
btn = document.querySelector('.customBtn');
btn.addEventListener('mouseover', function() {
i = ++i < color.length ? i : 0;
btn.style.background = color[i];
});
btn.addEventListener('mouseout', function() {
// revert to default colour
btn.style.background = '';
})
<input type="button" class="customBtn" value="My button" />
Just replace i = 1 < color.length ? ++i : 0; with i = (i+1 < color.length) ? ++i : 0;. That's all.
var color = ['#3e50a2', '#faa51a', '#ed1c24', '#2a9446'];
var i = -1;
document.querySelector('.customBtn').addEventListener('mouseover', function() {
i = (i+1 < color.length) ? ++i : 0;
document.querySelector('.customBtn').style.background = color[i]
});
document.querySelector('.customBtn').addEventListener('mouseout', function() {
document.querySelector('.customBtn').style.background = '#000';
})
<a class="customBtn">Button</a>
If you must use javascript, take a look at the second example.
Pure CSS solution
You could accomplish this with a CSS animation, which would be both more efficient and less error prone. Unless there's a specific reason you need to use javascript here I'd strongly recommend this approach.
This example could be modified to do hard transitions instead of fading from one color to the next, but here's a quick demo:
button {
background: black;
color: white;
border: none;
padding: 0.5em 1em;
}
button:hover {
animation: buttonhover 1s infinite;
}
#keyframes buttonhover {
0% {
background: #3e50a2;
}
25% {
background: #faa51a;
}
50% {
background: #ed1c24;
}
75% {
background: #2a9446;
}
}
<button>Hello</button>
Javascript Solution
If you must use javascript for whatever reason, you can use the % operator to keep from running off the end of the colors array:
const colors = ['#3e50a2', '#faa51a', '#ed1c24', '#2a9446'];
let index = 0;
let interval;
const hover = (e) => {
interval = setInterval(() => {
e.target.style.backgroundColor = colors[index];
index = (index + 1) % colors.length;
}, 300);
}
document.querySelector('button').addEventListener('mouseover', hover);
document.querySelector('button').addEventListener('mouseout', (e) => {
clearInterval(interval);
e.target.style.backgroundColor = 'black';
});
button {
background: black;
color: white;
border: none;
padding: 0.5em 1em;
}
<button>Hello</button>

Categories

Resources