Trouble with setInterval in an object's method - javascript

I can't figure out why when I call the reset method of the object, the timer is still null. I simplified version of my object is below, followed by the jQuery that constructs a new object and runs the code. See UPPERCASE comments for my specific question points. Thanks!
var countdownTimer = {
// Default vars
milliseconds: 120000,
interval: 1000,
timer: false,
/* ... stuff ... */
countdown: function () {
var root = this;
var originalTime = root.milliseconds;
/* ... stuff */
// IN MY MIND THIS NEXT LINE SETS THE INSTANCE OF THIS OBJECT'S TIMER PROPERTY TO THE setIterval's ID. BUT DOESN'T SEEM TO BE CORRECT. WHY?
root.timer = setInterval(function () {
if (root.milliseconds < 1) {
clearInterval(root.timer); // THIS LINE SEEMS TO WORK
root.countdownComplete(); // callback function
return false;
}
root.milliseconds = root.milliseconds - root.interval;
/* .... stuff ... */
}, root.interval);
},
start: function (ms) {
if (ms) {
this.milliseconds = ms;
}
if(this.timer) {
clearInterval(this.timer); // NOT SURE IF THIS WORKS OR NOT
}
this.countdown();
},
reset: function (ms) {
var root = this;
if(root.timer) {
clearInterval(root.timer); // THIS DOES NOT WORK
} else {
console.log('timer not exist!!!!'); // ALWAYS END UP HERE. WHY?
}
/* .... stuff ... */
},
countdownComplete: function() { }
};
// Setting up click events to create instances of the countdownTimer
$(function () {
var thisPageCountdown = 4000;
$('[data-countdown]').on('click', '[data-countdown-start], [data-countdown-reset]', function () {
var $this = $(this);
var $wrap = $this.closest('[data-countdown]');
// create instance of countdownTimer
var myCountdown = Object.create(countdownTimer);
if ($this.is('[data-countdown-start]')) {
$this.hide();
$('[data-countdown-reset]', $wrap).css('display', 'block');
myCountdown.$wrap = $wrap;
myCountdown.start(thisPageCountdown);
// myCountdown.countdownComplete = function() {
// alert("Updated Callback!");
// };
}
if ($this.is('[data-countdown-reset')) {
$this.hide();
$('[data-countdown-start]', $wrap).css('display', 'block');
// RESET CALLED HERE BUT DOESN'T WORK RIGHT. SAYS myCountdown.timer IS STILL null. WHY?
myCountdown.reset(thisPageCountdown);
}
});
});

