Vanilla Javascript, mouseout delay with cancelation without jQuery - javascript

i am currently having a problem. I am using css to hide and display elements depending on the mouse function. One of my elements (a navigation arrow) depends on some other things. I now need a cancleable timer function which counts for lets say 2 seconds on mouseleave and then changes the class attribute. But it should have a timer which cancels on mouseover immediately. I dont want it to disappear too early.
Below my code with which i tried so far. I have no idea how to access the current timings of that setIntervall stuff. I alreasy experimented with Date.now(). But now i hope some of the geeks is able to help me.
Thanks in advance.
function hideElementOnMouseOut(el)
{
el.addEventListener("mouseleave", function( event )
{
mySlideAction = setInterval( function()
{
}, 1000 );
}
}

You can initialize interval on mouseleave function and clear this interval on mouseover function, which would prevent executing it's function.
Check the snippet below.
function hideElementOnMouseOut(el)
{
var interval;
el.addEventListener("mouseleave", function(event)
{
el.innerHTML = 'mouse out';
interval = setInterval(function()
{
el.innerHTML = 'time out';
el.className = 'out';
}, 1000);
});
el.addEventListener("mouseover", function(event)
{
el.innerHTML = 'mouse in';
el.className = '';
if(interval) {
clearInterval(interval);
}
});
}
hideElementOnMouseOut(document.getElementById("element"));
#element {
display: block;
width: 200px;
height: 200px;
background: red;
}
#element.out {
background: blue;
}
<div id="element"></div>

Related

Is there a way to fire a function only if an event is sustained for a while

I'm trying to call a function only if the user hovered over the target for a certain amount of seconds.
So it doesn't fire instantly and also doesn't just fire after a time frame but only fires if the user hovered for the entire duration of the timeframe.
If this could be done in JavaScript it would be fantastic. Thanks in advance
Just set a timeout when the mouse hovers the div, and clear it when the mouse leaves the div. In this way the callback will be fired only and only if the mouse remained within the div for n seconds
const el = document.querySelector('.hover-me');
let timeoutHandler = null;
el.addEventListener('mouseenter', () => {
timeoutHandler = setTimeout(() => alert('Hovered for 2 seconds'), 2000);
});
el.addEventListener('mouseleave', () => {
if (timeoutHandler) clearTimeout(timeoutHandler);
timeoutHandler = null;
});
div.hover-me {
width: 200px;
height: 200px;
background-color: lightblue;
}
<div class="hover-me">Hover me</div>

Reduce mousemove events

