jquery each() with setInterval - javascript

I have an object filled with various elements that I wish to iterate through using each() and then perform an action on the element whose turn it is. So:
var arts = $("#press-sqs > article");
shuffle(arts);
$(arts).each(function(){
setInterval(function() {
// in here perform an action on the current element in 'arts'
}, 2000);
});
( shuffle() is a basic shuffle function )
What I can't figure out is how to access the current element as a selector and perform an action on it. $(this) is $(window).
Finally I would then need the function to start the iteration again once it reaches the end of art and keep on looping ad infinitum.

If you're using setInterval, you'd get identical results swapping the order:
setInterval(function() {
$(arts).each(function(){
doSomethingWith(this);
});
}, 2000);
I don't think you want what you think you do here. I reckon you want:
var i = 0;
setInterval(function() {
var art = arts[i++];
doSomethingWith(art)
if(i >= arts.length) i = 0;
}, 2000);

jQuery's .each(...) method passes the "current" element (and its index) into the callback. this is just a convenience for when you don't need to do anything too complicated.
$(arts).each(function(i, current){
setInterval(function() {
// in here perform an action on the current element in 'arts'
}, 2000);
});
Above, the current element is available within the setInterval callback as current, for example. Note that this element is passed in its "raw" form, as this is, so if you want to call jQuery methods on it, you'll need to wrap it in the same way, ie: $(current).

Use that.
$(arts).each(function(){
var that = this;
setInterval(function() {
// in here perform an action on the current element in 'arts'
doSomethingWith(that)
}, 2000);
});

Related

JavaScript function parameter error

I have this simple slideshow set up and it all works fine if I remove the parameters and add the class and speed manually.
If I use parameters to set the class and speed, it fails. With the first image getting the current class applied and then the dom seems to go crazy.
‘undefined’ pops up a lot although there are no errors in console.
Any help would be appreciated. Thanks
let pos = 0;
window.addEventListener("load", function(event) {
testIt('current', 5000);
});
function testIt(_class, _speed) {
const testPara = document.querySelectorAll('.bg_imgs');
let i;
for(i = 0; i < testPara.length; i++) {
testPara[i].classList.remove(_class);
}
pos++;
if(pos > testPara.length) {pos = 1;}
testPara[pos-1].classList.add(_class);
setTimeout(_class, _speed); }
It looks like you're sending the wrong parameters to setTimeout. It should take the form setTimeout(function, time), but instead, you're passing it what I assume is a string.
setTimeout(function() { testIt(_class, _speed); }, _speed);
should work
SetTimeout takes a function as its first param. If you are trying to delay the recursion try:
setTimeout(() => testIt(_class, _speed), _speed)
Thought window.requestAnimationFrame would be a better solution. More info on animation frame

How can I loop jQuery code?

