delay adding class to elements with setTimeout? - javascript

So I have a bunch of div elements that I want to add a class to, so it trigger a transition -- but I want the class to be added 1 at a time, not simultaneously so the animation appears 1 at a time.
This is how my javascript looks like.
var campaignInfo = document.querySelectorAll(".campaign-info-container");
setTimeout(function(){
for(i=0; i<=campaignInfo.length;i++){
campaignInfo[i].classList.add("campaign-container-slide");
console.log(campaignInfo[i]);
};
}, 2000);
so basically I use querySelectorAll so it captures all the divs with that class, which shows up when I console.log() it.
But regardless the classes are not added with a delay.. I'm not sure how I can add an iteration to the milliseconds 2000 * i if i is only defined inside the loops. I tried creating a var counter; outside the setTimeout, and increment it inside the loop with counter++, but the delay still won't work.
Also, I'm getting this error "Can't read property classList of undefined.
Thanks guys. Oh and please no jQuery :)
EDIT:
Thanks for the solution! :)
So does the setInterval work like a loop in your case?
Because I want this interval to occur whenever I click on a button, but removed when I exit out of the frame. So I added the below to remove the class from the elements. But I use a for loop instead..
for(i=0; i<=campaignInfo.length; i++){
campaignInfo[i].classList.remove("campaign-container-slide");
}
and this works, but I'm just curious how come my first initial code (not this one above) doesn't work? how come using a counter works but not looping through it?

