Jquery = setInterval code works in Firefox but not in Chrome - javascript

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.

Related

How do I set auto timer to content slideshow?

I am using the following as a content slider but unsure as to instead have the click the button to play the slides through but wanting to take that away and for the slides to go go through on load?
DEMO
$(document).ready(function() {
var height = 300,
width = 600,
tabs = 3,
$tabs = $('.tab'),
contentNum = 1,
delay = 2000, // time in milliseconds to pause between tabs
timer;
$('.play').click(function(){
var $t = $(this);
if ($t.hasClass('playing')) {
// stop
clearTimeout(timer);
$t.removeClass('playing').html('play');
} else {
// play
timer = setInterval(function(){
contentNum++; // change to contentNum--; to go left
if (contentNum > tabs){ contentNum = 1; } // loop right
if (contentNum < 1) { contentNum = tabs; } // loop left
$tabs.eq(contentNum-1).find('a').trigger('click');
}, delay);
$t.addClass('playing').html('stop');
}
});
$('.main_inner').css({
width: tabs * width
});
$('a.tab_link').click(function() {
$tabs.filter('.active').removeClass('active');
$(this).parent().addClass('active');
// make sure contentNum is a number and not a string
contentNum = parseInt( $(this).attr('rel'), 10 );
$('.main_inner').animate({
marginLeft: '-' + (width * contentNum - width)
}, 600);
return false;
});
});
No really sure what you mean.
If you wan't to autoplay on page load and keep the Play button use
$('.play').trigger('click');
If you wan't to autoplay without play/stop button replace
$('.play').click(function(){
// you current play/pause button code
});
simply by
// play
setInterval(function(){
contentNum++; // change to contentNum--; to go left
if (contentNum > tabs){ contentNum = 1; } // loop right
if (contentNum < 1) { contentNum = tabs; } // loop left
$tabs.eq(contentNum-1).find('a').trigger('click');
}, delay);

How do I add a class of "active" to a visible slide within a slideshow?

