Handling multiple setInterval calls - javascript

I am having trouble working out how to create a simple pop-and-drop effect for a class of divs. Since the onmouseover events of each of the items in this class are currently calling the same function, rapidly mousing between divs leaves the previous div stuck halfway through (if you mouse on and off from the bottom of the div, you'll see the effect I'm trying to produce).
My current approach also calls for me to replicate quite a bit of code to call the function from each individual div's event handlers, so if there's a way to wrap those up into one function I'd really like to know how. I'm sure there's a simple and efficient way to do all this, just can't seem to work it out.
jsFiddle example: http://jsfiddle.net/DvXDb/
Here's my code (script at the bottom):
div.bouncer{
position:relative;
top:50px;
}
function next(elem) {
do {
elem = elem.nextSibling;
} while (elem && elem.nodeType != 1);
return elem;
}
var thisDiv; var nextDiv; var selDiv;
var containerDivList = document.getElementsByClassName("container");
var containerArray = Array.prototype.slice.call(containerDivList);
var container1 = containerArray[0];
var container2 = containerArray[1];
var container3 = containerArray[2];
var container4 = containerArray[3];
container1.onmouseover = function () {
callPop(this.getElementsByTagName('div')[0]);
}
container1.onmouseout = function () {
callDrop(this.getElementsByTagName('div')[0]);
}
container2.onmouseover = function () {
callPop(this.getElementsByTagName('div')[0]);
}
container2.onmouseout = function () {
callDrop(this.getElementsByTagName('div')[0]);
}
container3.onmouseover = function () {
callPop(this.getElementsByTagName('div')[0]);
}
container3.onmouseout = function () {
callDrop(this.getElementsByTagName('div')[0]);
}
var relativeHeights = ['0px', '5px', '10px', '15px', '20px', '25px', '30px', '35px', '40px', '45px', '50px'];
var index = 10;
var intervalHandle1;
function callPop(thisDiv) {
clearInterval(intervalHandle1);
selDiv = thisDiv;
intervalHandle1 = setInterval(popImage, 5);
}
function callDrop(thisDiv) {
clearInterval(intervalHandle1);
selDiv = thisDiv;
intervalHandle1 = setInterval(dropImage, 5);
}
function popImage() {
selDiv.style.top = relativeHeights[index];
index--;
if (selDiv.style.top === '0px') {
index = 0;
}
}
function dropImage() {
index++;
selDiv.style.top = relativeHeights[index];
if (selDiv.style.top === '50px') {
index = 10;
}
}

Here's an example using jQuery, you really should be using a Framework for things like this. They simplify everything.
http://jsfiddle.net/AEJY9/
$(function () {
$('.bouncer').hover(
function () {
$(this).stop().animate({
top: 10
}, 500);
},
function () {
$(this).stop().animate({
top: 50
}, 500);
}
);
});

Related

Access a div out of the div the function refers to