If I understand you correctly, you could use nested setTimeout calls, or use setInterval:
var campaignInfo = document.querySelectorAll(".campaign-info-container");
var index = 0; // The index of the next `campaignInfo` div to update
var interval = setInterval(function() { // Create a new interval that fires every 500ms
campaignInfo[index++].classList.add("campaign-container-slide");
if (index === campaignInfo.length) { // The last div has been reached, so...
clearInterval(interval); // Clear the interval using the reference
}
}, 500);
body { font-size: 2em;}
.campaign-info-container { background: #555; }
.campaign-container-slide { background: #eee; }
<div class="campaign-info-container">One</div>
<div class="campaign-info-container">Two</div>
<div class="campaign-info-container">Three</div>
<div class="campaign-info-container">Four</div>

Related

JQuery animation not running in correct sequence

I am creating a game called 'Pointless'. Pointless here is a game show in the UK. Anyway, there is a countdown which counts down from 100 to whatever score the team got. I am trying to replicate this. For an example, please see this video.
Anyway, I am trying to replicate the countdown myself, however whenever I try the whole thing gets executed at once instead of one div at a time like it should. I need to hide those divs one by one.
Please see this JSFiddle.
for (var i = 0; i <= 10 ; i++) {
$('#' + i).toggle('slide');
}
When you call toggle or any other animate functions in jQuery, it does not block the rest of the code. The animation continues on, while the rest of the code is running. You can add a delay for each of those blocks to start the animation.
You can try this one:
I also suggest you to use .slideToggle('slow') instead of .toggle('slide').
$('#' + i).delay(i*100).slideToggle('slow');
Because the .toggle() events (along with other events) are accually enqued and triggered after the execution of the entire for-loop. Or rather even if they where not, you are calling toggle on all of them nearly all at once, so they will all toggle at the same time. One way to get around it it to use a timer such as setInterval or setTimeout:
$('#Go').click(function(){
var i = 0;
var timer = setInterval(function(){
i++;
$('#' + i).toggle('slide');
if(i > 10) clearInterval(timer);
},100)
})
Fiddle Example
Amir solution is greate. I want juste to add a small correction :
toggle method of jQuery doesn't have any arguments whose value could be slide.
Here is the correct syntaxe :
$(selector).toggle(speed,easing,callback)
speed in [milliseconds, "slow", "fast"]
easing in ["swing", "linear"] (More easing functions are available in external plugins)
callback is a function

Replay jQuery function every 5 seconds

I want to replay my jquery function ChangeStats() every 5 seconds, it's currently doing sod all.
function ChangeStats() {
$('body').find('.admin-stats-big-figures-hidden').fadeIn(500);
setTimeout(function() {
$('body').find('.admin-stats-big-figures').fadeOut(500);
}, 500);
}
$(document).ready(function(){
setInterval(ChangeStats, 5000);
})();
Yes I have got the right class names.
No I haven't used underscores in my HTML.
I think it's something to do with my use of "find()", once the DOM has loaded and the function is set is it meant to traverse up the DOM tree instead of down?
EDIT:
Updated code, still not working.
HTML:
<span class="admin-stats-big-figures">%productCount%</span>
<span class="admin-stats-big-figures-hidden">hey</span>
Ok, I am going to go out on a limb and make several assumptions here; one is that you wish to cycle between two elements repeatedly, another is that you are using $(this) in the context of the window rather than a containing element. If either of these are incorrect then the following solution may not be suitable. However, let's give this a shot, eh?
1) You need to use setInterval rather than setTimeout to create a repeating call. You can of course "chain" your timeouts (ie: call the succeeding timeout from the code of the current timeout). This has some benefits in certain situations, but for now let's just assume you will use intervals rather than timeouts.
2) You call the find() jQuery method every time, which is a little unnecessary, especially if you will be repeating the actions so one idea would be to cache the lookup. If you are going to do that a custom object would be more suitable than separate global variables.
3) Some flexibility in terms of starting and stopping the animation could be provided. If we use a custom object as mentioned in (2) then that can easily be added.
4) You are using fadeIn and fadeOut, however if you wish the items to cycle then fadeToggle may be your best solution as it will simply allow you to do exactly that, toggle, without needing to check the current opacity state of the element.
5) Finally in my example I have provided a little extra "padding HTML" in order for the example to look good when run. Fading in jQuery will actually set the faded item to a CSS display of "none" which results in the content "jumping about" in this demo, so I have used some div's and a couple of HTML entity spaces to keep the formatting.
Ok, after all that here is the code..
// your custom animation object
var myAnim = {
// these will be cached variables used in the animation
elements : null,
interval : null,
// default values for fading and anim delays are set to allow them to be optional
delay : { fade: 500, anim: 200 },
// call the init() function in order to set the variables and trigger the animation
init : function(classNameOne, classNameTwo, fadeDelay, animDelay) {
this.elements = [$("."+classNameOne),$("."+classNameTwo)];
// if no fade and animation delays are provided (or if they are 0) the default ones are used
if (animDelay) this.delay.anim = animDelay;
if (fadeDelay) this.delay.fade= fadeDelay;
this.elements[0].fadeOut(function(){myAnim.start()});
},
// this is where the actual toggling happens, it uses the fadeToggle callback function to fade in/out one element once the previous fade has completed
update : function() {
this.elements[0].fadeToggle(this.delay.anim,function(el,delay){el.fadeToggle(delay)}(this.elements[1],this.delay.anim));
},
// the start() method allows you to (re)start the animation
start : function() {
if (this.interval) return; // do nothing if the animation is currently running
this.interval = setInterval(function(){myAnim.update()},this.delay.fade);
},
// and as you would expect the stop() stops it.
stop : function () {
if (!this.interval) return; // do nothing if the animation had already stopped
clearInterval(this.interval);
this.interval = null;
}
}
// this is the jQuery hook in order to run the animation the moment the document is ready
$(document).ready(
function(){
// the first two parameters are the two classnames of the elements
// the last two parameters are the delay between the animation repeating and the time taken for each animation (fade) to happen. The first one should always be bigger
myAnim.init("admin-stats-big-figures","admin-stats-big-figures-hidden",500,200);
}
);
OK, so now we need the HTML to compliment this (as I say I have added a little formatting):
<div><span class="admin-stats-big-figures">One</span> </div>
<div><span class="admin-stats-big-figures-hidden">Two</span> </div>
<hr/>
<input type="button" value="Start" onclick="myAnim.start()"/> | <input type="button" value="Stop" onclick="myAnim.stop()"/>
I have also provided buttons to stop/start the animation. You can see a working example at this JSFiddle - although the stop/start buttons are not working (presumably something specific to JSFiddle) they do work when in context though.
Here im gonna just replace your $(this). and maybe it'll work then.. also using callback.
function ChangeStats() {
$('body').find('.admin-stats-big-figures-hidden').fadeIn(500, function() {
$('body').find('.admin-stats-big-figures').fadeOut(500);
});
}
$(document).ready(function(){
setTimeout('ChangeStats()', 5000);
});