I've this code:
$("body").mousemove(function (event) {
var lowestspanelm = findNearestSpan(gg_phone_spans, event);
});
During mouse move this is generating too many events and increasing CPU load which is taking around 1 minute or so to become normal.
To reduce this load what I want is to act only when mouse has stopped for at least 1/2 second.
What is the way to do it?
Though your question points in a direction that you need a debounce function, my advise is that you try a throttle function instead.
A debounced function will only fire when there are no more incoming events
A throttled function will fire only every so often
$("body").mousemove(throttle(function (event) {
var lowestspanelm = findNearestSpan(gg_phone_spans,event);
}, 500));
This will fire only every .5 seconds as long as there are incoming events
You won't need jQuery for this.
document.addEventListener('mousemove', throttle(function (event) {
var lowestspanelm = findNearestSpan(gg_phone_spans,event);
}, 500));
Here is a demonstration of the differences.
If you hover over the NORMAL element the background color will change with every pixel moved.
If you move inside the THROTTLE element, the color will change only every half of a second as long as you are moving.
If you move inside the DEBOUNCE element, the color will change only .5 seconds after you stopped moving (or moved out of the element).
Here are debounce and throttle next to each other
function debounce(callback, wait) {
var timeout;
return function(e) {
clearTimeout(timeout);
timeout = setTimeout(() => {
callback(e);
}, wait);
}
}
function throttle(callback, wait) {
var timeout
return function(e) {
if (timeout) return;
timeout = setTimeout(() => (callback(e), timeout=undefined), wait)
}
}
function onMove(e) {
e.target.classList.toggle('gold')
}
document.querySelector('.normal').addEventListener('mousemove', onMove)
document.querySelector('.throttle').addEventListener('mousemove', throttle(onMove, 500))
document.querySelector('.debounce').addEventListener('mousemove', debounce(onMove, 500))
.normal,
.throttle,
.debounce {
width: 100px;
height: 100px;
margin: 25px;
background: silver;
border: 1px solid gold;
}
.normal.gold,
.throttle.gold,
.debounce.gold {
background: gold;
}
body {
display: flex;
}
<div class="normal">NORMAL</div>
<div class="throttle">THROTTLE</div>
<div class="debounce">DEBOUNCE</div>
One possible way is to use timeouts:
var mouseTimeout;
$("body").mousemove(function (event) {
clearTimeout(mouseTimeout);
mouseTimeout = setTimeout(function() {
var lowestspanelm = findNearestSpan(gg_phone_spans,event);
}, 500);
});
You can adjust the 500 to have it trigger more or less often.
As commented this is generally called a debounce function, and is available in many general purpose libraries. It is also fairly easy to write (a simple version) yourself.
$(document).on("mousemove",debounce(function(event) {
console.log("This will only happen after you stop moving the mouse for 1/2 second")
},500));
function debounce(fn, time){
let timer = null;
return (evt) => {
clearTimeout(timer);
timer = setTimeout( () => fn(evt), time);
};
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Disable scroll while waiting to for scroll selection animation to complete

I'm trying to make customize section-scroll where scroll event is disabled while animation is in motion.
I tried with overflow hidden and other similar functions to apply while scroll animation is in progress but no luck.
JS fiddle example
CSS
.stop-scrolling {
height: 100%;
overflow: hidden;
}
JS example:
$('html').on('wheel', function(event) {
if (event.originalEvent.deltaY > 0) {
//scroll down
counter++;
$('body').addClass('stop-scrolling');
console.log("Start animacije");
setTimeout(function () {
$('body.stop-scrolling').removeClass('stop-scrolling');
console.log("End animation");
}, 800);
if (!(counter > maxSections)) {
$('html, body').animate({
scrollTop: $( $("#sect-"+counter) ).offset().top
}, 800);
}
} else {
//scroll up
counter--;
$('body').addClass('stop-scrolling');
console.log("Start animation");
setTimeout(function () {
$('body.stop-scrolling').removeClass('stop-scrolling');
console.log("End animation");
}, 800);
if (!(counter < 1)) {
$('html, body').animate({
scrollTop: $( $("#sect-"+counter) ).offset().top
}, 800);
}
}
if (counter <= 0) {
counter = 1;
}
else if (counter >= 3) {
counter = maxSections;
}
console.log(counter);
});
If you scroll while animation is in progress, scroll will continue until you reach end of sections.
Is possible to disable scroll event while animation is in progress?
Here's simple fix with less code modification.
Just declare a variable that represents scrolling state. And using this state you can ignore incoming scroll events if currently scrolling.
let scrolling = false;
$('html').on('wheel', function(event) {
if (scrolling) {
return;
} else {
scrolling = true;
setTimeout(function () {
scrolling = false;
}, 800 /* animation duration */);
}
...
Here's final fiddle link.
Using setTimeout function you can reset scrolling state to false so, new events will be received.
Instead of using CSS, you can use your script to block scroll events.
You can use the onComplete parameter of the .animate method to run a callback after animation ends.
With this, you can use a flag variable to determine whether or not the page is scrolling.
The whole process would be something like:
Animation started.
Flag animating = true.
Block scrolling.
Animation ended.
Flag animating = false.
Unblock scrolling.
Here's a minimal and reproducible example. It may have some bugs, but it should solve your main problem.
$(document).ready(function(){
'use strict';
// Flag to decide whether or not scroll is allowed.
let animating = false;
$(window).on('scroll', (e) => {
if (animating){
// Block scroll
e.preventDefault();
}
});
$('.section').on('wheel', e => {
e.stopPropagation();
// Do not run this function again when animation is happening.
if (animating){
return;
}
const $target = $(e.target);
const isScrollDown = e.originalEvent.deltaY > 0;
// Choose whether to scroll to next or previous section.
const $targetSection = isScrollDown ? $target.next('.section') : $target.prev('.section');
// Check whether or not there is a target
const hasTargetSection = $targetSection.length > 0;
// Only animate when there is a target.
if (hasTargetSection){
// Flag animation start
animating = true;
$('html, body').animate(
// Animation properties
{
scrollTop: $targetSection.offset().top
},
// Animation duration
800,
// Function to run after animation ends
() => {
// Flag animation ended
animating = false;
}
);
}
});
});
html, body, #app {
width: 100%;
height: 100%;
padding: 0;
margin: 0;
}
.section {
width: 100%;
height: 100%;
font-size: 3em;
display: flex;
justify-content: center;
align-items: center;
}
.a {
background-color: #f008;
}
.b {
background-color: #0f08;
}
.c {
background-color: #00f8;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="app">
<div class="section a">Section A</div>
<div class="section b">Section B</div>
<div class="section c">Section C</div>
</div>
What you are asking for is called throttling. I recommend you read this link to understand it better.
So to fix your code, you only need to enable throttling for your wheel event listener.
Like this:
let throttle = (fn, timedelay) => {
let pass = true;
return function () {
if (pass) {
setTimeout(() => {
fn.apply(this, arguments);
pass = true;
}, timedelay);
pass = false;
}
};
};
let scrollFunction = throttle(function(event) {
// your code
}, 200); // 200 miliseconds sounds good
$('html').on('wheel', scrollFunction);
Here is a working code: https://jsfiddle.net/d1fcL5en/

event is executed twice instead of once

Click on the 5 and it will become 7-9-11...
I'm expecting 6-7-8...
Any help?
function go_plus(e) {
let obj = $(e.target);
let a = parseInt(obj.text());
a += 1;
obj.text(a);
}
var setint = '';
function go_spinn(e) {
setint = setInterval(function() {
go_plus(e);
}, 79);
}
$('#count').on('mouseleave mouseup', function() {
clearInterval(setint);
});
#count {
cursor: pointer;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id='count' onclick='go_plus(event)' onmousedown='go_spinn(event)'>5</div>
So the problem is you can "slow-click" things - hold the mousedown for a second or two, then let it go, and still result in a click.
The best way to solve the problem is to put a timeout before go_spin starts that you can clear when you click.
The drawback is your go_spinn doesn't start up as fast - i.e. you need to hold the mouse down for the duration of your timeout, in my example it was 200ms, before your go_spinn starts. Test it, you might be able to drop it back a little bit (to 150ms or so) to achieve what you want.
EDIT: By the way, I was just making an assumption on what you were trying to achieve - what were you actually trying to achieve with this code?
function go_plus(e){
let obj = $(e.target);
let a = parseInt(obj.text());
a += 1;
obj.text(a);
}
var setint = '';
var startspin;
function go_spinn(e){
startspin = setTimeout(function() {
setint = setInterval(function(){go_plus(e);}, 79);
},200);
}
$('#count').on('click mouseleave mouseup', function(){
clearInterval(setint);
clearInterval(startspin);
});
#count{
cursor:pointer;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id='count' onclick='go_plus(event)' onmousedown='go_spinn(event)'>5</div>
Just change your html code to <div id='count' onmousedown='go_spinn(event)'>5</div> so that you remove the onclick event.

On / Off function after a certain period of time

Wondering how you can make .off() only happen for a specific period of time. I have a div which once clicked i want to disable the click event for 2 seconds, and then be allowed to click again. At the moment all I have is the div can be clicked, then once clicked it is off.
Here is a brief example of what I am asking:
$('.test').on('click', function() {
// *do stuff*
$('.test').off('click'); *for a certain perdiod of time*
});
It's a much simpler task to use a boolean variable as a flag to state whether the click handler should be executed, instead of attaching/detaching events from multiple elements. Try this:
var clickEnabled = true;
$('div').click(function() {
clickEnabled = false;
setTimeout(function() {
clickEnabled = true;
}, 2000);
});
$('.test').on('click', function(e) {
if (!clickEnabled) {
e.preventDefault();
} else {
// do stuff...
}
});
Note that you should also make the fact that .test is disabled visible in the UI, otherwise you'll just confuse and annoy your visitors when they click an element expecting an action, but nothing happens.
What about activate on after a certain period of time?
function myFunction() {
...do stuff
$('.test').off('click'); for a certain perdiod of time
setTimeout(function(){ $('.test').on('click', myFunction)}, 2000);
}
Using the setTimeout you may do something like:
var enabled = true;
var timeoutSeconds = 2;
$(function () {
$('.test').on('click', function(e) {
if (enabled) {
// *do stuff*
enabled = false;
window.setTimeout(function() {
enabled = true;
}, timeoutSeconds * 1000);
} else {
e.preventDefault();
}
});
});
You could do it like this.
function onClick() {
// do stuff here
$('.test').off('click');
setTimeout(function(){
$('.test').on('click', onClick);
}, 2000)
}
$('.test').on('click', onClick);
Or you can do it with css and toggleClass
// css
.disabled {
pointer-events: none;
}
// js
$('.test').on('click', function(){
// do stuff here
$('.test').addClass('disabled');
setTimeout(function(){
$('.test').removeClass('disabled');
}, 2000);
});

Categories

Resources