When you use var myCountdown = Object.create(countdownTimer); inside of your click function callback you are scoping it only to that callback and once the callback has executed it is garbage collected. You need to only create one instance of the countdownTimer, and it should be outside of your click event handler.
var thisPageCountdown = 4000;
// create instance of countdownTimer
var myCountdown = Object.create(countdownTimer);
$('[data-countdown]').on('click', '[data-countdown-start], [data-countdown-reset]', function () {
var $this = $(this);
var $wrap = $this.closest('[data-countdown]');

TL;DR You can fix your issue by avoiding use of the keyword this in static methods.
When you use the keyword this in a static javascript method, it refers to the item before the last dot from the call point. Example:
foo.bar(); // inside, this will refer to foo
foo.bar.foobar(); //inside, this will refer to foo.bar
var a = foo.bar.foobar();
a(); //this will refer to either null or window - oops
To prevent this behavior, you should always use the fully qualified name reference in static methods instead of relying on the this keyword.
Example from above:
reset: function (ms) {
//var root = this; // don't do this
if(countdownTimer.timer) {
clearInterval(countdownTimer.timer);
} else {
console.log('timer not exist!!!!');
}
/* .... stuff ... */
}

Related

Stoping jquery .then chain via user input

This is probably a simple question, but i'm totally lost.
I have this function.
m.util.genericSwipeVertFunc = function (
ajaxRequest,
swipeOutTarget,
swipeInTarget
) {
var stage1, stage2, failStage, dfd = $.Deferred(), finalStage, functionPromise;
// Swipe of screen wait for ajax request
stage1 = function () {
return $.when(
ajaxRequest, // Returns $.Deferred()
m.util.animateDeffered(swipeOutTarget, "fadeOutDown", true) // Returns $.Deferred()
);
};
// Swipe and Show
stage2 = function () {
swipeInTarget.show();
return m.util.animateDeffered(swipeInTarget, "fadeInDown"); // Returns $.Deferred()
};
finalStage = function () {
dfd.resolve();
}
failStage = function () {
console.log("fail!");
swipeInTarget.hide();
};
functionPromise = stage1()
.then(stage2)
.then(finalStage);
$.when(functionPromise,dfd)
.fail(failStage);
return dfd;
};
Basically it does some fancy animations to fade in and out different response outputs from ajax functions. This all works fine, except when the user tries to change between targets very fast(before one chain finishes they start another) I get crazy animation all over the place.
I want to be able to reject the chain at any point by doing something like this.
// called on script load.
var currentAction = $.Deferred();
// Called everytime someone starts animation chain.
currentAction.reject();
currentAction = m.util.genericSwipeVertFunc(dfd, swipeOutTarget, swipeInTarget);
);
With my current code the failFunction is hit correctly but it doesn't stop the execution of stage2. So it hides then shows it and continues breaking things.
So to the question. How do I put a deferred in a chain that i can reject at any time during the chains execution ? :)
Example fiddle
http://jsfiddle.net/ff3jojbo/
Update for clarification
I am using animate.css for my animations. Not jquery animation.
I am more interested in how to stop the chain from starting the next stage at any point from user input.
Answer fiddle
http://jsfiddle.net/aefkwa8a/
Try using .queue() , .promise()
// array of functions to add to queue
var arr = [];
var swipeInTarget = $("#stage1");
var swipeOutTarget = $("#stage2");
// pseudo `ajax` call
var ajaxRequest = function ajaxRequest(next) {
return $.Deferred(function(d) {
setTimeout(function() {
d.resolve("ajaxRequest")
}, Math.random() * 5000)
}).promise()
// Note `.then(function() {console.log(this)})` for example ,
// can be removed
.then(function(data) {
console.log(data)
}).then(next)
}
var stage1 = function stage1(next) {
return swipeOutTarget.fadeTo(Math.random() * 5000, Math.random())
.promise()
// Note `.then(function() {console.log(this)})` for example ,
// can be removed
.then(function() {
console.log(this)
})
.then(next)
}
var stage2 = function stage2(next) {
return swipeInTarget
.show(Math.random() * 5000, function() {
return $(this).fadeTo(Math.random() * 2000, Math.random())
})
.promise()
// Note `.then(function() {console.log(this)})` for example ,
// can be removed
.then(function() {
console.log(this)
})
.then(next)
}
// do stuff when queue cleared
var failStage = function failStage() {
return swipeInTarget.hide(Math.random() * 2000)
.promise().then(function() {
console.log("m processes stopped")
})
}
// always do stuff when queue cleared,
// or all functions in queue complete
var finalStage = function finalStage() {
console.log("complete", this)
}
// create jQuery object
var m = $({
m: arr
});
// add function to `"stages"` queue
m.queue("stages", [stage1, stage2, finalStage]);
// do stuff when all functions complete , or queue cleared
m.promise("stages")
.then(finalStage);
// dequque `"stages"` queue
m.dequeue("stages");
// clear `"stages"` queue
$("button").click(function() {
m.queue("stages", [])
.promise("stages").always(failStage)
})
#stage2 {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
<button>stop m processes</button>
<div id="stage1">stage1</div>
<div id="stage2">stage2</div>
OP's own solution here can fail after several clicks. In particular, if button is clicked while a section is flying in, then the latest demanded section may fly in, then disappear.
This solution is completely different.
Instead of using jQuery's queue/dequeue, it uses a regular stage1().then(stage2) promise chain, and stops progress down that chain by removing the CSS animation classes from the animated element and detaching its animationend handler, thus ensuring the promise associated with completion never resolves.
As you will see, much of the functionality is factored as jQuery plugins, which makes for convenient, compact syntax.
$(function () {
// **************************
// *** Various outer vars ***
// **************************
var $sections = $('#TabSection>div').hide();
var ajaxPromise;
var str = {
//various strings
'animationend': 'webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend',
'fadeOutClasses': 'fadeOutDown animated',
'fadeInClasses': 'fadeInDown animated',
'allClasses': 'fadeOutDown fadeInDown animated'
};
// ***********************************************
// *** Utilities in the form of jQuery plugins ***
// ***********************************************
jQuery.fn.killAnim = function(animation) {
/* jQuery plugin :
* Remove all the animation classes from all possible targets, and
* detach any currently attached animationend handlers.
* Depends on: str (object).
*/
return this.off(str.animationend).removeClass(str.allClasses);
};
jQuery.fn.cssAnimate = function (animation) {
/* jQuery plugin :
* Perform CSS animation and return promise.
* Depends on: str (object); killAnim (plugin).
*/
var that = this;
return $.Deferred(function(dfd) {
// if no target or target not visible, resolve;
if(that.length == 0 || !that.is(':visible')) {
dfd.resolve();
}
that.addClass(animation).one(str.animationend, dfd.resolve);
}).then(function() {
that.killAnim();
});
};
jQuery.fn.genericSwipeVertFunc = function () {
/* jQuery plugin :
* Sequence two CSS animations - fadeOut then fadeIn.
* Depends on: str (object); killAnim (plugin); cssAnimate (plugin).
*/
var that = this; // swipeInTarget
var swipeOutTarget = $sections.filter(':visible()').eq(0);
function stage1() {
$sections.killAnim().not(swipeOutTarget).hide();
return swipeOutTarget.cssAnimate(str.fadeOutClasses).then(function() {
swipeOutTarget.hide();
});
};
function stage2() {
$sections.not(that).killAnim().hide();
return that.show().cssAnimate(str.fadeInClasses);
};
return stage1().then(stage2);
};
// **********************
// *** Event handlers ***
// **********************
$('button').on('click', function (event) {
var inTarget = $($(this).data('tar'));
if(ajaxPromise) {
ajaxPromise.abort('aborted');
}
// *** start: emulate AJAX ***
ajaxPromise = $.Deferred(function(dfrd) {
setTimeout(dfrd.resolve, 1000);
});
ajaxPromise.abort = ajaxPromise.reject;
// *** end: emulate AJAX ***
ajaxPromise.then(function() {
return inTarget.genericSwipeVertFunc();
}).fail(function(e) {
$sections.killAnim().hide();
console.log(e);
});
});
});
I believe this solution to be more reliable. Even with lots of manic clicking, I could not defeat it.
Try it here

