So, I have a button and I want to constantly have an event triggering when the button is hovered. If I use mouseover, then this event triggers only once when the cursor comes on it from somewhere outside.
btn.addEventListener("mouseover", function(){
console.log("Hello");
});
For instance, I want this console log to happen constantly while the cursor is over the button.
Please check if mousemove event works for you.
let interval = null;
btn.addEventListener("mousemove", function(){
console.log("Hello");
});
btn.addEventListener('mouseenter', function(){
interval= setInterval(()=>console.log('Hello'), 100)
})
btn.addEventListener('mouseout', function(){
clearInterval(interval)
})
#btn{
width: 300px;
height: 60px;
border-radius: 8px;
}
<button id="btn">
hover me
</button>
You could do this by using mouseover and mouseout (and a setInterval). Basically, start an interval when the mouse enters, and clear it when it exits.
let interval;
btn.addEventListener("mouseover", function(){
interval = setInterval(function() {
console.log("Hello");
}, 100);
});
btn.addEventListener("mouseout", function(){
clearInterval(interval);
});
You can use a combination of
flags, either on the elements themselves as here, or as separate variable(s)
a setInterval function
to figure out which elements are being hovered at any given time. (This is obviously a slightly silly example since you can't very well mouse-over multiple elements that sit next to each other at a time.)
function addEventListeners(button) {
button.addEventListener("mouseover", () => button.dataset.hovered = "1");
button.addEventListener("mouseout", () => button.dataset.hovered = "0");
}
function reportHovers(elements) {
const hoveredIds = elements.filter(el => el.dataset.hovered === "1").map(el => el.id);
document.getElementById("out").value = `${Math.floor(new Date() / 1000)}\n${JSON.stringify(hoveredIds)}`;
}
var buttons = Array.from(document.querySelectorAll("button"));
buttons.forEach(button => addEventListeners(button));
setInterval(() => reportHovers(buttons), 100);
<button id="b1">Button 1</button>
<button id="b2">Button 2</button>
<button id="b3">Button 3</button>
<textarea id="out"></textarea>
Related
I'm stuck in Javascript at looping a piece of code when an event listener is being
placed.
For example, let's say I have a div:
<div id="option">
</div>
and I added a Javascript mouseenter event listener:
const divItem = document.getElementById("option")
divItem.addEventListner("mouseenter", () => {
console.log("Mouse is entered")
})
Now the console log happens once and after I hover the mouse, but I want it to happen every after 4 seconds
and log the same message in the console until the mouse is moving out of the div.
I tried using setTimeout:
divItem.addEventListner("mouseenter", () => {
const timeoutEvent = () => {
console.log("Mouse is entered")
setTimeout( () => { timeoutEvent() }, 4000 )
}
timeoutEvent()
})
but it is logging even after the mouse left the div,
so how can I solve this?
You're on the right track. If you want every four seconds, you want to:
Use setInterval (or set a new setTimeout every time), and
Cancel it when you see mouseleave
const divItem = document.getElementById("option")
// The timer handle so we can cancel it
let timer = 0; // A real timer handle is never 0, so we can use it as a flag
divItem.addEventListener("mouseenter", () => {
console.log("Mouse is entered");
timer = setInterval(() => {
if (timer) {
console.log("Mouse is still here");
}
}, 1000);
})
divItem.addEventListener("mouseleave", () => {
console.log("Mouse left");
clearInterval(timer);
timer = 0;
});
<div id="option">
this is the div
</div>
(I've used one second in that example instead of four so it's easier to see it working.)
Or using setTimeout rescheduling itself instead:
const divItem = document.getElementById("option")
// The timer handle so we can cancel it
let timer = 0; // A real timer handle is never 0, so we can use it as a flag
const timerInterval = 1000;
function tick() {
if (timer) {
console.log("Mouse is still here");
timer = setTimeout(tick, timerInterval);
}
}
divItem.addEventListener("mouseenter", () => {
console.log("Mouse is entered");
timer = setTimeout(tick, timerInterval);
})
divItem.addEventListener("mouseleave", () => {
console.log("Mouse left");
clearTimeout(timer);
timer = 0;
});
<div id="option">
this is the div
</div>
You should use setInterval instead of setTimeout.
You can define your 'interval' function and the interval in global scope and then use them to set and start the interval execution and clearInterval ( stop de execution ) on mouseenter/mouseleave events.
const divItem = document.getElementById("option")
let myInterval;
const timeoutEvent = () => {
console.log("Mouse is entered")
}
divItem.addEventListener("mouseenter", () => {
myInterval = setInterval(timeoutEvent, 1000);
})
divItem.addEventListener("mouseleave", () => clearInterval(myInterval))
<div id="option">
My Option
</div>
divItem.addEventListener("mouseenter", () => {
console.log("Mouse is entered");
timer = setInterval(() => {
console.log("Mouse is still here");
}
}, 4000);
})
I have this script where I highlight the clicked div and its parents, then I make them white again respectively. However, I would like to stop consecutive clicks, allow only one click until setTimeout finishes. Basically, user should wait for the whole animation to finish before clicking again.
const allDivElements = document.querySelectorAll("div");
let timeout = 300;
allDivElements.forEach((div) => {
div.addEventListener("click", function (e) {
setTimeout(() => {
changeBg(this, true);
setTimeout(() => {
changeBg(this, false);
timeout = 300;
}, timeout);
}, timeout);
timeout += 300;
});
});
function changeBg(div, phase) {
if (phase) div.style.backgroundColor = "lightblue";
else div.style.backgroundColor = "#fff";
}
As far as I looked over the possible solutions, I was not able to find one that prevents click events until setTimeout methods. Any detailed help will be appreciated.
EDIT: Sorry if I cause confusion. This is the link to the whole application in case you'd like to test it out: https://codesandbox.io/s/busy-goldstine-ope9w?file=/src/index.js
Thanks in advance!
It looks like you're trying to make this too complicated. Just use a Boolean value for each element to track whether the click should work or not:
const timeout = 300;
allDivElements.forEach((div) => {
let clickAllowed = true;
div.addEventListener("click", function (e) {
if (clickAllowed) {
clickAllowed = false;
changeBg(this, true);
setTimeout(() => {
changeBg(this, false);
clickAllowed = true;
}, timeout);
}
});
});
Handle your listeners using addEventListener and removeEventListener
Once all the elements are processed, add back the events using addEventListener
A recursion could also come handy to iterate over parent Elements:
const divs = document.querySelectorAll("div");
const EVT = (el, t, n, f, o = {}) => el.forEach(e => e[`${{on:"add",off:"remove"}[t]}EventListener`](n, f, o));
const changeBg = (div) => {
if ([...divs].indexOf(div) < 0) return EVT(divs, "on", "click", clickHandler); // Add
div.style.backgroundColor = "lightblue";
setTimeout(() => {
div.style.backgroundColor = "white";
changeBg(div.parentElement); // Recursive call, this time pass the parent
}, 300);
};
const clickHandler = async(ev) => {
EVT(divs, "off", "click", clickHandler); // Remove listeners
changeBg(ev.currentTarget); // Start
};
EVT(divs, "on", "click", clickHandler); // Add listeners
body {
font-family: sans-serif;
}
div {
border: 1px solid black;
padding: 5px;
background: #fff;
}
<div id="1">1
<div id="2">2
<div id="3">3
<div id="4">4
<div id="5">5
</div>
</div>
</div>
</div>
</div>
This question already has answers here:
Incrementing value continuously on mouse hold
(5 answers)
Closed 5 years ago.
I have this script that adds 1 to a value every time I click on a button:
<script>
function incrementValue(id) {
var value = parseInt(document.getElementById(id).innerHTML);
value = value + 1;
document.getElementById(id).innerHTML = value;
}
</script>
<button onclick="incrementValue('skill_1')"> add </button><br>
<span id=skill_1>0</span>
However I want to adjust it so that if I hold down the mouse button, it'll repeat so I don't have to keep pressing it over and over.
Any way to do that using javascript? Or would jquery suit?
To achieve this you need to use the mousedown event to start a timeout (which is the delay before the incremental count starts) and an interval (which does the repeated counting). You'll also need a mouseup and mouseleave handler to remove both of those timers. Try this:
var timeout, interval;
[].forEach.call(document.querySelectorAll('.add'), function(button) {
button.addEventListener('mousedown', function() {
var id = button.dataset.target;
incrementValue(id);
timeout = setTimeout(function() {
interval = setInterval(function() {
incrementValue(id);
}, 50);
}, 300);
});
button.addEventListener('mouseup', clearTimers);
button.addEventListener('mouseleave', clearTimers);
function clearTimers() {
clearTimeout(timeout);
clearInterval(interval);
}
});
function incrementValue(id) {
var el = document.getElementById(id);
var value = parseInt(el.textContent, 10);
document.getElementById(id).textContent = ++value;
}
<button class="add" data-target="skill_1">add</button><br />
<span id="skill_1">0</span>
You'll need 3 event handler:
mousedown that will call a function, that will call itself with a timeout (continuosIncerment) while the mouse button is pressed.
mouseup that will clear the timeout when the button is released.
mouseleave that clears the timeout when the mouse leaves the button area.
const btn = document.querySelector('#btn');
const skill_1 = document.querySelector('#skill_1');
let value = 0;
let timer;
function continuosIncerment() {
skill_1.innerHTML = ++value;
timer = setTimeout(continuosIncerment, 200);
}
function timeoutClear() {
clearTimeout(timer);
}
btn.addEventListener('mousedown', continuosIncerment);
btn.addEventListener('mouseup', timeoutClear);
btn.addEventListener('mouseleave', timeoutClear);
<button id="btn"> add </button><br>
<span id="skill_1">0</span>
Instead of reading the value from the HTML, then writing it back, it's easier to hold the value in a variable, increment it, then write it out.
Did you know you can do this with a simple HTML spinner?
<input type="number" min="0" max="50" step="1">
I'd go with a solution like this: on mouse down event starts a repeating timer that triggers your function and it stops when the mouse up event occurs.
var inter = null;
function setInter(){
inter=setInterval(incrementValue, 500);
}
function unsetInter(){
clearInterval(inter);
}
function incrementValue() {
var value = parseInt(document.getElementById('skill_1').innerHTML);
value = value + 1;
document.getElementById('skill_1').innerHTML = value;
}
<button
onmousedown="setInter()"
onmouseup="unsetInter()"> add </button>
<br>
<span id=skill_1>0</span>
I'm learning event phasing of nested elements so I create small project. Codepen JS starts on 43rd line.
So here's simple nested divs.
<div id="zzz" class="thir">
0
<div id="xxx" class="thir">
0
<div id="sss" class="thir">
0
</div>
</div>
</div>
And here what we do with them.
const ar2 = [zzz, xxx, sss];
ar2.map(e => {
e.addEventListener('click', nestedClick, phase);
})
function nestedClick(e) {
// e.stopPropagation();
const meow = this;
const prevColor = this.style.backgroundColor;
this.style.backgroundColor = '#757575';
window.setTimeout(() => { meow.style.backgroundColor = prevColor}, 500);
}
To visually show how capturing/bubbling works I'd like to change background color and set timeout on each step, wait until it's done and trigger next click with the same strategy.
But here I see after I click on any element event still goes through, changing color and forces all .setTimeout() like at the same time. How can I repair it?
Side question: why e.stopPropagation() works whether it's capturing or bubbling phase?
Thank you for attention!
You need to shift the start time of the timers. And for a flashing effect having a second timer would be good.
let counter = 1;
const ar2 = [...document.getElementsByClassName('thir')];
ar2.map(e => {
e.addEventListener('click', nestedClick);
e.addEventListener('mouseup', function() {
counter = 1;
});
});
function nestedClick(e) {
const prevColor = this.style.backgroundColor;
debugger;
setTimeout( () => {
this.style.backgroundColor = '#757575';
setTimeout( () => {
this.style.backgroundColor = prevColor;
}, 50 * (counter++));
}, 500 * (counter++));
}
<div id="zzz" class="thir">
CLICK ME
<div id="xxx" class="thir">
CLICK ME
<div id="sss" class="thir">
CLICK ME
</div>
</div>
</div>
How can I trigger a function when the browser window stops scrolling? Either by mouse wheel, click, space bar or arrow key? Is there any event for such action? I have tried to search online but couldn't get any solution. I'm fine with a jQuery solution.
There's no "event" but you could make your own, like this:
$(function() {
var timer;
$(window).scroll(function() {
clearTimeout(timer);
timer = setTimeout(function() {
$(window).trigger("scrollStop");
}, 250);
});
});
Then you could bind to it, like this:
$(window).bind("scrollStop", function() {
alert("No one has scrolled me in 250ms, where's the love?");
});
This creates an event, there's no "stop" for this, but you can define your own... in this case "stop" is defined as "hasn't scrolled in 250ms", you can adjust the timer to your liking, but that's the idea.
Also, if you're just doing one thing there's no need for an event, just put your code where I'm calling $(window).trigger("scrollStop") and it'll run n milliseconds after the scroll stops.
The Non-jQuery Javascript version of the chosen answer:
var elem = document.getElementById('test');
(() => {
var onScrollStop = (evt) => {
// you have scroll event as evt (for any additional info)
var scrollStopEvt = new CustomEvent('scrolling-stopped', {detail: 'foobar stopped :)'});
elem.dispatchEvent(scrollStopEvt);
}
var scrollStopLag = 300 // the duration to wait before onScrollStop is triggerred.
var timerID = 0;
const handleScroll = (evt) => {
clearInterval(timerID);
timerID = setTimeout(
() => onScrollStop(evt),
scrollStopLag
)
}
elem.addEventListener('scroll', handleScroll);
})()
elem.addEventListener(
'scrolling-stopped',
(evt) => console.log(evt.detail)
)
#test {
height: 300px;
width: 300px;
overflow: auto;
}
#test #test-inner {
height: 3000px;
background: linear-gradient(lightseagreen 0%, lightyellow 40%, lightcoral 100%);
}
<h4>Scroll inside the green box below:</h4>
<div id="test">
<div id="test-inner"></div>
</div>
Good Luck...
P.S. I am a BIG fan of jQuery :)