I've got a problem with the last function. The goal is that when I click on the "suiv" div, the script start a loop with my div ".crea" that is out of the suiv div.
However, the loop this.div.find('.crea').each(function){} doesn't work.
$(document).ready(function(){
s = new slider(".galerie");
});
var slider = function (id) {
var self = this;
this.div = $(id);
this.nb = 0;
this.index = 0;
this.div.find(".crea").each(function () {
self.nb++;
//alert($(this).css("z-index"));
});
alert(this.nb);
this.index = 0;
this.suiv = this.div.find(".suiv");
this.prec = this.div.find(".prec");
this.suiv.click(function () {
this.div.find(".crea").each(function () {
//self.index=parseInt($(this).css("z-index"));
alert("wesh2");
}
});
}
Change find('crea') to parent('crea').children() and it will work.
find only goes downwards.
go up to the parent, then use children
this.suiv.click(function () {
this.div.parent.children(".crea").each(function () {
//self.index=parseInt($(this).css("z-index"));
alert("wesh2");
}
});
}

$.each animation running separately

I'm trying to run each animation function one after the other instead of all at once.
This is what I've got so far:
$(document).ready(function(){
var bars = $('.bar');
bars.each(function(){
var widthpercent = $(this).attr("data-percent");
$(this).fadeIn();
$(this).animate({width:widthpercent},500);
});
});
I've tried using .delay() and setTimeout() in various combinations to no avail.
Could anyone point me in the right direction? Thank you!
It sounds to me like you're looking for animate's complete function. You can write a recursive function to keep calling the function in the complete function until all the items have been animated. To simplify: every time one element is animated, a callback is fired that animates the next element. That is the purpose of the complete parameter, so I'm certain that is what you're looking for.
Here's an example you can adapt to your specific needs.
Live demo here (click).
var $divs = $('div');
function animate(element) {
$(element).animate({height: '30px'}, {
complete: function() {
if (current < $divs.length-1) {
++current;
animate($divs[current]);
}
}
});
}
var current = 0;
animate($divs[current]);
Further, this same logic can be applied to your fadeIn. Just wrap fadeIn's callback around that logic, like this:
Live demo here (click).
var $divs = $('div');
function animate(element) {
$(element).fadeIn(function() { //now the animation is a callback to the fadeIn
$(element).animate({height: '70px'}, {
complete: function() {
if (current < $divs.length-1) {
++current;
animate($divs[current]);
}
}
});
});
}
var current = 0;
animate($divs[current]);
And here's your code: live demo here (click).
$(document).ready(function(){
var $divs = $('.bar');
function animate(element) {
$(element).fadeIn(function() { //you could unwrap this depending on what you're looking for
var widthpercent = $(element).attr("data-percent");
$(element).animate({
width:widthpercent,
duration: '500ms'
}, {
complete: function() {
if (current < $divs.length-1) {
++current;
animate($divs[current]);
}
}
});
}); //end fadeIn callback
}
var current = 0;
animate($divs[current]);
});
Try this:
var animate = function (el) {
return function () {
var widthpercent = el.data('percent');
el.fadeIn();
el.animate({
width: widthpercent
}, 500);
}
}
var bars = $('.bar');
bars.each(function (index) {
var $this = $(this);
setTimeout(animate($this), index * 500);
});
Fiddle
$(document).ready(function(){
var bars = $('.bar');
bars.each(function(i){
var widthpercent = $(this).attr("data-percent");
$(this).delay(i*800).animate({width:widthpercent,opacity:1,},500);
});
});
This will animate after delaying 800 * i milliseconds.
See this JSFiddle example.

Execute function IF another function is complete NOT when

I am having trouble creating a slider that pauses on hover, because I execute the animation function again on mouse off, if I flick the mouse over it rapidly (thereby calling the function multiple times) it starts to play up, I would like it so that the function is only called if the other function is complete, otherwise it does not call at all (to avoid queue build up and messy animations)
What's the easiest/best way to do this?
$(document).ready(function() {
//get variables
var slide_width = $('.slider_container').width();
var number_of_slides = $('.slider_container .slide').length;
var slider_width = slide_width*number_of_slides;
//set element dimensions
$('.slide').width(slide_width);
$('.slider').width(slider_width);
var n = 1;
$('.slider_container').hover(function() {
//Mouse on
n = 0;
$('.slider').stop(true, false);
}, function() {
//Mouse off
n = 1;
if (fnct == 0) sliderLoop();
});
//Called in Slide Loop
function animateSlider() {
$('.slider').delay(3000).animate({ marginLeft: -(slide_width * i) }, function() {
i++;
sliderLoop();
});
}
var i = 0;
var fnct = 0
//Called in Doc Load
function sliderLoop() {
fnct = 1
if(n == 1) {
if (i < number_of_slides) {
animateSlider();
}
else
{
i = 0;
sliderLoop();
}
}
fnct = 0
}
sliderLoop();
});
The slider works fine normally, but if I quickly move my mouse on and off it, then the slider starts jolting back and forth rapidly...been trying to come up with a solution for this for hours now..
Here's what fixed it, works a charm!
$(document).ready(function() {
//get variables
var slide_width = $('.slider_container').width();
var number_of_slides = $('.slider_container .slide').length;
var slider_width = slide_width*number_of_slides;
//set element dimensions
$('.slide').width(slide_width);
$('.slider').width(slider_width);
var n = 1;
var t = 0;
$('.slider_container').hover(function() {
clearInterval(t);
}, function() {
t = setInterval(sliderLoop,3000);
});
var marginSize = i = 1;
var fnctcmp = 0;
//Called in Doc Load
function sliderLoop() {
if (i < number_of_slides) {
marginSize = -(slide_width * i++);
}
else
{
marginSize = i = 1;
}
$('.slider').animate({ marginLeft: marginSize });
}
t = setInterval(sliderLoop,3000);
});

get interval ID from else statement when set in IF

I am attempting to create a responsive slider, that will change to a simple set of dot points when in mobile mode (< 940).
The issue I am facing is in my else statement I am unable to clearintervals that were made in the if statement, because t comes up as undefined. I have resorted to using
for (var i = 1; i < 99999; i++) window.clearInterval(i); to clear the interval which works, but I don't like it because it's ugly and cumbersome, is there another way of accomplishing this?
$(document).ready(function() {
function rePosition() {
//get responsive width
var container_width = $('.container').width();
//Slider for desktops only
if(container_width >= 940) {
//get variables
var slide_width = $('.slider_container').width();
var number_of_slides = $('.slider_container .slide').length;
var slider_width = slide_width*number_of_slides;
//set element dimensions
$('.slide').width(slide_width);
$('.slider').width(slider_width);
var n = 1;
var t = 0;
$('.slider_container').hover(function() {
clearInterval(t);
}, function() {
t = setInterval(sliderLoop,6000);
});
var marginSize = i = 1;
//Called in Doc Load
function sliderLoop(trans_speed) {
if (trans_speed) {
var trans_speed = trans_speed;
}
else
{
var trans_speed = 3000;
}
if (i < number_of_slides) {
marginSize = -(slide_width * i++);
}
else
{
marginSize = i = 1;
}
$('.slider').animate({ marginLeft: marginSize }, trans_speed);
}
t = setInterval(sliderLoop,6000);
$('.items li').hover(function() {
$('.slider').stop();
clearInterval(t);
var item_numb = $(this).index();
i = item_numb;
sliderLoop(500);
}, function() {
t = setInterval(sliderLoop,6000);
});
}
else
{
for (var i = 1; i < 99999; i++)
window.clearInterval(i);
$('.slider').stop(true, true);
$('.slider').css('margin-left', '0px');
//rearrange content
if($('.slider .slide .slide_title').length < 1) {
$('.items ul li').each(function() {
var item_numb = $(this).index();
var content = $(this).text();
$('.slider .slide:eq(' + item_numb + ')').prepend('<div class="title slide_title">' + content + '</div>')
});
}
}
}
rePosition();
$(window).resize(function() {
rePosition();
});
});
Teemu's comment is correct. I'll expand on it. Make an array available to all of the relevant code (just remember that globals are bad).
$(document).ready(function() {
var myIntervalArray = [];
Now, whenever you create an interval you will need to reference later, do this:
var t = setInterval();//etc
myIntervalArray.push(t); //or just put the interval directly in.
Then to clear them, just loop the array and clear each interval.
for (var i=0; i<myIntervalArray.length; i++)
clearInterval(myIntervalArray[i]);
}
Umm, wouldn't t only be defined when the if part ran... as far as I can tell, this is going to run and be done... the scope will be destroyed. If you need to maintain the scope across calls, you'll need to move your var statements outside of reposition(), like so:
$(document).ready(function() {
var t = 0;
...
function rePosition() { ... }
});

Onload method is calling itself more than once on successive reloads to application (var image = new Image(), javascript issue)

I don't understand why onload method(see the code below) is calling itself more than once in IE8 and IE9 browsers on consecutive reloads.
I guess, onload method is fired more than once that is causing this image to render(appear) more than once in IE8,IE9. Below js code is written by my senior Front-Ender (he is using his own written js library for all .append(), anim(), ... methods). The code is working as expected in Firefox, Chrome but the image(html code) is rendering more than once while reloading with a IE8 or IE9 browser.
Some Quick Info:
waitingFor() - Periodically scans dom for matching elements,
mdiTog - The image that is appearing more than once..
How it works:
The div attribute Class="suggestions contextSuggesions" is inserted in the body element and contains an unordered list (ul, li, ul) that toggles between opacity 0 and 1 if you click on the image (mdiTog).
Below is the image of Mditog appearing more than once and it's appearance is random:
waitingFor('.viewport .homePage', function () {
var mdiTog = new Image();
mdiTog.onload = function () {
var $mdi = M$('.mdiTabs ul'),
$contextMenu = M$($body.append('<div class="suggestions contextSuggesions" style="display:none"></div>')),
$toggle = M$($mdi.append('<li class="mdiTog" style="display:none"></li>')),
visible = false,
active = false;
var hideCtx = function () {
if (visible) {
$contextMenu.anim('', {opacity: 100}, {opacity: 0}, 250, function () {
$contextMenu.css({display: 'none'});
});
visible = false;
}
};
$toggle.css({opacity: 0, display: 'inline-block'}).anim('', {opacity: 0}, {opacity: 100}, 1000).on('click', function () {
var togOS = $toggle.offset(),
menu = '';
M$('li', $mdi.get()).each(function (el, idx) {
if (idx > 1) {
menu += '<li rel="' + idx + '">' + $mdi.text(null, el) + '</li>';
}
});
$contextMenu.css({opacity: 0, display: 'block', top: togOS.top + 'px', left: (togOS.left - 20) + 'px'}).anim('', {opacity: 0}, {opacity: 100}, 400, function () {
visible = true;
}).html('<ul>' + menu + '<li class="uiUtil hr" onclick="ApolloUI.widgets.expose.show()">' + translate('previewAll') + '</li><li class="uiUtil" onclick="ApolloUI.mdi.closeAll()">' + translate('closeAll') + '</li></ul>');
});
$contextMenu.on('click', function (e) {
var target = $contextMenu.eventTarget(e);
if (target.tagName === 'LI' && target.className.indexOf('uiUtil') === -1) {
$contextMenu.fire('click', M$('.mdiTabs li').get(parseInt(target.getAttribute('rel'), 10)));
}
hideCtx();
}).on('mouseleave', function () {
active = false;
}).on('mouseenter', function () {
active = true;
});
setInterval(function () {
if (!active && visible) {
hideCtx();
}
}, 1000);
};
mdiTog.src = 'media/img/ico_16_context.png';
});
I do have to close this issue as soon as possible, so any suggestions/modifications/addition in this code to force onload to execute only once on even on several consecutive reloads in IE8/IE9 browsers are welcome.
If the onload fires twice, you should use a guard against executing the handler more than once:
var mdiTog = new Image();
var hasFired = false;
mdiTog.onload = function () {
if (hasFired)
return;
hasFired = true;
…
};
Or, if you're using a jQuery-like library, you could try the one event binding method:
var mdiTog = new Image();
M$(mdiTog).one("load", function () {
…
});

Categories

Resources