I wanted to make a javascript library function is not working

I wanted to call the run function that should call the other and action will be done on the base of element_id
NGL = {}
NGL.SceneBuilder = function() {
var yamlFile = 'http://example.com/main.yaml'
var parseYaml = function() {
}
var buildScene = function() {
// other code
simulationStarted(element_id);
}
return {
run: function(element_id) {
parseYaml();
buildScene(element_id);
}
}
}
NGL.SceneBuilder.run('#someid');
You're not executing your factory so NGL.SceneBuilder is a function, not an object having the run property. Call the function :
NGL.SceneBuilder = (function() {
...
})(); // <<===
Note also that you forget to declare the element_id parameter in buildScene but maybe is it just for the question.

Change JS function to an object

So I asked a question awhile ago here > Cons of MouseOver for webpages and ran into some issues with enabling/disabling events. According to the answer from the post, I was supposed to update my function as an object to call it out easily. However after a few hours of trial and error as well as online research, I still don't understand how the object works
So this is the function I want to put into an object,
$(function () {
$('#01 img:gt(0)').hide();
setInterval(function () {
$('#01 :first-child').fadeOut(1500)
.next('img').fadeIn(1500)
.end().appendTo('#01');
}, 3000);
});
And this was the code provided to initialize my object,
var Slideshow = (function () {
this.interval;
this.start = function () {
...
initialize
...
// catch the interval ID so you can stop it later on
this.interval = window.setInterval(this.next, 3000);
};
this.next = function () {
/*
* You cannot refer to the keyword this in this function
* since it gets executed outside the object's context.
*/
...
your logic
...
};
this.stop = function () {
window.clearInterval(this.interval);
};
})();
So how exactly should I implement my function into the object so that it works?
I would structure it like this:
function Slideshow(container) {
this.interval = undefined;
this.start = function () {
container.find("img").first().show();
container.find("img:gt(0)").hide();
this.interval = window.setInterval(this.next, 3000);
};
this.next = function () {
var first = container.find(":first-child");
first.fadeOut(1500, function () {
first.next("img").fadeIn(1500);
first.appendTo(container);
});
};
this.stop = function () {
window.clearInterval(this.interval);
};
}
$(function () {
var div = $("#div01");
var slides = new Slideshow(div);
div.hover(function() {
slides.stop();
}, function() {
slides.start();
});
slides.start();
});
DEMO: http://jsfiddle.net/STcvq/5/
latest version courtesy of #Bergi
What you should aim to do, looking at the recommended code, is to move the logic of your setInterval inside the Slideshow.next() function. That basically covers your fadeout, fadein logic.
So your function would look something like:
this.next = function() {
$('#01 :first-child').fadeOut(1500)
.next('img').fadeIn(1500)
.end().appendTo('#01');
};
in the simplest of worlds.
Ideally, you would want to instantiate your Slideshow by telling it which id it should use, by passing that in the constructor. That is, you should be able to call
new Slideshow('#01') as well as new Slideshow('#02') so that you can truly reuse it.
Then, your next function would change to look something like (assuming the id is stored in this.elementId):
this.next = function() {
$(this.elementId + ':first-child').fadeOut(1500)
.next('img').fadeIn(1500)
.end().appendTo('#01');
};
Hope this helps
change syntax to :
var Slideshow = (function () {
return {
interval:null,
start : function () {
// catch the interval ID so you can stop it later on
this.interval = window.setInterval(this.next, 3000);
},
next: function () {
},
stop : function () {
window.clearInterval(this.interval);
}
};
})();
as you are using jquery, a better ans is to create a little plugin:
http://learn.jquery.com/plugins/basic-plugin-creation/

