I'm working on a jQuery plugin that utilises JavaScript intervals. However, when the plugin is called again with the argument running as false, then I want the previous interval called on that particular DOM element to be cleared.
Here is some shortened code:
(function ($) {
$.fn.examplePlugin = function (running) {
return this.each(function () {
if (running === false) {
// Clear the previous interval assigned to this DOM element
} else {
setInterval(function () {
// Interval code
}, 50);
}
return this;
});
};
})(jQuery);
How is this possible? I thought of using global variables to store the intervals but I don't really want to do that. I also don't have any clue how to assign an interval to a particular DOM element.
If you want to save intervals per DOM element, then you'd be best off with .data(), which does exactly what you want - setting data on a particular DOM element.
var saved = $(this).data("__examplePlugin_interval");
if (saved) { // I think you mean when it *is* running
clearInterval(saved);
$(this).removeData("__examplePlugin_interval");
} else {
var interval = setInterval(function () {
// Interval code
}, 50);
// choose a name that won't be accidentally overwritten by others
$(this).data("__examplePlugin_interval", interval);
}
Related
I am using an already defined function and now want to add a pollServer function to it so that this functions runs over and over. I keep getting errors when I try to wrap the existing function in another. Is there a better way to do this?
function callD(id) {
jQuery('document').ready(function pollServer(){
window.setTimeout(function () {
var ab = document.getElementById('a')
console.log(ab);
var bod = document.getElementById(+id)
if (ab == null) {
bod.style.background='green'
} else {
bod.style.background='blue'
}
}, 1200);
})
}
callD();
pollServer();
pollServer isn't defined where you're calling it. Also id isn't being passed to callD, and you also have a +id which doesn't make sense in a document.getElementByid, since if there's any non-number in the ID, that would be NaN. You're also not polling a server, you're setting a timeout once and doing some work that doesn't involve a server. You would want setInterval for regular polling, or to call the function again on some condition like a failure.
$(document).ready(function () {
var intervalId;
function callD(id) {
function pollServer() {
intervalId = window.setInterval(function () {
var ab = document.getElementById('a')
console.log(ab);
var bod = document.getElementById(id)
if (ab == null) {
bod.style.background='green'
} else {
bod.style.background='blue'
}
}, 1200);
}
pollServer();
}
callD('some-id');
// on some condtion eventually:
clearInterval(intervalId);
})
Yeah, jQuery can make things pretty gnarly with all the nested callbacks. To make the code cleaner and easier to understand, I like to split my functions up and define them all at the top-most level of the script, then compose them together like so:
/**
* this function will check for the existing elements
* and update styles
*/
function setBodyStyle(id) {
var ab = document.getElementById('a');
console.log(ab);
var bod = document.getElementById(+id);
if (ab == null) {
bod.style.background='green';
} else {
bod.style.background='blue';
}
}
/**
* this function will create a timeout or interval
* which will in turn run setBodyStyle()
*/
function pollServer() {
// I think you want setInterval here if you're polling?
// setInterval will run _every_ 1200ms,
// setTimeout only runs once after 1200ms
window.setInterval(function() {
// not sure where you're getting id from,
// but you'll want to pass it here
setBodyStyle();
}, 1200);
}
// when the document is ready, run pollServer()
jQuery(document).ready(pollServer);
Having small functions that do one thing is just best-practice for the reasons I mentioned above. This will help your script be more understandable, which will help you find bugs.
For example, two things I don't understand about your code above:
where does the id variable come from? I don't see you passing it to your function from anywhere
how does your script poll the server? I don't see the code for that anywhere either.
Seemed you mean run the function pollServer every 1.2 sec. If so, you'd need to do two things
Use setInterval rather than setTimeout
Delete the last line for the pollServer function, because it is not accessible from outside the ready function block.
I've written some code that when you click a button it adds an instance of a function to an array,
var objects = [];
$(document).on("click", ".addButton", function(){
objects.push(new newObject(1));
});
function newObject(amount){
setInterval(function(){
addValue(amount);
}, 1000);
}
So then every second each new object created keeps running the addValue function every second adding the amount.
The problem is when I try and destroy that function with objects.pop() it deletes the object but the setInterval doesn't stop running.
How do I make it destroy everything in that function and stop it from running?
There is nothing quite like that in JS for setInterval. I would suggesting declaring a method to handle clean up.
// "Class" declaration
function newObject(amount) {
var id = setInterval(function() {
addValue(amount);
}, 1000);
this.kill = function() {
clearInterval(id);
}
}
// "Public" api for the data structure
var objects = [];
function addNewObject() {
objects.push(new newObject(1));
}
function destroyLastObject() {
objects.pop().kill();
}
// Event bindings
$(document).on("click", ".addButton", addNewObject);
$(document).on("click", ".removeButton", destroyLastObject);
Completely untested, but along these lines should work.
EDIT
This, imo, is a great resource for learning about different patterns within javascript - long but well well worth the read: https://addyosmani.com/resources/essentialjsdesignpatterns/book/
You got to find something to check against to clear the interval. I am clearing based on array length. It only executes once.
// you got to find something to check against to clear the interval
var objects = [];
document.addEventListener("click", function(){
console.log('click');
objects.push(new newObject(1));
});
function newObject(amount){
var interval= setInterval(function(){
if(objects.length !==0){
clearInterval(interval);
}
}, 1000);
}
This is originally from (Pause execution in while loop locks browser (updated with fiddles))
I have been at this all day and I can't figure out how to keep javascript from advancing to the next line and in essence executing all lines at once. I have tried every combination of delay / setTimeout I can think of to no avail.
I just want the elements in the array to flash once then pause, then do it again for another element in the array till all elements have been removed and the array is empty.
But because javascript is executing all lines at once I end up with the appearance of all elements flashing at the same time.
Here is the fiddle:
http://jsfiddle.net/ramjet/xgz52/7/
and the relevant code:
FlashElement: function () {
while (elementArray.length) {
alert('a ' + elementArray.length);
var $el = elementArray.eq(Math.floor(Math.random() * elementArray.length));
PageLoadAnimation.FlashBlast($el);
alert('delay complete');
elementArray = elementArray.not($el);
alert('array popped');
alert('z ' + elementArray.length);
}
},
ANSWER FOR THIS SITUATION. Hopefully it will help others.
As Zach Saucier points out the loop was really my problem...but not the only problem. I was the other problem(s).
Me first.
Fool that I am I was really causing my own complications with two things I was doing wrong.
First using jsfiddle my javascript would error due to syntax or some such thing but fiddle doesn't tell you that (to my knowledge) so my fiddle wouldn't run but I took it in pride as MY CODE IS FINE stupid javascript isn't working.
Second I was passing my function to setTimeout incorrectly. I was adding the function parens () and that is not correct either which would bring me back to issue one above.
WRONG: intervalTimer = setInterval(MyFunction(), 1500);
RIGHT: intervalTimer = setInterval(MyFunction, 1500);
As for the code. As Zach pointed out and I read here (http://javascript.info/tutorial/settimeout-setinterval) while he was responding setting a timeout in a loop is bad. The loop will iterate rapidly and with the timeout one of the steps in the loop we get into a circular firing squad.
Here is my implementation:
I created a couple variables but didn't want them polluting the global scope so I created them within the custom domain. One to hold the array of elements the other the handle to the setInterval object.
var PageLoadAnimation =
{
elementArray: null,
intervalTimer: null,
....
}
In my onReady function (the one the page calls to kick things off) I set my domain array variable and set the interval saving the handle for use later. Note that the interval timer is how long I want between images flashes.
onReady: function ()
{
elementArray = $('#PartialsContainer').children();
//black everything out just to be sure
PageLoadAnimation.BlackOutElements();
//flash & show
intervalTimer = setInterval(PageLoadAnimation.FlashElement, 1500);
},
Now instead of looping through the array I am executing a function at certain intervals and just tracking how many elements are left in the array to be flashed. Once there are zero elements in the array I kill the interval execution.
FlashElement: function ()
{
if(elementArray.length > 0) //check how many elements left to be flashed
{
var $el = PageLoadAnimation.GrabElement(); //get random element
PageLoadAnimation.FlashBlast($el); //flash it
PageLoadAnimation.RemoveElement($el); //remove that element
}
else
{
//done clear timer
clearInterval(intervalTimer);
intervalTimer = null;
}
},
So the whole thing is:
var PageLoadAnimation =
{
elementArray: null,
intervalTimer: null,
onReady: function () {
elementArray = $('#PartialsContainer').children();
//black everything out just to be sure
PageLoadAnimation.BlackOutElements();
//flash & show
intervalTimer = setInterval(PageLoadAnimation.FlashElement, 1500);
//NOT this PageLoadAnimation.FlashElement()
},
BlackOutElements: function () {
$('#PartialsContainer').children().hide();
},
FlashElement: function ()
{
if(elementArray.length > 0)
{
var $el = PageLoadAnimation.GrabElement();
PageLoadAnimation.FlashBlast($el);
PageLoadAnimation.RemoveElement($el);
}
else
{
//done clear timer
clearInterval(intervalTimer);
intervalTimer = null;
}
},
GrabElement: function()
{
return elementArray.eq(Math.floor(Math.random() * elementArray.length));
},
RemoveElement: function($el)
{ elementArray = elementArray.not($el); },
FlashBlast: function ($el) {
//flash background
$el.fadeIn(100, function () { $el.fadeOut(100) });
}
}
Hope that help others understand the way to go about pausing execution in javascript.
The reason why you were having trouble is because setTimeout function is non-blocking and will return immediately. Therefore the loop will iterate very quickly, initiating each of the timeouts within milliseconds of each other instead of including the previous one's delay
As a result, you need to create a custom function that will wait on the setInterval to finish before running again
FlashElement: function () { // Call it where you had the function originally
myLoop();
},
...
function myLoop() {
setTimeout(function () { // call a setTimeout when the loop is called
var $el = elementArray.eq(Math.floor(Math.random() * elementArray.length));
PageLoadAnimation.FlashBlast($el);
elementArray = elementArray.not($el);
if (0 < elementArray.length) { // if the counter < length, call the loop function
myLoop();
}
}, 1000)
}
Feel free to change the delay to whatever value you wish (3000ms to let each fade finish before the last at the moment). If you want to start the fade in of the next before the previous ends and keep them in their original positions you would have to animate the opacity using .css instead of using fadeIn and fadeOut
My answer is based on this answer from another SO question
(I've looked at all similar questions/answers but none of them solve my problem.)
The code:
var timeoutHandle;
function showLoader(show) {
if (show) {
$('.loader').html('Loading...');
$('.loader').show();
timeoutHandle = setTimeout(function () {
if ($('.loader').is(':visible')) {
$('.loader').html('Still loading...');
}
}, 15000);
}
else {
$('.loader').hide();
clearTimeout(timeoutHandle);
}
}
The AJAX function simply calls showLoader(true) before calling the server, and then showLoader(false) after a result. I still sometimes see the text change from "Loading..." to "Still loading..." long before 15 seconds, so it's as if a timer thread is still running. Is there something wrong with the code above? Or could the problem be with other code..
edit: I must add that showLoader(true) can be called again (and again) before a response from the server
You should add a check to see if there is already a timeoutHandle before creating a new one.
try this:
if(timeoutHandle){
clearTimeout(timeoutHandle);
timeoutHandle = null;
}
timeoutHandle = setTimeout(function () {
if ($('.loader').is(':visible')) {
$('.loader').html('Still loading...');
}
}, 15000);
and then in the else case set timeoutHandle to null after you clear it like so:
clearTimeout(timeoutHandle);
timeoutHandle = null;
This will eliminate the chance of you creating concurrent timeouts if showLoader(true) function is called more than once.
What might be happening is that you're placing multiple calls to showLoader since this is a global function you can access it from anywhere, you typically don't want that.
I would consider changing it to a monad implementation:
function create_loader(elem) {
var handle = null;
function show() {
elem.html('Loading...');
elem.show();
if (handle !== null) {
clearTimeout(handle); // clear the previous one
}
handle = setTimeout(function () {
elem.html('Still loading...');
}, 15000);
}
return {
show: show,
clear: function () {
elem.hide();
clearTimeout(handle);
handle = null;
}
};
}
Usage:
var loader = create_loader($(".loader"));
loader.clear();
loader.show();
loader.show(); // each new call to show will reset the 15s timer
loader.show();
loader.show();
loader.clear();
// and you can make another one that operates independently of other one
var another_loader = create_loader($(".anotherLoader"));
Now you have a loader object that knows about it's own state.
In your post you mention that showloader can be called multiple times before the first return. This is your problem. You are overwriting an already existing timeoutHandle with a new one without destroying the already existing handle. You should check if the timeoutHandle is set or not set before you create a new one.
You don't call clearTimeout(timeoutHandle) then starting new request, if timeoutHandle exist
I have a recursive type function in Javascript that runs like this:
function loadThumb(thumb) {
rotate=setTimeout(function() {
loadThumb(next);
}, delay);
}
Note: I've simplified the function to make it easier to read.
I have "a" tags called like this
Load thumb 3
However, they don't clearout the timer, the timer continues to cycle through the function irregardless of the clearTimeout() being called.
Any ideas why? I think it might have something to do with a scope problem or something like that.
Yeah, you need to make rotate a global variable. Simply declare it outside the function like so:
var rotate;
var delay = 1000;
function loadThumb(thumb) {
alert("loading thumb: " + thumb);
rotate = setTimeout(function() {
loadThumb(thumb + 1);
}, delay);
}
Also, you need to make sure you clear the timeout before you call loadThumb. Otherwise you'll clear the timer you just started.
Load thumb 3
fiddle: http://jsfiddle.net/63FUD/
it may be the issue of scope so make rotate as global variable and call clearTimeout(rotate);
refer clearTimeout() example
It may be a scoping issue if you are not declaring rotate externally.
Try this:
var rotate = 0;
function loadThumb(thumb) {
rotate=setTimeout(function() {
loadThumb(next);
}, delay);
}
Return false on the link
Since you are not using var rotate, it should not be a scoping issue since rotate would be in the window scope. Can you show the complete code?
It is considered poor coding to inline the script - you should attach the event handler onload of the page
Also you should not have the setTimeout inside a function that might be called for one image
Try this:
var rotate,next=1;
function loadThumb(thumb) {
if (thumb) ... use thumb
else ... use next
}
function slide() {
rotate=setInterval(function() {
loadThumb();
next++;
if (next>=images.length) next=0;
}, delay);
}
window.onload=function() {
var links = document.getElementsByTagName("a");
if (links[i].className==="thumbLink") {
links[i].onclick=function() {
var idx = this.id.replace("link","");
loadThumb(idx);
clearInterval(rotate);
return false;
}
}
document.getElementById("start").onclick=function() {
slide();
return false;
}
document.getElementById("stop").onclick=function() {
clearInterval(rotate);
return false;
}
slide();
}
assuming
Start
Stop
Show 1
Show 2
Show 3
If you have to manage multiple timeouts, you can use an object in the global scope and some custom methods to create and remove your timeouts. To access the methods you can either put the calls in the onclick handler of your links (like in the example), or use a library like jQuery to bind them.
<script type="text/javascript">
var timeouts = timeouts || {};
function createTimeout(name, milliseconds, callback) {
timeouts.name = setTimeout(callback, milliseconds);
}
function removeTimeout(name) {
if (typeof(timeouts.name) !== undefined) {
clearTimeout(timeouts.name);
timeouts.name = undefined;
}
}
createTimeout('foo', 5000, function() {
alert('timeout')
});
</script>
i have also posted an example on jsFiddle http://jsfiddle.net/AGpzs/
I'm not sure what exactly you are doing, because as far as I can see you didn't post all the code, but this looks better for me:
function loadThumb(thumb) {
return setTimeout(function() {
loadThumb(next);
}, delay);
}
and then:
Load thumb 3