I have a page that is mainly a large slider, I am using unslider (unslider.com) right now, but would be willing to switch to a different slider if it provides the functionality I need. The goal is to have the slide show move through my different projects. At the bottom of the page, will be an arrow button that when clicked will slide in the specific project details for the current or visible slide. I was planning to load the project details into a hidden div via ajax based on the data attribute of the "active" slide.
The problem I am having is that I don't know how to add a class of "active" to the visible slide. I am fairly new, but I believe I will need to use javascript or jquery to change the "active" class appropriately. Here is the HTML and jQuery for the slider:
<div class="banner">
<ul>
<li class="slide_1" data-link="ajaxed_content_url">
<img src="slide_1.jpg" />
</li>
<li class="slide_2" data-link="ajaxed_content_url">
<img src="slide_2.jpg" />
</li>
<li class="slide_3" data-link="ajaxed_content_url">
<img src="slide_3.jpg" />
</li>
</ul>
<a class="details_btn" href="#"></a>
</div>
<script>
$('.banner').unslider({
speed: 500,
delay: 5000,
complete: function() {},
keys: true,
dots: true,
fluid: true
});
</script>
When the slider is rendered in the browser, an "active" class is not added to the code. Any help would be greatly appreciated.
Unslider simply animates the ul, so it does not actually change the picture li's. However, it DOES add an active class to the dots. Digging through the source code, I found where it adds the active class to the dots and edited in a similar line that should apply an active class to the pictures. Here is the edited source code:
/**
* Unslider by #idiot
*/
(function($, f) {
// If there's no jQuery, Unslider can't work, so kill the operation.
if(!$) return f;
var Unslider = function() {
// Set up our elements
this.el = f;
this.items = f;
// Dimensions
this.sizes = [];
this.max = [0,0];
// Current inded
this.current = 0;
// Start/stop timer
this.interval = f;
// Set some options
this.opts = {
speed: 500,
delay: 3000, // f for no autoplay
complete: f, // when a slide's finished
keys: !f, // keyboard shortcuts - disable if it breaks things
dots: f, // display ••••o• pagination
fluid: f // is it a percentage width?,
};
// Create a deep clone for methods where context changes
var _ = this;
this.init = function(el, opts) {
this.el = el;
this.ul = el.children('ul');
this.max = [el.outerWidth(), el.outerHeight()];
this.items = this.ul.children('li').each(this.calculate);
// Check whether we're passing any options in to Unslider
this.opts = $.extend(this.opts, opts);
// Set up the Unslider
this.setup();
return this;
};
// Get the width for an element
// Pass a jQuery element as the context with .call(), and the index as a parameter: Unslider.calculate.call($('li:first'), 0)
this.calculate = function(index) {
var me = $(this),
width = me.outerWidth(), height = me.outerHeight();
// Add it to the sizes list
_.sizes[index] = [width, height];
// Set the max values
if(width > _.max[0]) _.max[0] = width;
if(height > _.max[1]) _.max[1] = height;
};
// Work out what methods need calling
this.setup = function() {
// Set the main element
this.el.css({
overflow: 'hidden',
width: _.max[0],
height: this.items.first().outerHeight()
});
// Set the relative widths
this.ul.css({width: (this.items.length * 100) + '%', position: 'relative'});
this.items.css('width', (100 / this.items.length) + '%');
if(this.opts.delay !== f) {
this.start();
this.el.hover(this.stop, this.start);
}
// Custom keyboard support
this.opts.keys && $(document).keydown(this.keys);
// Dot pagination
this.opts.dots && this.dots();
// Little patch for fluid-width sliders. Screw those guys.
if(this.opts.fluid) {
var resize = function() {
_.el.css('width', Math.min(Math.round((_.el.outerWidth() / _.el.parent().outerWidth()) * 100), 100) + '%');
};
resize();
$(window).resize(resize);
}
if(this.opts.arrows) {
this.el.parent().append('<p class="arrows"><span class="prev">â†</span><span class="next">→</span></p>')
.find('.arrows span').click(function() {
$.isFunction(_[this.className]) && _[this.className]();
});
};
// Swipe support
if($.event.swipe) {
this.el.on('swipeleft', _.prev).on('swiperight', _.next);
}
};
// Move Unslider to a slide index
this.move = function(index, cb) {
// If it's out of bounds, go to the first slide
if(!this.items.eq(index).length) index = 0;
if(index < 0) index = (this.items.length - 1);
var target = this.items.eq(index);
var obj = {height: target.outerHeight()};
var speed = cb ? 5 : this.opts.speed;
if(!this.ul.is(':animated')) {
// Handle those pesky dots
_.el.find('.dot:eq(' + index + ')').addClass('active').siblings().removeClass('active');
//HERE IS WHAT I ADDED
_.el.find('ul li:eq(' + index + ')').addClass('active').siblings().removeClass('active');
this.el.animate(obj, speed) && this.ul.animate($.extend({left: '-' + index + '00%'}, obj), speed, function(data) {
_.current = index;
$.isFunction(_.opts.complete) && !cb && _.opts.complete(_.el);
});
}
};
// Autoplay functionality
this.start = function() {
_.interval = setInterval(function() {
_.move(_.current + 1);
}, _.opts.delay);
};
// Stop autoplay
this.stop = function() {
_.interval = clearInterval(_.interval);
return _;
};
// Keypresses
this.keys = function(e) {
var key = e.which;
var map = {
// Prev/next
37: _.prev,
39: _.next,
// Esc
27: _.stop
};
if($.isFunction(map[key])) {
map[key]();
}
};
// Arrow navigation
this.next = function() { return _.stop().move(_.current + 1) };
this.prev = function() { return _.stop().move(_.current - 1) };
this.dots = function() {
// Create the HTML
var html = '<ol class="dots">';
$.each(this.items, function(index) { html += '<li class="dot' + (index < 1 ? ' active' : '') + '">' + (index + 1) + '</li>'; });
html += '</ol>';
// Add it to the Unslider
this.el.addClass('has-dots').append(html).find('.dot').click(function() {
_.move($(this).index());
});
};
};
// Create a jQuery plugin
$.fn.unslider = function(o) {
var len = this.length;
// Enable multiple-slider support
return this.each(function(index) {
// Cache a copy of $(this), so it
var me = $(this);
var instance = (new Unslider).init(me, o);
// Invoke an Unslider instance
me.data('unslider' + (len > 1 ? '-' + (index + 1) : ''), instance);
});
};
})(window.jQuery, false);
I don't think you will be able to add an active class. However, The complete function is executed after every slide animation, try something like this:
<script>
var NB_OF_SLIDES = ...;
var i = 0;
loadAjaxForSlide i;
$('.banner').unslider({
speed: 500,
delay: 5000,
complete: function() {
i++;
if(i >= NB_OF_SLIDES){
i = 0;
}
loadAjaxForSlide i;
},
keys: true,
dots: true,
fluid: true
});
</script>
But I wouldn't consider loading data via Ajax after every slide animation, it's kinda messy. Try loading everything at once at page load.

jquery - Make number count up when in viewport [duplicate]

