clearTimeout on dynamically updating timer - javascript

I've got a timer that I'm updating dynamically.
------------------update --------------------------
when I first posted the question, I didn't think it mattered that the timer was being called from within a backbone view, but I believe as a result of that, I can't use a global variable (or at least a global variable isn't working). I'll be calling multiple timers, so setting only one global variable and deleting it won't work. I need to be able to clear a single timer without clearing the others.
What I start the timer,
function countDown(end_time, divid){
var tdiv = document.getElementById(divid),
to;
this.rewriteCounter = function(){
if (end_time >= MyApp.start_time)
{
tdiv.innerHTML = Math.round(end_time - MyApp.start_time);
}
else {
alert('times up');
}
};
this.rewriteCounter();
to = setInterval(this.rewriteCounter,1000);
}
in my app, I initiate the timer in a backbone view with
MyApp.Views.Timer = Backbone.View.extend({
el: 'div#timer',
initialize: function(){
timer = this.model;
this.render();
},
events: {
"clicked div#add_time": "update_timer"
}
render: function(){
$(this.el).append(HandlebarsTemplates['timer'](timer);
this.start_timer();
},
start_timer: function(){
delete main_timer; // this doesn't work :(
clearTimtout(main_timer); //this doesn't work either :(
var main_timer = setTimeout(new countDown(timed.length, 'main_timer'),timed.length*1000);
},
update_timer: function(){
timed.length=timed.length+30
this.start_timer();
}
});
so what I'm trying to do is to update the timer, kill the old timer, and then restart it with the new values. I have different timers, so just calling the timed.length within the countdown function won't work.

var main_timer = setTimeout(new countDown(timed.length, 'main_timer'),timed_length*1000);
This statement creates a local variable main_timer. Instead you have to create a global variable and use that to clear the time out as shown below
clearTimtout(main_timer);
main_timer = setTimeout(new countDown(timed.length, 'main_timer'),timed_length*1000);
EDIT:
use a function as setTimeout handler as shown below
clearTimeout(main_timer);
main_timer = setTimeout(function(){
new countDown(timed.length, 'main_timer');
},timed_length*1000);
note: hope timed.length and timed_length are correct.
EDIT:
modify countdown like given below.
function countDown(end_time, divid){
var tdiv = document.getElementById(divid),
to;
this.rewriteCounter = function(){
if (end_time >= MyApp.start_time)
{
tdiv.innerHTML = Math.round(end_time - MyApp.start_time);
}
else {
alert('times up');
}
};
this.clearRewriteCounter = function(){
clearInterval(to);
}
this.rewriteCounter();
to = setInterval(this.rewriteCounter,1000);
return this;
}
and in MyApp.Views.Timer
MyApp.Views.Timer = Backbone.View.extend({
el: 'div#timer',
initialize: function(){
timer = this.model;
this.render();
},
events: {
"clicked div#add_time": "update_timer"
}
render: function(){
$(this.el).append(HandlebarsTemplates['timer'](timer);
this.start_timer();
},
start_timer: function(){
clearTimeout(this.main_timer);
this.main_timer = setTimeout(function(){
if(this.countDownInstance){
this.countDownInstance.clearRewriteCounter();
}
this.countDownInstance = new countDown(timed.length, 'main_timer');
},timed_length*1000);
},
update_timer: function(){
timed.length=timed.length+30
this.start_timer();
}
});

Related

Calling setInterval from within function

I'm making a slider and i ran into problem. I'm practicing modular js and this code works fine for the first time, but setInterval won't repeat itself so the slides continue changing. How to make it work? Thanks in advance.
var currentSlide = 1;
var slider = {
init: function(){
this.cacheDom();
this.setInt();
},
cacheDom: function(){
this.$panel = $('.slider');
this.$actPan = this.$panel.find('.panel.active');
},
setInt: function(){
return setInterval(this.mainFunction(), 3000);
},
mainFunction: function(){
this.$actPan.fadeOut(2000, this.showNextPanel());
this.$panel.find('.tabs li:nth-child('+ currentSlide +')').find('img').attr('src', 'images/g4228.png');
},
showNextPanel: function(){
this.$panel.find('#panel' + currentSlide).removeClass('active');
this.revert();
},
revert: function(){
currentSlide++;
if(currentSlide === 4){currentSlide = 1;}
this.$panel.find('.tabs li:nth-child('+ currentSlide +')').find('img').attr('src', 'images/g4228.png');
this.$panel.find('#panel' + currentSlide).fadeIn(2000, this.adder());
},
adder: function(){
this.$panel.find('#panel' + currentSlide).addClass('active');
}
};
slider.init();
So, I'm thinking that only setInt and init functions needs changing.
The problem is this line:
return setInterval(this.mainFunction(), 3000);
This is calling this.mainFunction() and passing the result to setInterval; setInterval needs to be passed a function to call on an interval. Replace that with this:
return setInterval(this.mainFunction.bind(this), 3000);
The bind is needed so that this is preserved when setInterval calls it.

Scope of variable in DOJO when created from within function

In a DOJO widget there is code in the postCreate and destroy method to create/start and stop a timer like you can see below. Depending on the value in a drop down box the timer is started or stopped. This works fine so far.
postCreate: function() {
var deferred = this.own(<...some action...>)[0];
deferred.then(
lang.hitch(this, function(result) {
this.t = new dojox.timing.Timer(result.autoRefreshInterval * 1000);
this.t.onTick = lang.hitch(this, function() {
console.info("get new data");
});
this.t.onStart = function() {
console.info("starting timer");
};
this.t.onStop = function() {
console.info("timer stopped");
};
})
);
this.selectAutoRefresh.on("change", lang.hitch(this, function(value) {
if (value == "Automatic") {
this.t.start();
} else {
this.t.stop();
}
}));
},
When leaving the page the timer is still active so I want to stop it when I leave the page using DOJOs destroy() method.
destroy: function() {
this.t.stop();
},
This however throws a this.t.stop is not a function exception. It seems like this.t is not created in the context of the widget although I use lang.hitch(this...
What am I missing here?
I solved that by just renaming the variable t to refreshTimer. Maybe t is some kind of reserved variable in Dojo?

clearInterval not working as I expect it too

I made a demo which is here. All you have to do is start typing in the text field, make sure you have the console open. So as you type, you'll instantly see the OMG Saved, and the counter in the console will go nuts.
Now click the button, watching the console you should see something like 11 or some other value, but you'll also see the counter reset and continues going. I do not want this. I want the counter to stop, I have clicked a button and while the page hasn't refreshed, the counter should stop if I understand these docs on setInterval().
the app I am developing which uses code very similar to this, does not refresh as most single page apps don't. So it is imperative that I have control over this setInterval.
So my question is:
How do I reset the counter such that, until I type again in the input box OR if the input box element cannot be found the flash message does not show up, the interval is set back to 0.
update
The following is the JavaScript code, which is run on the link provided above.
var ObjectClass = {
initialize: function() {
$('#flash-message').hide();
},
syncSave: function() {
$('#content').keypress(function(){
SomeOtherClass.autoSave = setInterval( function(){
$('#flash-message').show();
$('#flash-message').delay(1000).fadeOut('slow');
}, 500);
});
},
listenForClick: function() {
$('#click-me').click(function() {
console.log(SomeOtherClass.autoSave);
clearInterval(SomeOtherClass.autoSave);
});
}
};
var SomeOtherClass = {
autoSave: null
};
ObjectClass.initialize();
ObjectClass.syncSave();
ObjectClass.listenForClick();
You have to put this
clearInterval(SomeOtherClass.autoSave);
before this line:
SomeOtherClass.autoSave = setInterval( function(){
So that you kill the previous interval and you ahve ONLY ONE interval at the same time
Your code will be:
var ObjectClass = {
initialize: function () {
$('#flash-message').hide();
},
syncSave: function () {
$('#content').keypress(function () {
clearInterval(SomeOtherClass.autoSave);
SomeOtherClass.autoSave = setInterval(function () {
$('#flash-message').show();
$('#flash-message').delay(1000).fadeOut('slow');
}, 500);
});
},
listenForClick: function () {
$('#click-me').click(function () {
console.log(SomeOtherClass.autoSave);
clearInterval(SomeOtherClass.autoSave);
});
}
};
var SomeOtherClass = {
autoSave: null
};
ObjectClass.initialize();
ObjectClass.syncSave();
ObjectClass.listenForClick();
What you need to do is use a timeout instead of an interval, like this:
var ObjectClass = {
initialize: function() {
$('#flash-message').hide();
},
syncSave: function() {
$('#content').keypress(function(){
SomeOtherClass.autoSave = setTimeout( function(){
$('#flash-message').show();
$('#flash-message').delay(1000).fadeOut('slow');
}, 500);
});
},
listenForClick: function() {
$('#click-me').click(function() {
console.log(SomeOtherClass.autoSave);
if(typeof SomeOtherClass.autoSave === 'number'){
clearTimeout(SomeOtherClass.autoSave);
SomeOtherClass.autoSave = 0;
}
});
}
};
var SomeOtherClass = {
autoSave: 0
};
ObjectClass.initialize();
ObjectClass.syncSave();
ObjectClass.listenForClick();

increment issue with backbone

I could not find anything that mentioned my issue, I am using my router file in backbone to navigate to a different page based on the current page ID through the use of next and previous buttons. However, when I click next or previous once, it works fine, but the second time the button is clicked, the functin is called twice instead of once, and then if I click it another time it seems to be called even more than twice to a point where it seems to go berserk.
Here is my router file:
define([
'jquery',
'underscore',
'backbone',
'views/page',
'models/search',
'views/search',
'text!templates/search.html',
'models/song',
'text!templates/song.html'
], function($, _, Backbone, PageV, SearchM, SearchV, SearchT, SongM, SongT) {
var vent = _.extend({}, Backbone.Events);
var AppRouter = Backbone.Router.extend ({
routes: {
'page/:id': 'showPage',
'search': 'showView' ///:page
}
});
var initialize = function () {
var app_router
app_router = new AppRouter;
console.log('router file hit');
app_router.on('route:showPage', function (id) {
console.log('page rendered');
var songies, collected, songM;
songM = new SongM();
songM.localStorage = new Backbone.LocalStorage("music");
songM.localStorage.findAll().forEach(function (i) {
collected = i;
});
var songPages = Math.ceil(collected.music.length / 25); //10 pages
var start = id * 25;
var songies = collected.music.splice(start, 25);
var titles = {
week: collected.week,
year: collected.year,
channel: collected. channel
};
var page = new PageV({model: songM, collection: songies, vent: vent, titles: titles});
page.render(id);
vent.on('next', advance);
vent.on('previous', moveBack);
var currentId = parseInt(id);
//PROBLEM OCCURS TO THE BOTTOM TWO FUNCTIONS, and they are triggered by the vent.on above.
function moveBack () {
console.log('here is the current ID');
var newPage = 'page/' + (currentId - 1);
if(currentId <= songPages && currentId > 0 ) {
app_router.navigate(newPage, true);
} else {
app_router.navigate('search', true);
}
}
function advance () {
console.log('here is the current ID');
var newPage = 'page/' + (currentId + 1);
console.log(currentId);
console.log(songPages);
console.log(newPage);
if(currentId < songPages && currentId >=0 ) {
app_router.navigate(newPage, true);
} else {
app_router.navigate('search', true);
}
}
});
app_router.on('route:showView', function () {
console.log('search page loaded');
var searchM = new SearchM();
var search = new SearchV({model: searchM, vent: vent}); //
search.render();
vent.on('nextPage', printCons);
function printCons () {
console.log('changing pages');
app_router.navigate('page/0', true);
};
});
Backbone.history.start();
};
return {
initialize: initialize
};
});
Here is the page with the page view:
define([
'jquery',
'underscore',
'backbone',
'models/song',
'collections/songs',
'views/song',
'text!templates/page.html',
'text!templates/song.html'
], function($, _, Backbone, Song, Songs, SongV, PageT, SongT){
var Page = Backbone.View.extend({
el: $("#Sirius"),
events: {
"click .prev": "previous",
"click .next": "next"
},
previous: function () {
this.options.vent.trigger('previous');
},
next: function () {
this.options.vent.trigger('next');
},
render: function () {
var headings = this.options.titles;
var info = {
week: headings.week,
channel: headings.channel,
year: headings.year
}
var pagetemp = _.template( PageT, info);
this.$el.html( pagetemp );
var songColl = this.collection;
var songV = new SongV({collection: songColl});
songV.render();
}
});
return Page;
});
The only problems I can think of is that it somehow remembers the past instance and calls the function on both of them... or else I have no idea why it gets called twice... because if I refresh the page with that id and then click previous or next it does not increment it twice... so it must be in memory or something not sure how to go around it...
The problem is with the following event handler bindings within your app_router.on event handler:
vent.on('next', advance);
vent.on('previous', moveBack);
Each time you show a new route, you are binding those functions to the event aggregator again. You should move both of these bindings outside to the initialize function so you don't bind it multiple times.
Another quick fix, if for some reason moving these bindings outside breaks the functionality, would be to unbind the previous bindings and then bind the event handlers again:
vent.off('next');
vent.on('next', advance);
vent.off('previous');
vent.on('previous', moveBack);
See the Backbone docs for more details regarding this.
The problem is that you're creating a new view every time you change the route, but you're never deleting the old views. You're probably doubling the views every time you click on the next one!
Here's a post that might help:
Disposing of view and model objects in Backbone.js

Create a jQuery special event for content changed

I'm trying to create a jQuery special event that triggers when the content that is bound, changes. My method is checking the content with a setInterval and check if the content has changed from last time. If you have any better method of doing that, let me know. Another problem is that I can't seem to clear the interval. Anyway, what I need is the best way to check for content changes with the event.special.
(function(){
var interval;
jQuery.event.special.contentchange = {
setup: function(data, namespaces) {
var $this = $(this);
var $originalContent = $this.text();
interval = setInterval(function(){
if($originalContent != $this.text()) {
console.log('content changed');
$originalContent = $this.text();
jQuery.event.special.contentchange.handler();
}
},500);
},
teardown: function(namespaces){
clearInterval(interval);
},
handler: function(namespaces) {
jQuery.event.handle.apply(this, arguments)
}
};
})();
And bind it like this:
$('#container').bind('contentchange', function() {
console.log('contentchange triggered');
});
I get the console.log 'content changed', but not the console.log 'contentchange triggered'. So it's obvious that the callback is never triggered.
I just use Firebug to change the content and to trigger the event, to test it out.
Update
I don't think I made this clear enough, my code doesn't actually work. I'm looking for what I'm doing wrong.
Here is the finished code for anyone interested
(function(){
var interval;
jQuery.event.special.contentchange = {
setup: function(){
var self = this,
$this = $(this),
$originalContent = $this.text();
interval = setInterval(function(){
if($originalContent != $this.text()) {
$originalContent = $this.text();
jQuery.event.handle.call(self, {type:'contentchange'});
}
},100);
},
teardown: function(){
clearInterval(interval);
}
};
})();
Thanks to Mushex for helping me out.
also take a look to James similar script (declaring as jquery object method and not as event)
jQuery.fn.watch = function( id, fn ) {
return this.each(function(){
var self = this;
var oldVal = self[id];
$(self).data(
'watch_timer',
setInterval(function(){
if (self[id] !== oldVal) {
fn.call(self, id, oldVal, self[id]);
oldVal = self[id];
}
}, 100)
);
});
return self;
};
jQuery.fn.unwatch = function( id ) {
return this.each(function(){
clearInterval( $(this).data('watch_timer') );
});
};
and creating special event
jQuery.fn.valuechange = function(fn) {
return this.bind('valuechange', fn);
};
jQuery.event.special.valuechange = {
setup: function() {
jQuery(this).watch('value', function(){
jQuery.event.handle.call(this, {type:'valuechange'});
});
},
teardown: function() {
jQuery(this).unwatch('value');
}
};
Anyway, if you need it only as event, you script is nice :)
I know this post/question is a little old, but these days I was behind a similar solution and I found this:
$('#selector').bind('DOMNodeInserted', function(e) {
console.log(e.target);
});
Source: http://naspinski.net/post/Monitoring-a-DOM-Element-for-Modification-with-jQuery.aspx
Hope this help someone!
The finished code in the original question worked for me, thank you! I would just like to note that I am using jquery 1.9.1 and $.event.handle seems to have been removed. I changed the following to get it to work.
jQuery.event.handle.call(self, {type:'contentchange'});
to
jQuery.event.dispatch.call(self, {type:'contentchange'});
maybe you could try Mutation Observer
Here are the code:
mainArea = document.querySelector("#main_area");
MutationObserver = window.MutationObserver;
DocumentObserver = new MutationObserver(function() {
//what you want to run
});
DocumentObserverConfig = {attributes: true, childList: true, characterData: true, subtree: true};
DocumentObserver.observe(mainArea, DocumentObserverConfig);

Categories

Resources