asp.net ajax property value timestamp is undefined

I have a problem with the value assignment and retrieval in asp.net ajax. The value of timestamp is undefined
Code:
/// <reference name="MicrosoftAjax.js"/>
Type.registerNamespace("LabelTimeExtender1");
LabelTimeExtender1.ClientBehavior1 = function(element) {
LabelTimeExtender1.ClientBehavior1.initializeBase(this, [element]);
this._testelement=this.get_element();
this._timestamp= this.get_element().attributes['TimeStamp'].value;
alert(_timestamp);
},
LabelTimeExtender1.ClientBehavior1.prototype = {
initialize: function() {
LabelTimeExtender1.ClientBehavior1.callBaseMethod(this, 'initialize');
setInterval (this.timer,1000);
alert("after");
},
dispose: function() {
//Add custom dispose actions here
LabelTimeExtender1.ClientBehavior1.callBaseMethod(this, 'dispose');
},
timer: function(){
alert(this.timestamp);
var splitdate=this._timestamp.split(/[:]+/);
alert(splitdate);
var date= new Date(this._timestamp);
alert( date.toString());
var datenow= new Date ();
alert(datenow.toString());
this._element.innerText=" ";
alert(this._element);
if(date.getUTCFullYear<datenow.getUTCFullYear)
{
alert("year");
var myelement= this.get_element();
myelement .innerHTML= date.getUTCFullYear.toString();
}
if(date.getUTCMonth<datenow.getUTCMonth)
{
alert("month");
this.get_element().innerHTML=date.getUTCMonth.toString();
}
if(date.getUTCDay<datenow.getUTCDay)
{
this.get_element().innerHTML=date.getUTCDay.toString();
}
if(date.getUTCHours <datenow.getUTCHours )
{
this.get_element().innerHTML=date.getUTCHours .toString();
}
if(date.getUTCMinutes<datenow.getUTCMinutes)
{
this.get_element().innerHTML=date.getUTCMinutes.toString();
}
},
set_timestamp: function(value)
{
this._timestamp=value;
},
get_timestamp: function()
{
return this._timestamp;
}
}
LabelTimeExtender1.ClientBehavior1.registerClass('LabelTimeExtender1.ClientBehavior1', Sys.UI.Behavior);
if (typeof(Sys) !== 'undefined') Sys.Application.notifyScriptLoaded();
Why is the value of _timestamp undefined?
I would suggest moving the code which sets this._timestamp into your initialize function.
My other suggestion is to use the getters and setters even within your own code to ensure encapsulation. So, the alerts would actually be alert(this.get_timestamp()). And, in your initialize function, you would call this.set_timestamp(this.get_element().attributes['TimeStamp'].value).
Thanks to the comment I see the problem is actually in the setInterval call. When you call window.setInterval(this.timer, 1000);, when the timer function is called this refers to window, not to your object. So instead, do something like this:
var self = this;
window.setInterval(function () {
self.timer();
}, 1000);
That will make this inside of timer() refer to the correct object.

Javascript prototypal inheritance prototype function call

Have a question about calling one prototype function in another prototype function.
for instance lets say I have a basic slider with two prototype functions.
function Slider() {
}
Slider.prototype.transition = function() {
}
Slider.prototype.setTargets = function() {
}
What is the proper way of calling the setTargets function inside of the transition function so something like this:
Slider.prototype.transition = function() {
this.target.fadeOut('normal', function() {
// call setTargets?
this.setTargets(); // errors when i do this
});
}
thanks for the help
If this.target is an jQuery Object the callback of fadeOut will be called with this as the DOMNode.
Do this instead:
Slider.prototype.transition = function() {
var me = this;
this.target.fadeOut('normal', function() {
me.setTargets(); // <-- See me
});
}
I have chosen the name that me for all my initialized references to this. I never used that me for DomNodes, etc. makes sence for me.
Please see comments for furture views on this point.
EDIT:
Acually i used me not that - Dont know what im thinking ?? !
And for comment:
Slider.prototype.transition = function() {
var me = this;
this.target.fadeOut('normal', function() {
var domThis = this;
me.setTargets(); // <-- See me
setTimeout(function() {
// Use domThis [Dom Node]
}, 123);
});
}
Or:
You can make a jQuery object of this:
var $this = $(this);
me.setTargets(); // <-- See me
setTimeout(function() {
// Use $this [jQuery Object]
}, 123);
If you need the jQuery Object of this you can refer to: me.target
me.setTargets(); // <-- See me
setTimeout(function() {
// Use me.target [jQuery Object]
}, 123);
The fadeOut function is not called in the context of your slider object.
Slider.prototype.transition = function() {
var slider = this;
this.target.fadeOut('normal', function() {
// call setTargets?
slider.setTargets(); // should work now.
});
}

Categories

Resources