I can't figure out how to do it.
I have two separate scripts. The first one generates an interval (or a timeout) to run a specified function every x seconds, i.e. reload the page.
The other script contains actions for a button to control (pause/play) this interval.
The pitfall here is that both sides must be asyncronous (both run when the document is loaded).
How could I properly use the interval within the second script?
Here's the jsFiddle: http://jsfiddle.net/hm2d6d6L/4/
And here's the code for a quick view:
var interval;
// main script
(function($){
$(function(){
var reload = function() {
console.log('reloading...');
};
// Create interval here to run reload() every time
});
})(jQuery);
// Another script, available for specific users
(function($){
$(function(){
var $playerButton = $('body').find('button.player'),
$icon = $playerButton.children('i');
buttonAction = function(e){
e.preventDefault();
if ($(this).hasClass('playing')) {
// Pause/clear interval here
$(this).removeClass('playing').addClass('paused');
$icon.removeClass('glyphicon-pause').addClass('glyphicon-play');
}
else {
// Play/recreate interval here
$(this).removeClass('paused').addClass('playing');
$icon.removeClass('glyphicon-play').addClass('glyphicon-pause');
}
},
buttonInit = function() {
$playerButton.on('click', buttonAction);
};
buttonInit();
});
})(jQuery);
You could just create a simple event bus. This is pretty easy to create with jQuery, since you already have it in there:
// somewhere globally accessible (script 1 works fine)
var bus = $({});
// script 1
setInterval(function() {
reload();
bus.trigger('reload');
}, 1000);
// script 2
bus.on('reload', function() {
// there was a reload in script 1, yay!
});
I've found a solution. I'm sure it's not the best one, but it works.
As you pointed out, I eventually needed at least one global variable to act as a join between both scripts, and the use of a closure to overcome asyncronous issues. Note that I manipulate the button within reload, just to remark that sometimes it's not as easy as moving code outside in the global namespace:
Check it out here in jsFiddle: yay! this works!
And here's the code:
var intervalControl;
// main script
(function($){
$(function(){
var $playerButton = $('body').find('button.player'),
reload = function() {
console.log('reloading...');
$playerButton.css('top', parseInt($playerButton.css('top')) + 1);
};
var interval = function(callback) {
var timer,
playing = false;
this.play = function() {
if (! playing) {
timer = setInterval(callback, 2000);
playing = true;
}
};
this.pause = function() {
if (playing) {
clearInterval(timer);
playing = false;
}
};
this.play();
return this;
};
intervalControl = function() {
var timer = interval(reload);
return {
play: function() {
timer.play();
},
pause: function(){
timer.pause();
}
}
}
});
})(jQuery);
// Another script, available for specific users
(function($){
$(function(){
var $playerButton = $('body').find('button.player'),
$icon = $playerButton.children('i'),
interval;
buttonAction = function(e){
e.preventDefault();
if ($(this).hasClass('playing')) {
interval.pause();
$(this).removeClass('playing').addClass('paused');
$icon.removeClass('glyphicon-pause').addClass('glyphicon-play');
}
else {
interval.play();
$(this).removeClass('paused').addClass('playing');
$icon.removeClass('glyphicon-play').addClass('glyphicon-pause');
}
},
buttonInit = function() {
$playerButton.on('click', buttonAction);
interval = intervalControl();
};
buttonInit();
});
})(jQuery);
Any better suggestion is most welcome.
Related
I'm trying this below code:
$(document).ready(function() {
$("button").hover(
function() {
$(this).addClass("active");
},
function() {
$(this).removeClass("active");
}
);
});
Try this: http://plnkr.co/edit/Xlco44QPWvKEh1jb0gDf?p=preview
var button = $('button');
button.hover(
function() {
button.addClass('active');
setTimeout(function() {
button.removeClass('active');
}, 3000);
},
function() {
button.removeClass('active');
}
);
From what you said in your previous comment below, you tried setTimeout and it didn't work because of the the way you used this. The value of this inside the timeout function wasn't the same as in your outer function, so jQuery didn't match your button element.
Better to define the button once as a variable, and reuse the variable, that use repeated jQuery selectors.
UPDATE: Here's a slightly more sophisticated version that keeps the setTimeout timers from piling up:
$(function() {
var button = $('button');
var timeout = 0;
button.hover(
function() {
button.addClass('active');
timeout = setTimeout(function() {
button.removeClass('active');
timeout = 0;
}, 2000);
},
function() {
button.removeClass('active');
if (timeout) {
clearTimeout(timeout);
timeout = 0;
}
}
);
});
I have created a jQuery extention just for that! This is extremely common in web development:
jQuery.addTempClass.js
$.fn.addTempClass = function(tempClass, duration){
if( !tempClass )
return this;
return this.each(function(){
var $elm = $(this),
timer;
$elm.addClass(tempClass);
// clear any timeout, if was any
clearTimeout( $elm.data('timeout') )
timer = setTimeout(function(){
$elm.removeClass(tempClass).removeData('timeout');
}, duration || 100);
// set the timer on the element
$elm.data('timeout', timer);
});
};
Usage example:
$(document.body).addTempClass('some_class', 2000)
You can include this script as a dependency file for your build system or simply copy-paste this piece of code somewhere in your code, just after jQuery has loaded, so it will be available everywhere afterwards.
I'm have a timer that polls a server for data every 10 seconds. However, everytime a user switches to another controller, the timer should get destroyed. For some reason it's not happening with my code below. The timer keeps polling the server regardless if I change controllers.
controller.js
$scope.init = function() {
//timer and timer stoper
$scope.counter= 0;
var mytimeout = $timeout($scope.onTimeout, 10000);
$scope.$on('$locationChangeStart', function() {
$timeout.cancel(mytimeout);
});
};
$scope.onTimeout = function() {
//polling server function
$scope.counter++;
var mytimeout = $timeout($scope.onTimeout, 10000);
var increase = 0;
inboxServ.check_newusers().then(function(data) {
if (data == "true") {
$scope.retrieveusers(increase);
}
});
};
It seems like you have a scope issue. You have $scope.init() which creates a timeout (held by mytimeout) and also wires up logic to cancel it if you start to change location. However, the function executed (onTimeout) starts another timeout, but assigns it to a different locally scoped mytimeout.
I would expect it, as is, to cancel the first timeout if you change location within the first 10 seconds, and fail to do so any time after that because the variables are different.
It might be as simple as changing it to something like this:
$scope.init = function() {
//timer and timer stoper
$scope.counter= 0;
$scope.mytimeout = $timeout($scope.onTimeout, 10000);
$scope.$on('$locationChangeStart', function() {
$timeout.cancel($scope.mytimeout);
});
};
$scope.onTimeout = function() {
//polling server function
$scope.counter++;
$scope.mytimeout = $timeout($scope.onTimeout, 10000);
var increase = 0;
inboxServ.check_newusers().then(function(data) {
if (data == "true") {
$scope.retrieveusers(increase);
}
});
};
I'm trying to create a delay between two loops of the nivo-slider.
Without the setTimeout everything works just fine (but without delay). So the folloing example works:
$('#slider').nivoSlider({
lastSlide: function(){
$('#slider').data('nivo:vars').stop = true;
// setTimeout(function() {
$('#slider').data('nivo:vars').stop = false;
// }, 2000);
},
});
If I uncomment the setTimeout-lines the slider stops but does not start again? Any ideas why?
Update:
http://jsfiddle.net/kgYNX/
2nd update:
Tried it with a wrapping function, too. The function gets called but if I use setTimeout in the new function it stops working: http://jsfiddle.net/kgYNX/1/
Solved it slightly different:
beforeChange: function(){
$('#slider').data('nivo:vars').stop = true;
var delay = 0;
if ($('#slider').data('nivo:vars').currentSlide == $('#slider').data('nivo:vars').totalSlides - 2) {
delay = 2000;
}
setTimeout(function() {
$('#slider').data('nivo:vars').stop = false;
}, delay);
}
I don't know why "totalSlides - 2", but it works: http://jsfiddle.net/kgYNX/15/
As a variant, you may add custom option to slider vars collection to prevent stop execution on lastSlide handler when slider re-enabled by timeout:
lastSlide: function () {
var dontStop = $('#slider').data('nivo:vars').dontStopOnLast;
if (!dontStop) {
$('#slider').data("nivoslider").stop();
setTimeout(function () {
$('#slider').data("nivoslider").start();
}, 2000);
}
$('#slider').data('nivo:vars').dontStopOnLast = !dontStop;
}
I created this piece of code to increase my webpage performance.
If autoplay.v.mystart is true, the sliding and animations of 2 slideshows will not be played,I made condition on it. My aim is to stop the animations while user is scrolling and reactivate it while user stopped scrolling, I think it will reduce a webpage load, to make a webpage scroll smoother, as I listened to people say stop unused animations or hide things that's unused. However, I see it didn't go smoother, but a bit more laggy. Is it using scroll event listener and timer/cleartimeout will take up a lot of resources too? What is the best way to accomplish my aim , to reduce my webpage load? I am thinking should I remove this code?That will be a waste,I can't decide
var saviour = {
'$mywrapper' : $('#wrapper'),
'mychecked':false,
run : function(){
var wrapper_timer;
saviour.$mywrapper.scroll(function(){
if(saviour.mychecked==false){
saviour.mychecked = true;
autoplay.v.mystart = false;
clearTimeout(wrapper_timer);
setTimeout(function(){saviour.mychecked=false},1000);
wrapper_timer = setTimeout(function(){
autoplay.v.mystart = true;
console.log('autoplay restart')
},4000);
console.log('check');
}
});
}
}
saviour.run();
First, here's a jQuery addon that provides 'scrollstart' and 'scrollstop' events, based on this, which was written for an early version of jQuery and needed to be modernized.
(function($, latency) {
var special = $.event.special;
special.scrollstart = {
setup: function() {
var timer;
function handler(evt) {
if (timer) {
clearTimeout(timer);
} else {
evt.type = 'scrollstart';
$.event.handle.apply(this, arguments);
}
timer = setTimeout(function() {
timer = null;
}, latency);
};
$(this).on('scroll.start', handler);
},
teardown: function() {
$(this).off('scroll.start');
}
};
special.scrollstop = {
setup: function() {
var timer;
function handler(evt) {
var _self = this,
_args = arguments;
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(function() {
timer = null;
evt.type = 'scrollstop';
$.event.handle.apply(_self, _args);
}, latency);
};
$(this).on('scroll.stop', handler);
},
teardown: function() {
$(this).off('scroll.stop');
}
};
})(jQuery, 300);
This version :
Replaces .bind() and .unbind() with .on() and .off(), plus associated simplification.
Allows the latency to be specified as a parameter to the self-executing function wrapper.
With 'scrollstart' and 'scrollstop' event detection in place, the application snippet for starting and stopping the animation can be as simple as this :
$(window).on ('scrollstart', function(e) {
allowAnim = false;
stopAnim();
}).on ('scrollstop', function(e) {
allowAnim = true;
anim();
}).trigger('scrollstop');
where anim() and stopAnim() are your functions for starting and stopping animation(s) and allowAnim is a boolean var in an outer scope.
You may want to adjust the latency. I found 300 to be about the minimum acceptable value, and very responsive. Larger A higher value will be less responsive but will better prevent the animation from restarting in mid-scroll.
DEMO
This is a followup to this question, where I found out how to make code be repeated every x seconds. Is it possible to make an event that can change this? I.e. I have a checkbox which is meant to control whether this is repeated or not, so I figured I'd need something like this:
$(checkbox).bind("change", function() {
switch(whether if it is ticked or not) {
case [ticked]:
// Make the code repeat, while preserving the ability to stop it repeating
case [unticked]:
// Make the code stop repeating, while preserving the ability to start again
}
});
I have no idea what I could put in the cases.
You can do it by assigning your setInterval function to a variable.
var interval = setInterval(function() { }, 1000);
and then you can stop setInterval by
clearInterval(interval);
p.s.
to start your interval you need to call var interval = setInterval(function() { }, 1000); again
You can either stop and start the interval:
var timer;
function start() {
timer = window.setInterval(function(){
// do something
}, 1000);
}
function stop() {
window.clearInterval(timer);
}
start();
$(checkbox).bind("change", function() {
if ($(this).is(':checked')) {
start();
} else {
stop();
}
});
Or you can have a flag causing the interval to skip the code:
var enabled = true;
var timer = window.setInterval(function(){
if (!enabled) {
// do something
}
}, 1000);
$(checkbox).bind("change", function() {
enabled = $(this).is(':checked');
});
function fooFunc() {
$('#foo').text(+new Date());
}
var id;
var shouldBeStopped = false;
$('input').change(function() {
if (shouldBeStopped)
clearInterval(id);
else
id = setInterval(fooFunc, 200);
shouldBeStopped = !shouldBeStopped;
});
Live DEMO