Mouseleave does not work with .each and delay?

I have written some code to change the colour of each letter inside an a tag and show a pop up when you hover the link.
The mouseenter function works fine but when you hover off the link I would like to do the reverse of the original change ( so change back to the origional colour ).
I take the delay out of the leave function it works but the effect is not as nice. I am confused as to why this works on the enter but not on the leave?
Another thing to mention is when it does change colour back to the grey the mouseenter function does not work again, which is kind of annoying.
Here is a link to the site so you can see what I am talking about and the link is the one at the bottom that says "Touch Marketing"
http://dev.touch-akl.com/colin/
Any help please?
My jQuery looks like this
$('#copyright a').mouseenter(function(){
var $letters = $(this).find('span'),
$sayhi = $(this).find('img'),
delay = 0;
$sayhi.animate({top:-30, 'opacity':1}, 500, "easeInOutExpo");
$letters.each(function(){
$(this).delay(delay).queue(function(){
$(this).css({'color':'#333'});
});
delay+=35;
}); // end each
}).mouseleave(function(){
var $letters = $(this).find('span'),
delay = 0;
$letters.each(function(){
$(this).delay(delay).queue(function(){
$(this).css({'color':'#333'});
});
delay+=35;
});
}); // end leave
jQuery .queue() is complicated to use correctly so unless you need to interact with other things in the jQuery animation queue, it is often much, much simpler to just use setTimeout() like this. You also should make delay a local variable so it isn't an implicit global variable.
}).mouseleave(function(){
var delay = 0;
$(this).find('span').each(function(){
var item = $(this);
setTimeout(function(){
item.css({'color':'#333'});
}, delay);
delay+=35;
});
}); // end leave
Most likely the problem is with the closure created by your functions in mouseenter and mouseleave. They're both referencing the same delay variable. You might want to separate them:
delayEnter = 0;
delayLeave = 0;

How to use javascript to monitor a change in a div value?

I have a page with a countdown in a DIV with id ="count"
I would like to monitor this div value so, when it reaches 0, a alert pops up.
I've gono so far as
if(parseInt(document.getElementById('count').innerHTML) < 2){}
But I don't know how to "listen" for the div changes
Can anyone help me?
Btw: it needs to be in pure javascript, with no such things as jquery.
Update:
I have no say so in the original code. It's an external page and I'm trying to run this code at the address bar
Presumably you have a function running based on setInterval or setTimeout. Have that function call your function when it gets to zero.
If you can't do that, you can try optimised polling - use setInterval to read the value, estimate when it might be near zero, check again and estimate when it might be zero, etc. When it is zero, do your thing.
There are DOM mutation events, but they are deprecated and were never well or widely supported anyway. Also, they are called when content changes so probably too often for your scenario anyway.
If you are changing the value of #count yourself then call the alert from that place. If not use:
window.setInterval(function(){
if(parseInt(document.getElementById('count').innerHTML) < 2) alert('Alarm!');
},1000); // 1s interval
UPDATE
To clear that interval:
var timer = window.setInterval(function(){
if(parseInt(document.getElementById('count').innerHTML) < 2) {
alert('Alarm!');
window.clearInterval(timer);
}
},1000); // 1s interval
//or by using non-anonymous function
function check(){
if(parseInt(document.getElementById('count').innerHTML) < 2) {
alert('Alarm!');
window.clearInterval(timer);
}
}
var timer = window.setInterval(check,1000);
The only efficient way to monitor this is to go to the code that is actually changing the div and modify it or hook it to call a function of yours whenever it updates the contents of the div. There is no universal notification mechanism for anytime the contents of div changes. You will have much more success looking into modifying the source of the change.
The only option I know of besides the source of the change would be using an interval timer to "poll" the contents of the div to notice when it has changed. But, this is enormously inefficient and will always have some of inherent delay in noticing the actual change. It's also bad for battery life (laptops or smartphones) as it runs continuously.
You don't listen for the div to change. The div is just there for a visual representation of the program's state.
Instead, inside whatever timing event is counting down the number, use a condition such as...
if (i < 2) {
// ...
}