I've got a jQuery code which is supposed to change images after some amount of time and it works well, but it obviously stops as soon as the code ends. How can I make it run over and over again? I tried using javascript "if" loop but it didn't do anything.. or maybe I did it wrong?
(w4s and w5s are img's IDs)
Also I'm quite new to jQuery so if you have any comments about any errors I've made, I'd be glad to hear them!
Here's the code
$(function () {
$("#w4s").hide();
$("#w5s").hide();
$(document).ready(function () {
$(function () {
$("#w3s").delay("4000").fadeOut("slow", function () {
$("#w4s").fadeIn("slow",function () {
$(this).delay("4000").fadeOut("slow",function () {
$("#w5s").fadeIn("slow");
});
});
});
});
});
});
I guess you need something like this
window.setInterval(function() {
alert('I happen every 8 seconds');
}, 8000);
First of all:
$(document).ready(function() {...
is equivalent to
$(function() {...
so keep the latter and drop the usage of the former.
Second, understand what this invocation actually does: it tells jQuery to fire the callback (function() {...) once the DOM's ready. Therefore, you generally only need a single invocation of this pattern for all your code (unless you want different scopes, that is).
So, start your code like this in the outer most scope:
<script type="text/javascript">
$(function() {
// Your code goes here !!!
});
</script>
Now, since we've covered the basics, let's take care of your problem.
$(function(){
var looplength = 8000;
// You can combine selectors!!!
$("#w4s, #w5s").hide();
// Let's drop all these nested `domready` callbacks and
// in their stead set up an interval
window.setInterval(function() {
$("#w3s").delay("4000").fadeOut("slow", function(){
$("#w4s").fadeIn("slow",function(){
$(this).delay("4000").fadeOut("slow",function(){
$("#w5s").fadeIn("slow");
});
});
});
}, looplength);
});
I would use a timeout for this - its probably just a personal preference, but I find them much more efficient than intervals, and it gives me more control over the continuation of the loop.
Something like this would do it:
//store the timer in your outer scope
var timer = null;
//create an empty elements var in your outer scope for use later
var elements;
//create a loop function do do most of your work for you
function loop(duration, index){
//set the defaults if none are passed in
duration = typeof duration == "number" ? duration : 8000;
index = typeof index == "number" ? index : 0;
//your function made a bit more generic
//by selecting from the store elements list
$(elements[index]).fadeOut("slow", function(){
//Increase the index by 1 to select the next element,
//or reset to 0 if it is greater than the number of elements you have
index = index + 1 < elements.length ? index + 1 : 0;
$(elements[index]).fadeIn("slow");
});
//clear the timeout in case it hasn't been called already
clearTimeout(timer);
//set the timeout function to call this function again
// passing it back the index and duration
timer = setTimeout(function() {
loop(duration, index)
}, duration);
};
//Instantiate the loop function for the first time on document.ready
//It should continue on its own after that
$(document).ready(function() {
//set the elements after the DOM is loaded
elements = $("#w3s, #w4s, #w5s");
loop(4000);
});
Hope this helps. It a fairly robust approach, so you could reuse this function elsewhere as well. If you need to cancel the loop at any point, you have it stored as timer so you can just call clearTimeout('timer') so long as you are in the same scope.
Fiddle available here - https://jsfiddle.net/heuw8dt0/2/
EDIT:
Moved element selection inside the DOM ready function

Send setTimeout name, element id, and time as parameters of a function?

This is probably something really obvious, but I've searched around and tried a few things, and can't get it to work, so maybe someone can point out my error here.
I have a setTimeout that I will end up using over and over (and I know there is the setinterval, but I actually need to control when the timer starts and stops, and whether it starts again each time). Anyway, I figured if I'm writing it over and over, I should be able to use a function and pass it the parameters needed.
if ($('#selectRole').val() === 'Dispatch') {
//show Add Notes button
var funcAddNotesTimer = function(timerName,buttonName, timeToHide) {
console.log(timerName);
console.log(timeToHide / 1000);
timerName = setTimeout(function() {
$('buttonName').show();
}, timeToHide);
};
funcAddNotesTimer('addNotesTimer', '#disAddNotes', 30000);
I'm trying to set the timer function name to 'addNotesTimer', and when the timer is up I want to show the button with id #disAddNotes, and I want the timer to run for 30000 msec.
To me, what I have looks right, but I never get anything in my console log, so I don't think it's even getting into the function.
What am I doing wrong here?
I dont think its possible to use a string argument as the name of setTimeOut
Heres how you could approach it
// var timer = null; // dont really need that
var funcAddNotesTimer = function(buttonName, timeToHide) {
var timerName = setTimeout(function() {
//$('buttonName').show();
$(buttonName).show(); // buttonName is already a string so no need to add quotes around it.
}, timeToHide);
return timerName;
};
if ($('#selectRole').val() === 'Dispatch') {
var timer = funcAddNotesTimer('#disAddNotes', 30000);
// do something with timer
}
when you don't see any output in the console the reason must be something else(e.g. there is a bracket missing at the end of the code)
To set a variable with a dynamic name use the subscript-notation:
window[timerName] = setTimeout(/**/);
it will set a global variable named addNotesTimer.
As you currently do it you're simply overwriting the argument passed to the function.
Summary:
if ($('#selectRole').val() === 'Dispatch') {
//show Add Notes button
var funcAddNotesTimer = function(timerName,buttonName, timeToHide) {
console.log(timerName);
console.log(timeToHide / 1000);
window[timerName] = setTimeout(function() {
$(buttonName).show();
}, timeToHide);
};
funcAddNotesTimer('addNotesTimer', '#disAddNotes', 5000);
}

Use jQuery to attribute selector to select all IDs and run a function

I want to get all the IDs that start with blblblb_ and run a separate JS function that I made. Here is what I have, it is only getting the first ID:
$(window).scroll(function() {
var test = $('div[id^="blblblb_"]').attr('id');
foo(test);
});
Any ideas what I'm doing wrong?
The problem is that attr gives you the id of the first element in collection and you're only calling foo once anyway.
Use each to execute a function for all elements of a jquery collection :
$(window).scroll(function() {
$('div[id^="blblblb_"]').each(function(){
foo(this.id);
});
});
Based on your comment, if you have a growing collection of objects having this kind of id and wanting to be sure foo is only called once for each id, you might do this :
var done = {};
$(window).scroll(function() {
$('div[id^="blblblb_"]').each(function(){
if (!done[id]) {
foo(this.id);
done[id] = 1;
}
});
});
But I'm doubtful about the use case...

jQuery - why can't I bind events to elements in a loop?

Here is my code:
var b = $(slipStream.conf.mainVis).find('p#prev');
b.click(function() {
slipStream.slideLeft();
return false;
});
b = $(slipStream.conf.mainVis).find('p#next');
b.click(function() {
slipStream.slideRight();
return false;
});
b = $(slipStream.conf.controls).find('li img');
console.log(b);
for (var l in b) {
l.click(function() {
var visIndex = l.index();
console.log(visIndex);
});
};
The first two bindings go through, no problem. But I can't loop through a collection and bind something to each member? (the console is telling me that "l.click is not a function.") Is this a limitation of jQuery or is my code off? This seems like it would be the way to do it, though...
When you enumerate over a jQuery object, the values being enumerated are actual DOM nodes and not jQuery wrappers. Therefore, they don't have a click method but you can wrap them again to get all the usual functionality.
Of course this is not necessary because you can simply attach a wrapper directly from your initial jQuery instance:
$(slipStream.conf.controls).find('li img').click(function() {
var visIndex = $(this).index();
console.log(visIndex);
});
This is the classic "loop variables don't work properly in callbacks" bug.
Your variable l no longer has the originally supplied value by the time the callback is invoked - it has whatever final value was assigned in the last pass through the loop.
[FWIW, l isn't actually a jQuery object, so you have to wrap it - $(l) to use it with jQuery]
The usual fix to the loop bug is to create an additional closure that returns a function bound to the current value:
for (var l in b) { // NB: don't use `for ... in ...` on array-like objects!
var make_cb = function(n) {
return function() {
var visIndex = $(n).index();
console.log(visIndex);
}
}
$(l).click(make_cb(l));
};
Fortunately, you don't need a loop at all - you can have jQuery automatically add the callback to every element by itself:
b = $(slipStream.conf.controls).find('li img');
b.click(function() {
var visIndex = $(this).index();
console.log(visIndex);
});
Could it be that the problem is the forloop. .click is part of the jQuery, so you must be sure that it's called on element that is wrapper with jQuery.
$.each(b, function (index, element) {
$(element).click(function() {
});
};
With each() you can iterate through a set of jQuery objects:
$(slipStream.conf.controls).find('li img').each(function(){
$(this).click(function() {
var visIndex = $(this).index();
console.log(visIndex);
});
});
$(this) will match the currently indexed object from the collection.

Categories

Resources