This question already has answers here:
Animate counter when in viewport
(4 answers)
Closed 6 years ago.
I'm trying to make a number count up when it's within the viewport, but currently, the script i'm using will interrupt the count on scroll.
How would I make it so that it will ignore the scroll and just count up when it's within the viewport? This needs to work on mobile, so even when a user is scrolling on touch. It cannot interrupt the count.
Please see here:
http://jsfiddle.net/Q37Q6/27/
(function ($) {
$.fn.visible = function (partial, hidden) {
var $t = $(this).eq(0),
t = $t.get(0),
$w = $(window),
viewTop = $w.scrollTop(),
viewBottom = viewTop + $w.height(),
_top = $t.offset().top,
_bottom = _top + $t.height(),
compareTop = partial === true ? _bottom : _top,
compareBottom = partial === true ? _top : _bottom,
clientSize = hidden === true ? t.offsetWidth * t.offsetHeight : true;
return !!clientSize && ((compareBottom <= viewBottom) && (compareTop >= viewTop));
};
})(jQuery);
// Scrolling Functions
$(window).scroll(function (event) {
function padNum(num) {
if (num < 10) {
return "" + num;
}
return num;
}
var first = 25; // Count up to 25x for first
var second = 4; // Count up to 4x for second
function countStuffUp(points, selector, duration) { //Animate count
$({
countNumber: $(selector).text()
}).animate({
countNumber: points
}, {
duration: duration,
easing: 'linear',
step: function () {
$(selector).text(padNum(parseInt(this.countNumber)));
},
complete: function () {
$(selector).text(points);
}
});
}
// Output to div
$(".first-count").each(function (i, el) {
var el = $(el);
if (el.visible(true)) {
countStuffUp(first, '.first-count', 1600);
}
});
// Output to div
$(".second-count").each(function (i, el) {
var el = $(el);
if (el.visible(true)) {
countStuffUp(second, '.second-count', 1000);
}
});
});
Your example is more complicated than you're aware, I think. You're doing things in a pretty unusual way, here, using a jQuery animate method on a custom property as your counter. It's kind of cool, but it also makes things a little more complicated. I've had to add a number of things to straighten up the situation.
I went ahead and rewrote your visible plugin, largely because I had no idea what yours was doing. This one's simple!
When your counters become visible, they get a "counting" class so that the counter isn't re-fired on them when they're already counting.
I save a reference to the object you have your custom counter animation on to the data attribute of the counter. This is vital: without that reference, you can't stop the animation when it goes offscreen.
I do some fanciness inside the step function to keep track of how much time is left so that you can keep your counter running at the same speed even if it stops and starts. If your counter runs for half a second and it's set to use one second for the whole animation, if it gets interrupted and restarted you only want to set it to half a second when you restart the counter.
http://jsfiddle.net/nate/p9wgx/1/
(function ($) {
$.fn.visible = function () {
var $element = $(this).eq(0),
$win = $(window),
elemTop = $element.position().top,
elemBottom = elemTop + $element.height(),
winTop = $win.scrollTop(),
winBottom = winTop + $win.height();
if (elemBottom < winTop) {
return false;
} else if (elemTop > winBottom) {
return false;
} else {
return true;
}
};
})(jQuery);
function padNum(num) {
if (num < 10) {
return " " + num;
}
return num;
}
var $count1 = $('.first-count');
var $count2 = $('.second-count');
// Scrolling Functions
$(window).scroll(function (event) {
var first = 25; // Count up to 25x for first
var second = 4; // Count up to 4x for second
function countStuffUp(points, selector, duration) {
//Animate count
var $selector = $(selector);
$selector.addClass('counting');
var $counter = $({
countNumber: $selector.text()
}).animate({
countNumber: points
}, {
duration: duration,
easing: 'linear',
step: function (now) {
$selector.data('remaining', (points - now) * (duration / points));
$selector.text(padNum(parseInt(this.countNumber)));
},
complete: function () {
$selector.removeClass('counting');
$selector.text(points);
}
});
$selector.data('counter', $counter);
}
// Output to div
$(".first-count").each(function (i, el) {
var el = $(el);
if (el.visible() && !el.hasClass('counting')) {
var duration = el.data('remaining') || 1600;
countStuffUp(first, '.first-count', duration);
} else if (!el.visible() && el.hasClass('counting')) {
el.data('counter').stop();
el.removeClass('counting');
}
});
// Output to div
$(".second-count").each(function (i, el) {
var el = $(el);
if (el.visible() && !el.hasClass('counting')) {
var duration = el.data('remaining') || 1000;
countStuffUp(second, '.second-count', duration);
} else if (!el.visible() && el.hasClass('counting')) {
el.data('counter').stop();
el.removeClass('counting');
}
});
});
There's a lot here. Feel free to ask me questions if anything's not clear.

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);
});

javascript 'over-clicking' bug

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.

Categories

Resources