Adding and removing classes produces race condition when used within setTimeout - javascript

Have 2 objects on one, need to make both of them getting classes:
<script>
$(document).ready(function(){
var delayMillis = 200;
$('#overtop').hover(function(){ $('#honemove').addClass('hover');}, function () { setTimeout(function() { $('#honemove').removeClass('hover'); }, delayMillis); });
$('#overtop').hover(function(){ setTimeout(function() { $('#htwomove').addClass('hover'); }, delayMillis); }, function () { $('#htwomove').removeClass('hover'); });
});
</script>
<div id="overtop" class="overlay"></div>
<h1 id="honemove" class="h1a"><span>TITLE OF THE TITLE</span></h1>
<h2 id="htwomove"><span>INFO BELLOW TITLE</span></h2>
It works currently, but it glitches if u do a lot of mouse hovers, the CSS part is fine, I guess it doesn't like having two separate scripts on one action.

Use clearTimeout to prevent any sort of race condition from occurring:
$(document).ready(function() {
var $overtop = $('#overtop')
var $honemove = $('#honemove')
var $htwomove = $('#htwomove')
var delay = 200
var token
$overtop.hover(function() {
clearTimeout(token)
$honemove.addClass('hover')
token = setTimeout(function() {
$htwomove.addClass('hover')
}, delay)
}, function() {
clearTimeout(token)
$htwomove.removeClass('hover')
token = setTimeout(function() {
$honemove.removeClass('hover')
}, delay)
})
});
.hover {
color: #f00;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="overtop" class="overlay">Overlay</div>
<h1 id="honemove" class="h1a"><span>TITLE OF THE TITLE</span></h1>
<h2 id="htwomove"><span>INFO BELLOW TITLE</span></h2>

Related

jQuery/JS stop loop setTimeout on click

I have a map with multiple cities on it. I also have a loop showing the details of each city. I would like to completely stop the loop once I click on one of the cities.
I tried with: clearTimeout(loopIdx) but it didn't work. Could you please help me?! Cheers.
LOOP:
$(function() {
var $mapCol = $('.map-col');
var $mapBtn = $('.map-btn');
var $mapLoops = $('.map-loop');
var $btnLoops = $('.btn-loop');
loopIdx = (function _loop(idx) {
$mapCol.removeClass('active-map');
$mapBtn.removeClass('active-btn');
$mapLoops.removeClass('active-map').eq(idx).addClass('active-map');
$btnLoops.removeClass('active-btn').eq(idx).addClass('active-btn');
setTimeout(function() {
_loop((idx + 1) % $mapLoops.length);
}, 6000);
}(0));
});
BTN:
<div class="btn-loop">City</div>
Assign the result of setInterval() to a variable, and use that in the clearTimeout() call.
$(function() {
var $mapCol = $('.map-col');
var $mapBtn = $('.map-btn');
var $mapLoops = $('.map-loop');
var $btnLoops = $('.btn-loop');
var timer;
loopIdx = (function _loop(idx) {
$mapCol.removeClass('active-map');
$mapBtn.removeClass('active-btn');
$mapLoops.removeClass('active-map').eq(idx).addClass('active-map');
$btnLoops.removeClass('active-btn').eq(idx).addClass('active-btn');
timer = setTimeout(function() {
_loop((idx + 1) % $mapLoops.length);
}, 2000);
}(0));
$btnLoops.click(function() {
clearTimeout(timer);
});
});
.map-loop.active-map {
background-color: yellow;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="btn-loop">City</div>
<div class="map-loop">1</div>
<div class="map-loop">2</div>
<div class="map-loop">3</div>
<div class="map-loop">4</div>
<div class="map-loop">5</div>
<div class="map-loop">6</div>
<div class="map-loop">7</div>
You need to access the timeoutID by name or identifier to clear it. (See https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/clearTimeout.)
If you want loopIdx to be the identifier, you can use something like:
let needToClear = false;
let loopIdx = setTimeout(function(){}, 6000);
// ...state changes here...
if(needToClear){ clearTimeout(loopIdx); }

jQuery setIntervel until condition is true

After the page is loaded i like to check a condition after every 1 second until it becomes true, and then terminate that function. I tried the following script that simple putting heavy load on the page start.
$(document).ready(function () {
setInterval(function () {
for (var i = 0; i < 1; ) {
if ($(".showPrice").length) {
console.log("yes");
i = 1;
} else {
return false;
}
}
}, 1000);
});
to be able to clear the interval you need a reference to the interval and pass it to clearInterval(), just assign it to a variable:
$(document).ready(function () {
let interval = setInterval(function () {
if ($(".showPrice").length) {
clearInterval(interval);
console.log('Cleared!');
}
}, 1000);
setTimeout(function(){
$('.placeholder').html('<div class="showPrice">11.00 $</div>');
}, 3000)
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<body>
<div class="placeholder"></div>
</body>

Removing class after X seconds?

I'm trying this below code:
$(document).ready(function() {
$("button").hover(
function() {
$(this).addClass("active");
},
function() {
$(this).removeClass("active");
}
);
});
Try this: http://plnkr.co/edit/Xlco44QPWvKEh1jb0gDf?p=preview
var button = $('button');
button.hover(
function() {
button.addClass('active');
setTimeout(function() {
button.removeClass('active');
}, 3000);
},
function() {
button.removeClass('active');
}
);
From what you said in your previous comment below, you tried setTimeout and it didn't work because of the the way you used this. The value of this inside the timeout function wasn't the same as in your outer function, so jQuery didn't match your button element.
Better to define the button once as a variable, and reuse the variable, that use repeated jQuery selectors.
UPDATE: Here's a slightly more sophisticated version that keeps the setTimeout timers from piling up:
$(function() {
var button = $('button');
var timeout = 0;
button.hover(
function() {
button.addClass('active');
timeout = setTimeout(function() {
button.removeClass('active');
timeout = 0;
}, 2000);
},
function() {
button.removeClass('active');
if (timeout) {
clearTimeout(timeout);
timeout = 0;
}
}
);
});
I have created a jQuery extention just for that! This is extremely common in web development:
jQuery.addTempClass.js
$.fn.addTempClass = function(tempClass, duration){
if( !tempClass )
return this;
return this.each(function(){
var $elm = $(this),
timer;
$elm.addClass(tempClass);
// clear any timeout, if was any
clearTimeout( $elm.data('timeout') )
timer = setTimeout(function(){
$elm.removeClass(tempClass).removeData('timeout');
}, duration || 100);
// set the timer on the element
$elm.data('timeout', timer);
});
};
Usage example:
$(document.body).addTempClass('some_class', 2000)
You can include this script as a dependency file for your build system or simply copy-paste this piece of code somewhere in your code, just after jQuery has loaded, so it will be available everywhere afterwards.

How do I implement a delay on jQuery keyup?

I'm making a list filter and want a delay in case the user is a fast typer. Looking at different solutions for similar questions hasn't helped me and I don't understand the logic they implement.
This is my current code:
$.fn.filterList = function(){
var inputFilter = $(this);
var list = $('#' + inputFilter.data('list'));
var listItems = list.children('li');
inputFilter.keyup(function(){
setTimeout(function () {
var term = inputFilter.val().toLowerCase();
listItems.each(function(i, e){
var city = ($(e).text()).toLowerCase();
if(city.startsWith(term)){
console.log(city);
}
});
}, 800);
});
};
$('.my-input').filterList();
The problem with this is that it will trigger on each keyup, no matter how fast the user types.
How can I implement a delay so that it does not trigger for each keyup?
On each successive keypress you need to clear the previous timer so that the function only fires X milliseconds after typing ends. Try this:
var timer;
inputFilter.keyup(function() {
clearTimeout(timer);
timer = setTimeout(function() {
var term = inputFilter.val().toLowerCase();
listItems.each(function(i, e) {
var city = $(e).text().toLowerCase();
if (city.startsWith(term)) {
console.log(city);
}
});
}, 800);
});
Here's a simplified working example:
var timer;
$('#foo').keypress(function() {
clearTimeout(timer);
timer = setTimeout(function() {
$('div').fadeIn('fast').delay(1000).fadeOut('fast');
}, 800);
});
div { display: none; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input id="foo" />
<div>You stopped typing 1 second ago</div>
var delay = (function(){
var timer = 0;
return function(callback, ms){
clearTimeout (timer);
timer = setTimeout(callback, ms);
};
})();
inputFilter.keyup(function() {
delay(function(){
var term = inputFilter.val().toLowerCase();
listItems.each(function(i, e){
var city = ($(e).text()).toLowerCase();
if(city.startsWith(term)){
console.log(city);
}
});
}, 800 );
});
try the following code:
Html:
<input type="text" id="inputtext" />
Jquery code:
<script type="text/javascript">
$(document).ready(function () {
$('#inputtext').keyup(function () {
setTimeout(function () {
alert("Hi");
}, 5000);
});
});
</script>
On each keyup count clicks and after some of them trigger function.
Pro-tip: And use
.on('keyup', function(){}).

Anonymous function in setTimeout not working

I'm trying to create a delay between two loops of the nivo-slider.
Without the setTimeout everything works just fine (but without delay). So the folloing example works:
$('#slider').nivoSlider({
lastSlide: function(){
$('#slider').data('nivo:vars').stop = true;
// setTimeout(function() {
$('#slider').data('nivo:vars').stop = false;
// }, 2000);
},
});
If I uncomment the setTimeout-lines the slider stops but does not start again? Any ideas why?
Update:
http://jsfiddle.net/kgYNX/
2nd update:
Tried it with a wrapping function, too. The function gets called but if I use setTimeout in the new function it stops working: http://jsfiddle.net/kgYNX/1/
Solved it slightly different:
beforeChange: function(){
$('#slider').data('nivo:vars').stop = true;
var delay = 0;
if ($('#slider').data('nivo:vars').currentSlide == $('#slider').data('nivo:vars').totalSlides - 2) {
delay = 2000;
}
setTimeout(function() {
$('#slider').data('nivo:vars').stop = false;
}, delay);
}
I don't know why "totalSlides - 2", but it works: http://jsfiddle.net/kgYNX/15/
As a variant, you may add custom option to slider vars collection to prevent stop execution on lastSlide handler when slider re-enabled by timeout:
lastSlide: function () {
var dontStop = $('#slider').data('nivo:vars').dontStopOnLast;
if (!dontStop) {
$('#slider').data("nivoslider").stop();
setTimeout(function () {
$('#slider').data("nivoslider").start();
}, 2000);
}
$('#slider').data('nivo:vars').dontStopOnLast = !dontStop;
}

Categories