I need to set delays between "each" iteration
if (index == 3) {
$$('.check').each(function (el, ind) {
if (ind > 0) {
$$('.check')[ind - 1].addClass('hide');
};
$$('.check')[ind].removeClass('hide'); /*here should come delay*/
});
}
Please advice ;)
As Sergio said, you can use the index in the iteration for a cascading effect.
(function(){
// don't always lookup through DOM, cache el references if they are
// not changing dynamically
var checks = $$('.check'),
index = 3;
// ...
if (index === 3){
checks.each(function(el, i){
// i truthy is enough.
i && checks[i-1].addClass('hide');
// use a 100ms incremental delay for each next el
this.delay(i*100, el, 'hide');
}, Element.prototype.removeClass);
}
}());
the Function.prototype.delay can be applied on Element.prototype.removeClass (used as context for fun).
keep in mind that the above is broken - we can't see all your code or the intent behind it. delaying the applied removeClass will work but it will undo the addClass('hide') later so you'd end up with all elements visible.
As I understand you want to do a kind of highlight. You can use a native setTimeout for that. You have 2 options:
#1 animate all looped elements at the same time
#2 animate looped elements one at a time
option #1
if (index == 3) {
var check = $$('.check'); // cache this, once.
var delay = 1000; // 1 second
check.each(function (el, ind) {
if (ind > 0) {
check[ind - 1].addClass('hide');
};
(function () { // to lock the value of ind
var i = ind;
setTimeout(function () {
check[i].removeClass('hide'); /*here should come delay*/
}, delay);
})();
});
}
option #2
This case is very similar but multiplies the delay time by the index of the iteration and making animation delay/timeout bigger for each loop iteration
if (index == 3) {
var check = $$('.check'); // cache this, once.
var delay = 1000; // 1 second
check.each(function (el, ind) {
if (ind > 0) {
check[ind - 1].addClass('hide');
};
(function () { // to lock the value of ind
var i = ind;
setTimeout(function () {
check[i].removeClass('hide'); /*here should come delay*/
}, delay * i);
})();
});
}
Related
I have a li that I want to animate while hovering over.
I try to get the previous li item with ยง(this).prevAll("li") and then use JQuery to change the style.
For this I want to get the IDs of the previous items. My current code looks like this:
$("#somediv li").hover(function()
{
var object = $(this).prevAll("li");
var i;
for (i = object.length - 1; i >= 0; i--) {
dowork(i);
}
function dowork(i) {
setTimeout(function() {
console.log(i);
$("#"+object[i].id+"").addClass("animateprogress");
}, 500 * i);
}
Output of the for loop is 5,4,3,2,1,0 (depending on object length). The timeout output though reverses this to 0,1,2,3,4,5. I don't know why tough. I just want to output 5,4,3,2,1,0 with 500ms delay to style my previous li elements.
You'll have to distinguish between the delay you want and the object index you want. Both are now the same variable i in your function, but they should be somewhat opposite.
So for example do:
$("#somediv li").hover(function() {
var object = $(this).prevAll("li");
var i;
for (i = 0; i < object.length; i++) {
// pass a delay that increases with each iteration,
// and pass an index that decreases with each iteration:
dowork(500 * i, object.length - 1 - i);
}
function dowork(delay, index) {
setTimeout(function() {
console.log(index);
$("#"+object[index].id).addClass("animateprogress");
}, delay);
}
});
try:
setTimeout(animate, (object.length - 1 - i) * 500)
instead.
you can use these codes too
$("#somediv li").hover(function()
{
var object = $(this).prevAll("li");
var i;
for (i = object.length - 1; i >= 0; i--) {
dowork(i,(object.length-i)*500);
}
function dowork(i,d) {
setTimeout(function() {
console.log(i);
$("#"+object[i].id+"").addClass("animateprogress");
}, d);
}
It will give the result you expect.
The issue was on delay time you was adding greater time for greater number which is opposite of your expectation
I am wondering how to loop this auto-slide function? Its for this timeline here: https://bm-translations.de/#sprachrichtungen
I tried with if condition to check if next slide has items, but maybe because I am a beginner, its not working.
function timeLineAutoPlay() {
var current = 0;
setInterval(function() {
current++;
var current_elem = $($('.events a')[current - 1]);
if (current == 11) {
timeLineAutoPlay();
}
$($('.events a')[current]).trigger('click');
if ((current + 1) % 4 == 0) {
$('.next').trigger('click');
}
}, 8000);
}
timeLineAutoPlay();
Slider is frome here: https://codyhouse.co/gem/horizontal-timeline/
2nd problem:
If I click on a date, the next autoslide isnt the date after. Do you know how to adjust this code?
Tried this, but its not working:
timelineComponents['eventsWrapper'].on('click', 'a', function(event){
current = items.length;
}
The modulo operator is useful for wrapping at a specific number and creating a loop:
Edit: Included example for manual slider control.
Edit 2: Fixed typo in function call
// The interval id for the auto-slider if currently running.
var intervalId;
// The current slide index
var current = 0;
// Query document for the DOM nodes
var items = $('.events a');
// Clicks on `a` will bubble up to their `li` element and then to `div.events`.
// We can use this to listen to the click on li and then see which one was
// targeted.
$('.events').on('click', 'li', function(event) {
// If the third `li` was clicked, there are 2 in front of it so 2 is the index
// of the item that was clicked.
var count = $(this).prevAll('li').length;
// Update the current item based on the clicked element.
current = count;
// Reset the interval so the next slide does not occur immediately after the
// user has clicked.
if (intervalId != null) {
// Clear previous auto-play
clearInterval(intervalId);
// And start a new one which will first trigger 8000ms from now.
intervalId = timeLineAutoPlay();
}
});
function timeLineAutoPlay() {
// Return the interval id so that we can can cancel it later.
return setInterval(function() {
// Increment `current` but wrap at `items.length`, thus looping through the
// list of items.
//
// E.g. for 4 items; items.length === 4:
// current : 0 -> 1 -> 2 -> 3 -> 0 -> 1 -> ...
current = (current + 1) % items.length;
// Get the item at the current index
var item = items.eq(current);
// This would lead to multiple interval loops being spawned whenever we
// reach 11. That seems unintentional.
//
// if (current == 11) {
// timeLineAutoPlay();
// }
item.trigger('click');
}, 8000);
}
// Start the initial auto-slide and store the id
intervalId = timeLineAutoPlay();
Something like the following:
setInterval(function(){
var current_elem = $($('.events a')[current]);
if(!current_elem.hasClass("selected")){
$(".events a").each( function(index, elem) {
if($(this).hasClass("selected")){
current = index;
}
});
}
current++;
if(current >= $('.events a').length) {
current = 0;
}
$($('.events a')[current]).trigger('click');
}, 7000);
With this code how would I make the render staggered? I want it to render each element one by one rather than just rendering all at once. I've tried setTimeout but don't know how to implement it or whether its even the right way to do it.
renderSelected() {
var temp=[];
for (var i = 0; i < 11; i++) {
temp.push(this.selected(i))
}
return temp;
}
selected(number) {
return (<View key={number} style={styles.normal} >
<Text>{number}</Text>
</View>);
}
Update based on the answer but it still doesn't work. The code in the answer was too different since this is React Native.
renderLater(i) {
TimerMixin.setTimeout(() => {
this.selected(i);
}, 100);
}
renderSelected() {
var temp=[];
for (var i = 0; i < 11; i++) {
temp.push(this.renderLater(i))
}
return temp;
}
selected(number) {
return (<View key={number} style={styles.normal} >
<Text>{number}</Text>
</View>);
}
Based on the code, the problem is that you return temp which actually contains nothing, since renderLater returns nothing.
A solution is to create an element with a state, and depending on the state your render one or more elements. This is similar to the timer element on the reactjs page, where the state is updated every second triggering a new rendering. Instead of changing the ticker every second, you can increase a counter and in renderSelected() display all the elements up to that counter. There is no renderLater, just a this.setState() called regularly triggering a new rendering with a different number of elements.
var MyClass = React.createClass({
getInitialState: function() {
return {counter: 0};
},
tick: function() {
if (this.state.counter >= 10) return;
this.setState({counter: this.state.counter + 1});
},
componentDidMount: function() {
this.interval = setInterval(() => {this.tick();}, 50);
},
componentWillUnmount: function() {
clearInterval(this.interval);
},
render: function() {
var temp=[];
for (var i = 0; i <= 10 && i <= this.state.counter; i++) {
temp.push(this.selected(i))
}
return temp;
},
selected: function(number) {
return (<View key={number} style={styles.normal} >
<Text>{number}</Text>
</View>);
}
});
ReactDOM.render(<MyClass />, mountNode);
Live demo
You can also instead create all the elements separately from the start with each an empty render() function in the beginning and have them display something when enough time is elapsed. For that, you can still use the x.setState() function for each separate element to trigger a new rendering. This avoids erasing and redrawing already drawn elements at each tick.
Old answer:
You can do a global queue for delayed stuff.
var queue = [];
/* executes all element that have their delay elapsed */
function readQueue() {
var now = (new Date()).getTime();
for (var i = 0; i < queue.length; i++) {
if (queue[i][0] <= now) {
queue[i][1]();
} else {
if(i != 0) queue = queue.slice(i);
return;
}
}
queue = [];
}
/* Delay is in milliseconds, callback is the function to render the element */
function addQueue(delay, callback) {
var absoluteTime = (new Date()).getTime() + delay;
for (var i = 0; i < queue.length; i++) {
if (absoluteTime < queue[i][0]) {
queue.splice(i, 0, [absoluteTime, callback]);
return;
}
}
queue.push_back([absoluteTime, callback]);
}
var queueTimer = setInterval(readQueue, 10); //0.01s granularity
With that queue, if you want to render something some elements every 50ms later, then you can just do:
function renderElementLater(time, index) {
/* render */
addQueue(time, function(){ReactDOM.render(selected(index), mountNode);}):
}
for (var i = 0; i <= 10; i++) {
renderElementLater(50*i, i);
}
You can change the granularity (when the queue is read and checked) to something even less than every 10ms for finer control.
Although, I don't see what's the problem with the setTimeout. You could just do:
function renderElementLater(time, index) {
/* render */
setTimeout(function(){ReactDOM.render(selected(index), mountNode);}, time):
}
for (var i = 0; i <= 10; i++) {
renderElementLater(50*i, i);
}
Maybe your problem came that if you want to use the value of a variable that changes in a callback created in a loop, then it needs to be enclosed by another function (like I did by creating the new function renderElementLater instead of directly putting function code there).
I want to use setInterval to animate a couple things. First I'd like to be able to specify a series of page elements, and have them set their background color, which will gradually fade out. Once the color returns to normal the timer is no longer necessary.
So I've got
function setFadeColor(nodes) {
var x = 256;
var itvlH = setInterval(function () {
for (i in nodes) {
nodes[i].style.background-color = "rgb(0,"+(--x)+",0);";
}
if (x <= 0) {
// would like to call
clearInterval(itvlH);
// but itvlH isn't in scope...?
}
},50);
}
Further complicating the situation is I'd want to be able to have multiple instances of this going on. I'm thinking maybe I'll push the live interval handlers into an array and clean them up as they "go dead" but how will I know when they do? Only inside the interval closure do I actually know when it has finished.
What would help is if there was a way to get the handle to the interval from within the closure.
Or I could do something like this?
function intRun() {
for (i in nodes) {
nodes[i].style.background-color = "rgb(0,"+(--x)+",0);";
}
if (x <= 0) {
// now I can access an array containing all handles to intervals
// but how do I know which one is ME?
clearInterval(itvlH);
}
}
var handlers = [];
function setFadeColor(nodes) {
var x = 256;
handlers.push(setInterval(intRun,50);
}
Your first example will work fine and dandy ^_^
function setFadeColor(nodes) {
var x = 256;
var itvlH = setInterval(function () {
for (i in nodes) {
nodes[i].style.background-color = "rgb(0,"+(--x)+",0);";
}
if (x <= 0) {
clearInterval(itvlH);
// itvlH IS in scope!
}
},50);
}
Did you test it at all?
I've used code like your first block, and it works fine. Also this jsFiddle works as well.
I think you could use a little trick to store the handler. Make an object first. Then set the handler as a property, and later access the object's property. Like so:
function setFadeColor(nodes) {
var x = 256;
var obj = {};
// store the handler as a property of the object which will be captured in the closure scope
obj.itvlH = setInterval(function () {
for (i in nodes) {
nodes[i].style.background-color = "rgb(0,"+(--x)+",0);";
}
if (x <= 0) {
// would like to call
clearInterval(obj.itvlH);
// but itvlH isn't in scope...?
}
},50);
}
You can write helper function like so:
function createDisposableTimerInterval(closure, delay) {
var cancelToken = {};
var handler = setInterval(function() {
if (cancelToken.cancelled) {
clearInterval(handler);
} else {
closure(cancelToken);
}
}, delay);
return handler;
}
// Example:
var i = 0;
createDisposableTimerInterval(function(token) {
if (i < 10) {
console.log(i++);
} else {
// Don't need that timer anymore
token.cancelled = true;
}
}, 2000);
I am trying to create a page which needs to preform lots of loops. Using a while/for loops cause the page to hang until the loop completes and it is possible in this case that the loop could be running for hours. I have also tried using setTimeout, but that hits a recursion limit. How do I prevent the page from reaching a recursion limit?
var looper = {
characters: 'abcdefghijklmnopqrstuvwxyz',
current: [0],
target: '',
max: 25,
setHash: function(hash) {
this.target = hash;
this.max = this.characters.length;
},
getString: function() {
string = '';
for (letter in this.current) {
string += this.characters[this.current[letter]];
}
return string;
},
hash: function() {
return Sha1.hash(this.getString());
},
increment: function() {
this.current[0] += 1;
if (this.current[0] > this.max) {
if (this.current.length == 1) {
this.current = [0, 0];
} else {
this.current[1] += 1;
this.current[0] = 0;
}
}
if (this.current[1] > this.max) {
if (this.current.length == 2) {
this.current[2] == 0;
} else {
this.current[3] += 1;
this.current[2] = 0;
}
}
},
loop: function() {
if (this.hash() == this.target) {
alert(this.getString());
} else {
this.increment();
setTimeout(this.loop(), 1);
}
}
}
setInterval is the usual way, but you could also try web workers, which would be a more straightforward refactoring of your code than setInterval but would only work on HTML5 browsers.
http://dev.w3.org/html5/workers/
Your setTimeout is not doing what you think it's doing. Here's what it's doing:
It encounters this statement:
setTimeout(this.loop(), 1);
It evaluates the first argument to setTimeout, this.loop(). It calls loop right there; it does not wait for a millisecond as you likely expected.
It calls setTimeout like this:
setTimeout(undefined, 1);
In theory, anyway. In reality, the second step never completes; it recurses indefinitely. What you need to do is pass a reference to the function rather than the returned value of the function:
setTimeout(this.loop, 1);
However, then this will be window on the next loop, not looper. Bind it, instead:
setTimeout(this.loop.bind(this), 1);
setInterval might work. It calls a function every certain amount of milliseconds.
For Example
myInterval = setInterval(myFunction,5000);
That will call your function (myFunction) every 5 seconds.
why not have a loop checker using setInterval?
var loopWorking = false;
function looper(){
loopWorking = true;
//Do stuff
loopWorking = false;
}
function checkLooper()
{
if(loopWorking == false)
looper();
}
setInterval(checkLooper, 100); //every 100ms or lower. Can reduce down to 1ms
If you want to avoid recursion then don't call this.loop() from inside of this.loop(). Instead use window.setInterval() to call the loop repeatedly.
I had to hand-code continuation passing style in google-code prettify.
Basically I turned
for (var i = 0, n = arr.length; i < n; ++i) {
processItem(i);
}
done();
into
var i = 0, n = arr.length;
function work() {
var t0 = +new Date;
while (i < n) {
processItem(i);
++i;
if (new Date - t0 > 100) {
setTimeout(work, 250);
return;
}
}
done();
}
work();
which doesn't hit any recursion limit since there are no recursive function calls.