setInterval works only the first time

I am trying to use setTimer to animate a slide show using straightforward jQuery. I provide the user with a button (in the form of a "DIV" with a button background image) that he clicks to start the show and which then turns into a pause button. The slides are supposed to change every 3 seconds. Here is the relevant code:
playLink = $('<div id="lbPlayLink" />').click(function(){
$(playLink).hide();
$(pauseLink).show();
slideInterval = setInterval(function(){next()}, 3000)
})[0];
pauseLink = $('<div id="lbPauseLink" />').click(function(){
$(playLink).show();
$(pauseLink).hide();
clearInterval(slideInterval);
}).hide()[0];
The next() function call does the work of replacing the slide with the next one. I have checked out this function and it works perfectly if I call it directly (synchronously), however, when it gets called asynchronously by the setInterval, it works fine the first time (3 seconds after I click on the button), but is never called again, even though it should be called 3 seconds later. I know it's never called as I put an "alert" call at the beginning and end of the function.
If I replace the next() call in the setInterval with alert('test') then I can see the setInterval is doing what it is supposed to. I can't for the life of me see why alert() is OK but next() isn't, unless it has something to do with "scope" of functions, but in that case why does it work the first time?
I've tried debugging the code with firebug, but it can't really help with timeout functions like this. Neither Firefox nor IE8 show any error messages.
I've looked through the various posts here and elsewhere on setInterval, but can't see anything relevant that I haven't tried already. I've been experimenting now for about 3 hours and it's doing my head in. Can anyone suggest what I can try next?
Hmm, I don't like the way you wrote the code, it is not very readable, I would rather suggest something like the following (not tested yet, not sure if it works):
The CSS:
#slides{
/* So you can position your elements under the div#slides */
position: relative;
}
.button {
width: 50px;
height: 10px;
/* So you can position your button anywhere you like */
position: absolute;
bottom: 10px;
right: 10px;
}
.play {
background:url(..) no-repeat;
}
.pause {
background:url(..) no-repeat;
}
The HTML consist of the parent slides holding everything relating to slides, and the controller, basically holds your button image.
<div id="slides">
<div id="controller" class="button play"></div>
</div>
The code:
(function() {
//Let's wrap everything in an anonymous function, so to avoid variable confusion
function next() {
//Assume this is your code doing the sliding. I don't touch
}
var invt;
function play() {
//Always clear interval first before play
if (invt) clearInterval(invt);
invt = setInterval(function() {
next();
}, 3000);
}
function pause() {
if (invt) clearInterval(invt);
}
$(document).ready(function() {
$('#controller').click(function() {
//It's not playing so it has the play class
if ($(this).hasClass('play')) {
$(this).removeClass('play').addClass('pause');
pause();
}else{
$(this).removeClass('pause').addClass('play');
play();
}
});
});
})();
change this line on both:
$('<div id="lbPlayLink" />').click(function(){
to:
$('<div id="lbPlayLink" />').live("click", function(){
To clarify how I solved the problem:
I took onboard Ivo's suggestion to have a single button (though in the form of a "DIV" element rather than a "BUTTON") and change the class of this button between "lbPlay" and "lbPause" to change the background image and trigger the correct action.
My main problem was that I was unknowingly setting the event on this button multiple times and I believe this is what was causing the strange behaviour.
I got round it by putting the return code from the "setInterval" call into a variable ("ppHandler") attached to the DIV, using the jQuery "data" method.
The new code is:
if (typeof($(playPauseLink).data('ppHandler')) == 'undefined'){
$(playPauseLink).click(function(){
var $this = $(this);
if ($this.hasClass('lbPlay')) {
if ($this.data('ppHandler') != -1) clearInterval($this.data('ppHandler'));
$this.data('ppHandler', setInterval(function(){next();}, slideInterval*1000));
$this.removeClass('lbPlay').addClass('lbPause');
} else {
if ($this.data('ppHandler') != -1) clearInterval($this.data('ppHandler'));
$this.data({ppHandler: -1});
$this.removeClass('lbPause').addClass('lbPlay');
}
});
Thanks to all who responded. To see the final result go to "www.trips.elusien.co.uk" and click on the "slimbox examples" link and go to "example 9".

Categories

Resources