Execute function after a delay or kill it in jquery - javascript

I use a function to load a page with jQuery but only after a certain delay after hovering over a li. For that I use setTimeout on the mouseover and try to kill it on the mouseleave if the mouse hovered for less than 500ms over the li. However, the jQuery.ajax still launches, so basically, if I hover over all lis, that will launch plenty of xhr even if I stay only 1ms on the li.
var timer2;
var delay2 = 500;
$('body').on('mouseover','li',function(){
timer2 = setTimeout(function() {
var url="res.php";
jQuery.ajax(
{
type: 'POST',
url:url,
success: function(data){
$('#res').html(data);
}
});
}, delay2);
});
$('body').on('mouseleave', 'li', function() {
clearTimeout(timer2);
});

The problem here is simple. You use mouseover, mouseover can fire multiple setTimeouts while you are over the element.
$("div")
.on("mouseover", function(){ console.log("mouseover"); })
.on("mouseenter", function(){ console.log("mouseenter"); });
span { background-color: red;
font-size: 2em;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<span>Move mouse here</span> to <span>here</span> to <span>here</span></div>
So each time you move over elements inside of the parent, it fires another mouseover. So if that is the case you will create multiple events. So if you see multiple ajax calls for an li, that may be a reason why.
So change it to mouseenter, next cancel the event inside of enter OR track the events via the li itself and not a global.
$("ul")
.on("mouseenter", "li" function(){
$(this).data("timer", setTimeout( function () {});
}).on("mouseleave", "li" function(){
var id = $(this).data("timer");
if (id) window.clearTimeout(id);
})
And if you really want to be sure, clear the timeout on mouseenter....

Just clear your timeout before setting it again :
var timer2 = null;
$('body').on('mouseover','li',function(){
clearTimeout(timer2);
timer2 = setTimeout(function(){ .....
You need to initialize the timeout to null, otherwise you'll get an error can't clear timeout of undefined.
Also, try this to trigger the mouseleave :
$('body').on('mouseover','li',function(){
// ...
$(this).off("mouseleave").on("mouseleave", () => clearTimeout(timer2))
});
Edit : Working snippet
var timer2 = null;
var delay2 = 2000;
$('body').on('mouseover', 'li', function() {
clearTimeout(timer2);
console.log("Setting timeout...")
timer2 = setTimeout(() => console.log("Ajax call!"), delay2);
$(this).off("mouseleave").on('mouseleave', () => {
console.log("Clearing timeout.")
clearTimeout(timer2);
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<li>Hover over me</li>
<li>Over me too</li>

Give an id to that specific li and use as below. This works. Check console Networks tab for verification.
var timer2;
var delay2 = 500;
var ajax;
$('body').on('mouseover','li#one',function(){
console.log("Mouse over");
clearTimeout(timer2);
timer2 = setTimeout(function() {
if(ajax) {ajax.abort();}
var url="res.php";
ajax = $.ajax(
{
type: 'POST',
url:url,
async:true,
success: function(data){
$('#res').html(data);
}
});
}, delay2);
});
$('body').on('mouseleave', 'li#one', function() {
console.log("Mouse left");
clearTimeout(timer2);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<li id="one">One</li>
<li>Two</li>

There are few ways to solve this problem
abort your request after mouseleave
use variable inside callback to tell it "don't do it"
abort your request
// ...
var request;
$('body').on('mouseover','li',function(){
// ... I omited `setTimeout` to simplify the explanation
// you have to make this variable to be able to control your requests
var xhr = new window.XMLHttpRequest();
// here we save our request
request = jQuery.ajax(
{
// ...
// note this change here, it is required for jquery 3.0 and more
xhr : function(){ return xor; }
});
// ...
});
$('body').on('mouseleave', 'li', function() {
// ...
// now we can `abort` it any time user mouse leaves
request.abort();
});
Question about abort on SO
use variable
// ...
var leaved = false
$('body').on('mouseover','li',function(){
// ... I omited `setTimeout` to simplify the explanation
leaved = false
jQuery.ajax(
{
// ...
success: function(data){
if (leaved) return;
$('#res').html(data);
}
});
// ...
});
$('body').on('mouseleave', 'li', function() {
// ...
leaved = true
});

Related

JavaScript cleartimeout before calling the same function

Situation:
When the user click on a cell of "#test" table it will run "update_func" every 10 seconds.
When the user click on the same cell/ another cell, another "update_func" run again and there are multiple "update_func" running every 10 seconds.
Question:
I would like to stop the previous running "update_func" before a new one start, where should I use the clearTimeout() function to avoid multiple "update_func" keep running?
I have the following script:
$("#test").on("click", td, function() {
//do something..
update_func(v1,v2,v3);
});
function update_func(v1,v2,v3){
$.ajax({
url:"update.php",
method:"POST",
data:{testvalue:v1},
success:function(data){
$('#testbox').html(data);
}
}).always(function () {
window.setTimeout(function() { update_func(v1,v2,v3); }, 10000);
});
}
Save a reference to the timeout ID in the outer scope, assign the setTimeout call to it inside the always, and on click, clear the timeout:
let timeoutId;
$("#test").on("click", td, function() {
//do something..
clearTimeout(timeoutId);
update_func(v1,v2,v3);
});
function update_func(v1,v2,v3){
$.ajax({
url:"update.php",
method:"POST",
data:{testvalue:v1},
success:function(data){
$('#testbox').html(data);
}
}).always(function () {
timeoutId = window.setTimeout(function() { update_func(v1,v2,v3); }, 10000);
});
}

Trigger function only if mouseenter event lasted 1 second

I need to show a tooltip when a user hovers over a specific tag on my page. However, I want to do it only if the tag was hovered for at least a second. I tried the code below, but - obviously - setTimeout() will trigger after a second every time, even if the cursor is "long gone".
Is there an easy way in jQuery to achieve this? Not really interested in any plugin-solution.
Fiddle
HTML
<div class="tag-tooltip" id="tooltip-1">Followers: 34</div>
<div class="tag js-tag" data-id="1">Star Wars (hover over me!)</div>
jQuery
$(document).on('mouseenter', '.js-tag', function() {
var $this = $(this);
setTimeout(function() {
$('#tooltip-' + $this.data('id')).show();
}, 2000);
});
$(document).on('mouseleave', '.js-tag', function() {
$('#tooltip-' + $this.data('id')).hide();
});
UPDATE ON SOLUTION
Many good suggestions below, many ways to achieve same thing. I find clearTimeout() solution the cleanest, though. Thanks to everyone who contributed:)
You were almost there, here you go:
http://jsfiddle.net/j21wjtwh/4/
var hoverTimer;
$(document).on('mouseenter', '.js-tag', function() {
var $this = $(this);
hoverTimer = setTimeout(function() {
$('#tooltip-' + $this.data('id')).show();
}, 1000);
});
$(document).on('mouseleave', '.js-tag', function() {
clearTimeout(hoverTimer);
$('#tooltip-' + $(this).data('id')).hide();
});
Use a flag. Set it to false on mouseleave. In mouseenter check if variable is set.
var show = false; // Define var
// ^^^^^^^^^^^^^
$(document).on('mouseenter', '.js-tag', function () {
show = true; // Set to `true`
var $this = $(this);
setTimeout(function () {
if (show) { // Check if true
$('#tooltip-' + $this.data('id')).show();
}
}, 1000);
});
$(document).on('mouseleave', '.js-tag', function () {
$('#tooltip-' + $(this).data('id')).hide();
show = false; // Unset
});
Demo: http://jsfiddle.net/tusharj/j21wjtwh/2/
Here is a Fiddle
Here is a Code
$(document).on('mouseenter', '.js-tag', function() {
var $this = $(this);
setTimeout(function() {
if($('.js-tag').is(":hover"))
{
$('#tooltip-' + $this.data('id')).show();
}
}, 1000);
});
$(document).on('mouseleave', '.js-tag', function() {
$('#tooltip-' + $(this).data('id')).hide();
});
But there is one small bug here, try to hover/unhover fast, and you will see it
EDIT
As for me THIS answer much better. It doesn't contains my bug
Just keep track of whether or not you are currently hovering with a variable.
Set the hovering variable to true on mouse enter, and false on mouseleave.
Then in your setTimeout event, check if you are currently hovering.
Updated Fiddle
var hovering = false;
$('.js-tag').mouseenter(function () {
var $this = $(this);
hovering = true;
setTimeout(function () {
if (hovering) {
$('#tooltip-' + $this.data('id')).show();
}
}, 1000);
});
$('.js-tag').mouseleave(function () {
hovering = false;
$('#tooltip-' + $(this).data('id')).hide();
});
You can store timer handle in variable and clear it using clearTimeout on mouseleave.
Here is jsfiddle for it.
http://jsfiddle.net/Lz9snp9t/3/
Try this:
$('.js-tag').on('mouseover', function() {
var $this = $(this);
if(!$this.data('timeout')) {
$this.data('timeout', setTimeout(function() {
$('#tooltip-' + $this.data('id')).show();
}, 2000);
}
});
$('.js-tag').on('mouseout', function() {
var $this = $(this);
if($this.data('timeout')) {
clearTimeout($this.data('timeout'));
}
$('#tooltip-' + $this.data('id')).hide();
});

How to clear setTimeout on jQuery mouseover #id

This is my current code to run the series of setTimeout functions. How do I stop these when either the mouse moves, or is over a certain element?
$( document ).ready(function() {
clicky()
function clicky() {
setTimeout(function () {jQuery('#1500').trigger('click');}, 3000);
setTimeout(function () {jQuery('#1990').trigger('click');}, 6000);
setTimeout(function () {jQuery('#2010').trigger('click');}, 9000);
setTimeout(function () {jQuery('#battle').trigger('click');}, 12000);
setTimeout(function () {
jQuery('#water').trigger('click');clicky()
}, 15000);
}
});
You essentially need to save a reference to your timeouts so that they can be cleared when you need them to be. In the following example, I just used an object so that you could specify which timeout you wanted to affect, if desired.
Here's a working fiddle that will clear the timeouts on hover, then reset them when the mouse leaves: http://jsfiddle.net/6tQ4M/2/
And the code:
$(function(){
var timeouts = {};
function setTimeouts () {
timeouts['#1500'] = specifyTimeout('#1500', 3000);
timeouts['#1990'] = specifyTimeout('#1990', 6000);
timeouts['#2010'] = specifyTimeout('#2010', 9000);
timeouts['#battle'] = specifyTimeout('#battle', 12000);
timeouts['#water'] = specifyTimeout('#water', 15000, function(){
console.log('reset the timeouts');
clearTimeouts();
setTimeouts();
});
}
function clearTimeouts () {
for(var key in timeouts){
if(timeouts.hasOwnProperty(key)){
clearTimeout(timeouts[key]);
delete timeouts[key];
}
}
}
function specifyTimeout (id, time, callback) {
return setTimeout(function(){
$(id).trigger('click');
if(callback){
callback();
}
}, time);
}
$('a').on('click', function(){
$('#projects').append('clicky clicky!');
});
$('#map').on('mouseover', clearTimeouts);
$('#map').on('mouseleave', setTimeouts);
setTimeouts();
});
Let me know if you have any questions about the code at all!
Your setTimeout needs to be defined to a variable, so that it can be cleared by passing to clearTimeout(). Something like:
var interval = setTimeout(function() {
//msc
}, 8000);
window.clearTimeout(interval);
Well, according to what you ordered, when you hover an area, the setTimeOut should be fired, and when you are out of this region, the setTimeOut should be reset.
This is the code:
HTML
<div id="map"></div>
CSS
#map{
width:100px;
height:100px;
background-color: black;
}
Javascript
var timeoutHandle;
$('#map').mouseover(function(event){
window.clearTimeout(timeoutHandle);
});
$('#map').mouseout(function(event){
timeoutHandle = window.setTimeout(function(){ alert("Hello alert!"); }, 2000);
});
Basically you should keep a reference to the setTimeOut, in this case the variable is timeoutHandle, call clearTimeOut on mouse over and call setTimeOut again to reset the timer.
Here is the jsFiddle:
http://jsfiddle.net/bernardo_pacheco/RBnpp/4/
The same principle can be used for more than one setTimeOut timer.
You can see more technical details here:
Resetting a setTimeout
Hope it helps.

setTimeout not working properly

I have a script where I disable a button after it was clicked, for like 5 seconds, and then enable it again.
$(document).on('click', 'button', function () {
var htmls = $(this).html();
$(this).prop("disabled", true);
setTimeout(function () {
$(this).prop("disabled", false);
$(this).html(htmls);
}, 5000);
$(this).html('<img src="<?=CDN(' / icons / loading / loading5.gif ')?>" />');
});
Somehow the setTimeout won't end, so the button won't be enabled again. I don't get any error messages.
Save $(this) into variable before setTimeout call, since this keyword inside setTimeout handler refers to window object:
$(document).on("click", "button", function() {
var $this = $(this),
htmls = $this.html();
$this.prop("disabled", true)
.html('<img src="..." />');
setTimeout(function() {
$this.prop("disabled", false)
.html(htmls);
}, 5000);
});
Here does not refer to the DOM element.Try to put into another temarory variable.Because it is outside to setTimeOut
var $this = $(this),
htmls = $this.html();
$this.prop("disabled", true);
setTimeout(function() {
$this.prop("disabled", false).html(htmls);
}, 5000);

Prevent Double Animation in jQuery

How can I stop this function from happening twice when a user clicks too fast?
$(document).ready(function() {
$(".jTscroller a").click(function(event) {
event.preventDefault();
var target = $(this).attr("href");
$("#photo").fadeTo("fast", 0, function() {
$("#photo").attr("src",target);
$("#photo").load(function() {
$("#photo").fadeTo("fast", 1);
});
});
});
});
The issue I'm having is that if a user clicks too fast the element won't fade back in, it just stays hidden.
The issue wasn't what I thought it was. When I was clicking on the same thumbnail it would try to load in the same image and stick loading forever. The .stop() answer does fix double animation so I'm accepting that answer, but my solution was to check if the last clicked item was the currently displayed item. New script:
$(document).ready(function() {
$(".jTscroller a").click(function(event) {
event.preventDefault();
var last = $("#photo").attr("src");
var target = $(this).attr("href");
if (last != target) {
$("#photo").stop().fadeTo("fast", 0, function() {
$("#photo").attr("src",target);
$("#photo").load(function() {
$("#photo").fadeTo("fast", 1);
});
});
};
});
});
Well you use the correct word in your descripton. Use stop()
$("#photo").stop().fadeTo("fast", 0, function() {
You may use a setTimeout function to make a delay between click grabs. I mean, a second click will be processed only after sometime, after the first click. It sets an interval between clicks.
$(document).ready(function() {
var loaded = true;
$(".jTscroller a").click(function(event) {
if(!loaded) return;
loaded = false;
event.preventDefault();
var target = $(this).attr("href");
$("#photo").fadeTo("fast", 0, function() {
$("#photo").attr("src",target);
$("#photo").load(function() {
$("#photo").fadeTo("fast", 1);
loaded = true;
});
});
});
});
Keep track of its state
I believe what you are looking for is .stop()
http://api.jquery.com/stop/
$("#photo").stop(false, false).fadeTo()
I would prevent it like this:
var photo = $("#photo");
if (0 == photo.queue("fx").length) {
foto.fadeTo();
}
I differs from stop as it will only fire when all animations on this element are done. Also storing the element in a variable will save you some time, because the selector has to grab the element only once.
Use on() and off() :
$(document).ready(function() {
$(".jTscroller a").on('click', changeImage);
function changeImage(e) {
e.preventDefault();
$(e.target).off('click');
$("#photo").fadeOut("fast", function() {
this.src = e.target.href;
this.onload = function() {
$(this).fadeIn("fast");
$(e.target).on('click', changeImage);
});
});
}
});

Categories