I have a bug in Javascript where I am animating the margin left property of a parent container to show its child divs in a sort of next/previous fashion. Problem is if clicking 'next' at a high frequency the if statement seems to be ignored (i.e. only works if click, wait for animation, then click again) :
if (marLeft === (-combinedWidth + (regWidth) + "px")) {
//roll margin back to 0
}
An example can be seen on jsFiddle - http://jsfiddle.net/ZQg5V/
Any help would be appreciated.
Try the below code which will basically check if the container is being animated just return from the function.
Working demo
$next.click(function (e) {
e.preventDefault();
if($contain.is(":animated")){
return;
}
var marLeft = $contain.css('margin-left'),
$this = $(this);
if (marLeft === (-combinedWidth + (regWidth) + "px")) {
$contain.animate({
marginLeft: 0
}, function () {
$back.fadeOut('fast');
});
} else {
$back.fadeIn(function () {
$contain.animate({
marginLeft: "-=" + regWidth + "px"
});
});
}
if (marLeft > -combinedWidth) {
$contain.animate({
marginLeft: 0
});
}
});
Sometimes is better if you create a function to take care of the animation, instead of writting animation code on every event handler (next, back). Also, users won't have to wait for the animation to finish in order to go the nth page/box.
Maybe this will help you:
if (jQuery) {
var $next = $(".next"),
$back = $(".back"),
$box = $(".box"),
regWidth = $box.width(),
$contain = $(".wrap")
len = $box.length;
var combinedWidth = regWidth*len;
$contain.width(combinedWidth);
var currentBox = 0; // Keeps track of current box
var goTo = function(n) {
$contain.animate({
marginLeft: -n*regWidth
}, {
queue: false, // We don't want animations to queue
duration: 600
});
if (n == 0) $back.fadeOut('fast');
else $back.fadeIn('fast');
currentBox = n;
};
$next.click(function(e) {
e.preventDefault();
var go = currentBox + 1;
if (go >= len) go = 0; // Index based, instead of margin based...
goTo(go);
});
$back.click(function(e) {
e.preventDefault();
var go = currentBox - 1;
if (go <= 0) go = 0; //In case back is pressed while fading...
goTo(go);
});
}
Here's an updated version of your jsFiddle: http://jsfiddle.net/victmo/ZQg5V/5/
Cheers!
Use a variable to track if the animation is taking place. Pseudocode:
var animating = false;
function myAnimation() {
if (animating) return;
animating = true;
$(this).animate({what:'ever'}, function() {
animating = false;
});
}
Crude, but it should give you the idea.
Edit: Your current code works fine for me as well, even if I jam out on the button. On firefox.
Related
I do not have much experience in animation on Jquery. I want to make a simple animation that will highlight my text line by line with the possibility of stopping. I know how to do something like this for one line but I have no idea how to deal with loop.
here is my code:
var lines = $('#page')[0].getClientRects();
for (var i=0, max = lines.length; i < max; i++)
{
$('#under_liner')
.queue(function() {
$(this).css('top', lines[i].bottom).dequeue();
})
.animate({
width: lines[i].right - lines[i].left
}, 1000 )
.queue(function() {
$(this).css('width', 0).dequeue();
});
}
and jsfiddle http://jsfiddle.net/mz03kfua/2
I don't know if this is exactly what you are looking for, but here's how I'd do it.
Make a function that does the underlining
Make a recursive call on animation callback
Create a global variable to keep count of the current underlined line
Add a boolean that stops the function when false
var lines = $('#page')[0].getClientRects();
var play = true;
var index = 0;
underlineLine();
$('button').click(function(){
play = !play
if(play){
underlineLine()
$(this).html("STOP")
}else{
$(this).html("CONTINUE")
}
})
function underlineLine(){
if(index >= lines.length) return
if(play){
$('#under_liner').css('top', lines[index].bottom).dequeue();
$('#under_liner').css('width','0px');
$('#under_liner').animate({
width: lines[index].right - lines[index].left
}, 1000, function(){
underlineLine(index++)
})
$('#under_liner').css('width', 0).dequeue();
}
}
HERE IS A FIDDLE WITH THE CODE.
Hope it helps.
http://jsfiddle.net/mz03kfua/4/
var lines = $('#page')[0].getClientRects();
var current = 0;
var element;
function animateLine() {
if(typeof lines[current] !== "object") {
return;
}
var line = lines[current];
element = jQuery("<div />", {"class": "under_liner"}).prependTo("#page");
element.css({top: line.bottom}).animate({width: line.width}, 1000, function() {
current++;
animateLine();
});
}
function stopLine(e) {
e.preventDefault();
element.stop(true);
}
jQuery(".stop").click(stopLine);
animateLine();
I have this javascript that seems to be forcing IE9 into computability mode and forcing it to IE7. There are other areas of the website working properly that do not have this javascript code working on them, which leads me to believe something in this script is not compatible with IE9/IE7.
Basically, the code creates a pop up box when your mouse hovers over it. However if the pop up box displays over an image, the image shows through the pop up box as if it has precedence. I have tried changing the z-index on that div but no luck.
Any suggestions?
jQuery('.bubbleInfo').each(function () {
if(jQuery.trim(jQuery(this).find('#dpop').html()) != ''){ // start
var totalHeight = jQuery(this).height();
var distance = 15;
var time = 250;
var hideDelay = 150;
var hideDelayTimer = null;
var beingShown = false;
var shown = false;
var trigger = jQuery('.trigger', this);
var info = jQuery('.popup', this).css('opacity', 0);
jQuery([trigger.get(0), info.get(0)]).mouseover(function () {
if (hideDelayTimer) clearTimeout(hideDelayTimer);
if (beingShown || shown) {
// don't trigger the animation again
return;
} else {
// reset position of info box
beingShown = true;
info.css({
top: (totalHeight+38),
left: -77,
display: 'block'd
}).animate({
top: '-=' + distance + 'px',
opacity: 1
}, time, 'swing', function() {
beingShown = false;
shown = true;
});
}
return false;
}).mouseout(function () {
if (hideDelayTimer) clearTimeout(hideDelayTimer);
hideDelayTimer = setTimeout(function () {
hideDelayTimer = null;
info.animate({
top: '-=' + distance + 'px',
opacity: 0
}, time, 'swing', function () {
shown = false;
info.css('display', 'none');
});
}, hideDelay);
return false;
});
} // end
IE < 10 have issue in the jquery library version although IE10 supports all the version's of jquery.
I have this javascript function I use that when clicked goes a certain distance. This is used within a scroller going left to right that uses about 7 divs. My question is how do I get the click to go the full distance first before the click can be used again? The issue is if the user rapidly clicks on the arrow button it resets the distance and sometimes can end up in the middle of an image instead of right at the seam. What code am I missing to accomplish this?
$(function () {
$("#right, #left").click(function () {
var dir = this.id == "right" ? '+=' : '-=';
$(".outerwrapper").stop().animate({ scrollLeft: dir + '251' }, 1000);
});
});
I would've thought that the easiest way would be to have a boolean flag indicating whether or not the animation is taking place:
$(function () {
var animating = false,
outerwrap = $(".outerwrapper");
$("#right, #left").click(function () {
if (animating) {return;}
var dir = (this.id === "right") ? '+=' : '-=';
animating = true;
outerwrap.animate({
scrollLeft: dir + '251'
}, 1000, function () {
animating = false;
});
});
});
works for me: http://jsfiddle.net/BYossarian/vDtwy/4/
Use .off() to unbind the click as soon as it occurs, then re-bind it once the animation completes.
function go(elem){
$(elem).off('click'); console.log(elem);
var dir = elem.id == "right" ? '+=' : '-=';
$(".outerwrapper").stop().animate({ left: dir + '251' }, 3000, function(){
$("#right, #left").click(go);
});
}
$("#right, #left").click(function () {
go(this);
});
jsFiddle example
You can see in this simplified example that the click event is unbound immediately after clicking, and then rebound once the animation completes.
Use an automatic then call like this
var isMoving = false;
$(function () {
$("#right, #left").click(function () {
if (isMoving) return;
isMoving = true;
var dir = this.id == "right" ? '+=' : '-=';
$(".outerwrapper").stop().animate({ scrollLeft: dir + '251' }, 1000).then(function(){isMoving = false}());
});
});
I think that you miss the fact that when you make stop() you actually position the slider at some specific point. I.e. if your scroller is 1000px and you click left twice very quickly you will probably get
scrollLeft: 0 - 251
scrollLeft: -2 - 251
So, I think that you should use an index and not exactly these += and -= calculations. For example:
$(function () {
var numberOfDivs = 7;
var divWidth = 251;
var currentIndex = 0;
$("#right, #left").click(function () {
currentIndex = this.id == "right" ? currentIndex+1 : currentIndex-1;
currentIndex = currentIndex < 0 ? 0 : currentIndex;
currentIndex = currentIndex > numberOfDivs ? numberOfDivs : currentIndex;
$(".outerwrapper").stop().animate({ scrollLeft: (currentIndex * divWidth) + "px" }, 1000);
});
});
A big benefit of this approach is that you are not disabling the clicking. You may click as many times as you want and you can do that quickly. The script will still works.
This will work perfectly fine:
var userDisplaysPageCounter = 1;
$('#inventory_userdisplays_forward_button').bind('click.rightarrowiventory', function(event) {
_goForwardInInventory();
});
$('#inventory_userdisplays_back_button').bind('click.leftarrowiventory', function(event) {
_goBackInInventory();
});
function _goForwardInInventory()
{
//$('#inventory_userdisplays_forward_button').unbind('click.rightarrowiventory');
var totalPages = $('#userfooterdisplays_list_pagination_container div').length;
totalPages = Math.ceil(totalPages/4);
// alert(totalPages);
if(userDisplaysPageCounter < totalPages)
{
userDisplaysPageCounter++;
$( "#userfooterdisplays_list_pagination_container" ).animate({
left: "-=600",
}, 500, function() {
});
}
}
function _goBackInInventory()
{
//$('#inventory_userdisplays_back_button').unbind('click.leftarrowiventory');
if(userDisplaysPageCounter > 1)
{
userDisplaysPageCounter--;
$( "#userfooterdisplays_list_pagination_container" ).animate({
left: "+=600",
}, 500, function() {
});
}
}
I second BYossarian's answer.
Here is a variation on his demo, which "skips" the animation when the user clicks several times quickly on the buttons :
$(function () {
var targetScroll = 0,
outerwrap = $(".outerwrapper");
$("#right, #left").click(function () {
// stop the animation,
outerwrap.stop();
// hard set scrollLeft to its target position
outerwrap.scrollLeft(targetScroll*251);
if (this.id === "right"){
if (targetScroll < 6) targetScroll += 1;
dir = '+=251';
} else {
if (targetScroll > 0) targetScroll -=1;
dir = '-=251';
}
outerwrap.animate({ scrollLeft: dir }, 1000);
});
});
fiddle
The code (from an old plugin that I am trying to make responsive) slides a set of images across every n seconds. It uses setInterval code as below, and works well on Firefox. On Chrome it runs once only, and debugging indicates that the second setInteral function is just not called. Please help as its diving me mad. Running example at http://lelal.com/test/site10/index.html (sorry about the load time)
play = setInterval(function() {
if (!busy) {
busy = true;
updateCurrent(settings.direction);
slide();
}
}, settings.speed);
The complete plugin code is below (sorry its long)
/*
* jQuery Queue Slider v1.0
* http://danielkorte.com
*
* Free to use and abuse under the MIT license.
* http://www.opensource.org/licenses/mit-license.php
*/
(function($){
var QueueSlider = function(element, options) {
var play = false,
busy = false,
current = 2,
previous = 2,
widths = [],
slider = $(element),
queue = $('ul.queue', slider),
numImages = $('img', queue).size(),
viewportWidth = slider.width(),
settings = $.extend({}, $.fn.queueSlider.defaults, options);
$(window).resize(function(){
if(busy !== false)
clearTimeout(busy);
busy = setTimeout(resizewindow, 200); //200 is time in miliseconds
});
function resizewindow() {
viewportWidth = slider.width();
if (settings.scale > 0) {
slider.css('height',viewportWidth * settings.scale);
computeQueueWidth();
}
queue.css('left', -getQueuePosition());
busy = false;
}
function requeue() {
$('li', queue).each(function(key, value) {
$(this).attr('class', 'slide-' + (key+1));
});
}
function updateCurrent(dir) {
current += dir;
if (current < 1) {
current = numImages;
} else if (current > numImages) {
current = 1;
}
}
function getQueuePosition() {
var i = 0, index = current-1,
queuePosition = (viewportWidth - widths[index]) / -2;
for (i = 0; i < index; i++) { queuePosition += widths[i]; }
return queuePosition;
}
function computeQueueWidth() {
var queueWidth = 0;
// factor = slider.height() / settings.imageheight;
// settings.imageheight = settings.imageheight * factor;
// Get the image widths and set the queue width to their combined value.
$('li', queue).each(function(key, value) {
var slideimg = $("img", this),
slide = $(this),
// width = slide.width() * factor,
width = slideimg.width();
slide.css('width', width+'px');
queueWidth += widths[key] = width;
});
queue.css('width', queueWidth + 500);
}
function slide() {
var animationSettings = {
duration: settings.transitionSpeed,
queue: false
};
// Emulate an infinte loop:
// Bring the first image to the end.
if (current === numImages) {
var firstImage = $('li.slide-1', queue);
widths.push(widths.shift());
queue.css('left', queue.position().left + firstImage.width()).append(firstImage);
requeue();
current--; previous--;
}
// Bring the last image to the beginning.
else if (current === 1) {
var lastImage = $('li:last-child', queue);
widths.unshift(widths.pop());
queue.css('left', queue.position().left + -lastImage.width()).prepend(lastImage);
requeue();
current = 2; previous = 3;
}
// Fade in the current and out the previous images.
if (settings.fade !== -1) {
$('li.slide-'+current, queue).animate({opacity: 1}, animationSettings);
$('li.slide-'+previous, queue).animate({opacity: settings.fade}, animationSettings);
}
// Animate the queue.
animationSettings.complete = function() { busy = false; };
queue.animate({ left: -getQueuePosition() }, animationSettings);
previous = current;
}
//
// Setup the QueueSlider!
//
if (numImages > 2) {
// Move the last slide to the beginning of the queue so there is an image
// on both sides of the current image.
if (settings.scale > 0) {
slider.css('height',viewportWidth * settings.scale);
}
computeQueueWidth();
widths.unshift(widths.pop());
queue.css('left', -getQueuePosition()).prepend($('li:last-child', queue));
requeue();
// Fade out the images we aren't viewing.
if (settings.fade !== -1) { $('li', queue).not('.slide-2').css('opacity', settings.fade); }
// Include the buttons if enabled and assign a click event to them.
if (settings.buttons) {
slider.append('<button class="previous" rel="-1">' + settings.previous + '</button><button class="next" rel="1">' + settings.next + '</button>');
$('button', slider).click(function() {
if (!busy) {
busy = true;
updateCurrent(parseInt($(this).attr('rel'), 10));
clearInterval(play);
slide();
}
return false;
});
}
// Start the slideshow if it is enabled.
if (settings.speed !== 0) {
play = setInterval(function() {
if (!busy) {
busy = true;
updateCurrent(settings.direction);
slide();
}
}, settings.speed);
}
}
else {
// There isn't enough images for the QueueSlider!
// Let's disable the required CSS and show all one or two images ;)
slider.removeClass('queueslider');
}
};
$.fn.queueSlider = function(options) {
return this.each(function(key, value) {
var element = $(this);
// Return early if this element already has a plugin instance.
if (element.data('queueslider')) { return element.data('queueslider'); }
// Pass options to plugin constructor.
var queueslider = new QueueSlider(this, options);
// Store plugin object in this element's data.
element.data('queueslider', queueslider);
});
};
$.fn.queueSlider.defaults = {
scale: 0,
imageheight: 500,
fade: 0.3, // Opacity of images not being viewed, use -1 to disable
transitionSpeed: 700, // in milliseconds, speed for fade and slide motion
speed: 7000, // in milliseconds, use 0 to disable slideshow
direction: 1, // 1 for images to slide to the left, -1 to silde to the right during slideshow
buttons: true, // Display Previous/Next buttons
previous: 'Previous', // Previous button text
next: 'Next' // Next button text
};
}(jQuery));
Have a look here:
http://www.w3schools.com/jsref/met_win_setinterval.asp
The setInterval() method will continue calling the function until clearInterval() is called, or the window is closed.
Looks like you're calling clearInterval after the first usage of play, which makes it stop working.
I have built a jQuery rotator to rotate through 3 divs and loop them. I would like to add the functionality on mouse over to "freeze" the current div and then start again on mouse out.
I've thought about setting a variable to false at the start of the function and setting it true when it's on it's current frame but I've got my self a bit confused.
I've also tried to use the hover function but when using the in and out handlers, I'm confused as to how to stop, restart the animation.
function ImageRotate() {
var CurrentFeature = "#container" + featureNumber;
$(CurrentFeature).stop(false, true).delay(4500).animate({'top' : '330px'}, 3000);
var featureNumber2 = featureNumber+1;
if ( featureNumber == numberOfFeatures) {featureNumber2 = 1}
var NewFeature = "#container" + featureNumber2;
$(NewFeature).stop(false, true).delay(4500).animate({'top' : '0px'}, 3000);
var featureNumber3 = featureNumber-1;
if ( featureNumber == 1) {featureNumber3 = numberOfFeatures};
var OldFeature = "#container" + featureNumber3;
$(OldFeature).stop(false, true).delay(4500).css('top' , '-330px');
setTimeout('if (featureNumber == numberOfFeatures){featureNumber = 1} else {featureNumber++}; ImageRotate2()', 7500)};
Any help would be greatly appreciated!!
Thanks, Matt
If you were to add this code:
var timerId = null;
function startRotation() {
if (timerId) {
return;
}
timerId = setInterval('if (featureNumber == numberOfFeatures){featureNumber = 1} else {featureNumber++}; ImageRotate2()', 7500);
}
function stopRotation() {
if (!timerId) {
return;
}
clearInterval(timerId);
timerId = null;
}
and replace the last line of your code block with a simple call to startRotation();, then you could call stopRotation and startRotation when the mouse hovers over/leaves your element:
$('your-element-selector').hover(stopRotation, startRotation);
It's not clear what you are trying to do with the three divs without seeing the HTML and more code, so I think a basic example might help you better (demo).
HTML
<div class="test">image: <span></span></div>
Script
$(document).ready(function(){
var indx = 0, loop, numberOfFeatures = 5;
function imageRotate(){
indx++;
if (indx > numberOfFeatures) { indx = 1; }
$('.test span').text(indx);
loop = setTimeout( imageRotate , 1000 );
}
imageRotate();
$('.test').hover(function(){
clearTimeout(loop);
}, function(){
imageRotate();
});
})
changed things up a little bit, here is how I ended up doing it. `
var animRun = false;
var rotateHover = false;
function startRotation() {
rotateHover = false;
ImageRotate();
}
function stopRotation() {
rotateHover = true;
clearTimeout();
}
function ImageRotate() {
if (rotateHover == false){
animRun = true;
var CurrentFeature = "#container" + featureNumber;
$(CurrentFeature).stop(false, true).animate({'top' : '330px'}, featureDuration, function(){animRun = false;});
var featureNumber2 = featureNumber+1;
if ( featureNumber == numberOfFeatures) {featureNumber2 = 1}
var NewFeature = "#container" + featureNumber2;
$(NewFeature).stop(false, true).animate({'top' : '0px'}, featureDuration); /* rotate slide 2 into main frame */
var featureNumber3 = featureNumber-1;
if ( featureNumber == 1) {featureNumber3 = numberOfFeatures};
var OldFeature = "#container" + featureNumber3;
$(OldFeature).stop(false, true).css('top' , '-330px'); /*bring slide 3 to the top*/
//startRotation();
setTimeout('if (featureNumber == numberOfFeatures){featureNumber = 1} else {featureNumber++}; if (rotateHover == false){ImageRotate2()};', featureDelay);
};
};