i've wrote a snippet which should start counting a number from 1 to 1000 or pre defined.
Because of needing the script mulitple times i thought it would be a great idea to write it as an object and use it mulitiple times. But i get an error:
Uncaught ReferenceError: root is not defined
(anonymous function)
What did i wrong?
var time = function() {
var root = this;
var i=0;
root.max = 1000;
root.elm = false;
root.runTime = function() {
if(root.elm != false) {
if (i < root.max) {
i++;
root.elm.text(i);
} else {
clearInterval(root.interval);
}
}
this.interval = setInterval('root.runtTime()', 5);
};
};
if($(document).ready(function() {
var countUp= new time();
countUp.max = 1526;
countUp.elm = $("#elm");
countUp.runTime();
});
This is because of the following line:
this.interval = setInterval('root.runtTime()', 5);
Because it's a string it has to be evaluated as a global object.
Change to the following to ensure the same scope:
this.interval = setInterval(root.runtTime, 5);
Also there's a typo (runtTime should be runTime), so change to the following:
this.interval = setInterval(root.runTime, 5);
Finally you're using setInterval which will repeatedly call root.runTime every 5ms. Change to setTimeout if you wish to call this recursively:
this.interval = setTimeout(root.runTime, 5);
Alternatively set up the interval outside of your runTime function:
root.runTime = function() {
if(root.elm != false) {
if (i < root.max) {
i++;
root.elm.text(i);
} else {
clearInterval(root.interval);
}
}
};
this.interval = setInterval(root.runTime, 5);
Also you don't need the if statement around document.ready. This is a callback function which is triggered when the DOM has loaded, and therefore doesn't require an if statement.
$(document).ready(function() {
var countUp= new time();
countUp.max = 1526;
countUp.elm = $("#elm");
countUp.runTime();
});
Related
I am attempting to write a Countdown timer script, and it works with only one instance on the page, but if I add a second one, only the second starts counting.
I discovered that if I load both, but only call the start for the second one, the first one fires. It appears that they are not scoped correctly.
I'm using the new class syntax, so I thought it should work as is, but I'm obviously missing something. I'm hoping someone can help me understand what I'm doing wrong. My primary language is PHP, and I am not as well versed in JS as I'd like to be.
Here is a link to my gist which contains the code: https://gist.github.com/kennyray/b35f4c6640be9539c5d16581de7714e0
class CountdownTimer {
constructor(minutesLabel = null, secondsLabel = null) {
self = this;
this.minutesLabel = minutesLabel;
this.secondsLabel = secondsLabel;
this.totalSeconds = (this.minutesLabel.textContent / 60) + this.secondsLabel.textContent;
this.timer = null;
}
set minutesLabel(value) {
self._minutesLabel = value;
}
set secondsLabel(value) {
self._secondsLabel = value;
}
get minutesLabel() {
return self._minutesLabel;
}
get secondsLabel() {
return self._secondsLabel;
}
setTime() {
self.totalSeconds--;
if (parseInt(self.minutesLabel.innerHTML) == 0 && parseInt(self.secondsLabel.innerHTML) == 0) { self.stopTimer; return;}
if (self.secondsLabel.innerHTML.textContent < 0) { self.secondsLabel.innerHTML = 59 }
if (self.minutesLabel.innerHTML.textContent < 0) { self.minutesLabel.innerHTML = 59 }
self.secondsLabel.innerHTML = self.pad((self.totalSeconds % 60));
self.minutesLabel.innerHTML = self.pad(Math.floor(self.totalSeconds / 60));
}
pad(val) {
var valString = val + "";
if (valString.length < 2) {
return "0" + valString;
} else {
return valString;
}
}
resetTimer() {
clearInterval(self.timer);
self.totalSeconds = 0;
self.secondsLabel.innerHTML = self.pad(self.totalSeconds % 60);
self.minutesLabel.innerHTML = self.pad(parseInt(self.totalSeconds / 60));
}
startTimer() {
self.timer = setInterval(self.setTime, 1000);
}
stopTimer() {
clearInterval(self.timer);
}
}
const t1 = new CountdownTimer(document.getElementById("minutes1"), document.getElementById("seconds1"));
t1.startTimer();
const t2 = new CountdownTimer(document.getElementById("minutes"), document.getElementById("seconds"));
console.log(t1.startTimer() === t2.startTimer());
t2.startTimer();
<label id="minutes1">01</label>:<label id="seconds1">10</label>
<br>
<label id="minutes">00</label>:<label id="seconds">10</label>
You're declaring a global variable self (why the hell do you do this?) that get's overiden. Just use this in a class.
Your startTimer function then needs to be
startTimer() {
this.timer = setInterval(this.setTime.bind(this), 1000);
}
and should maybe check if there's already an interval and clear this.timer completely
startTimer() {
if (this.timer) this.stopTimer();
this.timer = setInterval(this.setTime.bind(this), 1000);
}
stopTimer() {
clearInterval(this.timer);
this.timer = null;
}
It really boils down to this line
self = this;
By not including the keyword var you elevate that to global scope. If you want to use self instead of this in the ctor (which is perfectly fine) just prefix it with var:
var self = this;
I got these variables
var moneycount = 0;
var b1 = document.createElement("IMG");
function update() {
document.getElementById('money').innerHTML = moneycount;
}
And functions for saving/loading
function save() {
localStorage.setItem("moneycount", moneycount);
};
function load() {
moneycount = localStorage.getItem("moneycount");
moneycount = parseInt(moneycount);
update();
};
This function works as intended but wont update with localStorage
function add() {
moneycount = moneycount + 1
if (moneycount >= 1) {
document.getElementById('badge1').appendChild(b1);
}
update();
}
It seems like var moneycount = 0 still is 0 after I load localStorage because if (moneycount >= 1) is not working. The document element is still displaying a bigger number after loading localStorage. Any ideas on how I can store my add() function?
Edit:
Found a temporary solution with setting setInterval(save, 5000); and adding <body onload="load();"> and calling it on refresh.
that is because you do not update the localStorage instead you just update the html element: should do it as followed:
var moneycount = 0;
var b1 = document.createElement("IMG");
function update() {
document.getElementById('money').innerHTML = moneycount;
localStorage.setItem("moneycount", moneycount);
}
function save() {
localStorage.setItem("moneycount", moneycount);
};
function add() {
localStorage.getItem("moneycount")+= 1;
if (moneycount >= 1) {
document.getElementById('badge1').appendChild(b1);
}
update();
}
The reason is (like mentioned in the comments) that your load() function also saves the localStorage instead of retrieving the value.
Your load() function should look like this:
function load() {
moneycount = localStorage.getItem("moneycount");
update();
};
I want to make a delay inside my for loop, but it won't really work.
I've already tried my ways that are on stackoverflow, but just none of them work for what I want.
This is what I've got right now:
var iframeTimeout;
var _length = $scope.iframes.src.length;
for (var i = 0; i < _length; i++) {
// create a closure to preserve the value of "i"
(function (i) {
$scope.iframeVideo = false;
$scope.iframes.current = $scope.iframes.src[i];
$timeout(function () {
if ((i + 1) == $scope.iframes.src.length) {
$interval.cancel(iframeInterval);
/*Change to the right animation class*/
$rootScope.classess = {
pageClass: 'nextSlide'
}
currentId++;
/*More information about resetLoop at the function itself*/
resetLoop();
} else {
i++;
$scope.iframes.current = $scope.iframes.src[i];
}
}, $scope.iframes.durationValue[i]);
}(i));
}
alert("done");
This is what I want:
First of all I got an object that holds src, duration and durationValue.
I want to play both video's that I have in my object.
I check how many video's I've got
I make iframeVideo visible (ngHide)
I insert the right <iframe> tag into my div container
It starts the $timeout with the right duration value
If that's done, do the same if there is another video. When it was the last video it should fire some code.
I hope it's all clear.
I've also tried this:
var iframeInterval;
var i = 0;
$scope.iframeVideo = false;
$scope.iframes.current = $scope.iframes.src[i];
iframeInterval = $interval(function () {
if ((i + 1) == $scope.iframes.src.length) {
$interval.cancel(iframeInterval);
/*Change to the right animation class*/
$rootScope.classess = {
pageClass: 'nextSlide'
}
currentId++;
/*More information about resetLoop at the function itself*/
resetLoop();
} else {
i++;
$scope.iframes.current = $scope.iframes.src[i];
}
}, $scope.iframes.durationValue[i])
Each $timeout returns a different promise. To properly cancel them, you need to save everyone of them.
This example schedules several subsequent actions starting at time zero.
var vm = $scope;
vm.playList = []
vm.playList.push({name:"video1", duration:1200});
vm.playList.push({name:"video2", duration:1300});
vm.playList.push({name:"video3", duration:1400});
vm.playList.push({name:"video4", duration:1500});
vm.watchingList=[];
var timeoutPromiseList = [];
vm.isPlaying = false;
vm.start = function() {
console.log("start");
//ignore if already playing
if (vm.isPlaying) return;
//otherwise
vm.isPlaying = true;
var time = 0;
for (var i = 0; i < vm.playList.length; i++) {
//IIFE closure
(function (i,time) {
console.log(time);
var item = vm.playList[i];
var p = $timeout(function(){playItem(item)}, time);
//push each promise to list
timeoutPromiseList.push(p);
})(i,time);
time += vm.playList[i].duration;
}
console.log(time);
var lastPromise = $timeout(function(){vm.stop()}, time);
//push last promise
timeoutPromiseList.push(lastPromise);
};
Then to stop, cancel all of the $timeout promises.
vm.stop = function() {
console.log("stop");
for (i=0; i<timeoutPromiseList.length; i++) {
$timeout.cancel(timeoutPromiseList[i]);
}
timeoutPromiseList = [];
vm.isPlaying = false;
};
The DEMO on PLNKR.
$timeout returns promise. You can built a recursive chain of promises like this, so every next video will play after a small amount of time.
I am using google maps and i am trying to put a pause in execution to prevent QUERY_LIMIT usage issue. My function that plots the addresses looks like this.
The code works, however i want to try setTimeout or setInterval to see if its going to look better on UI.
How do i call it, what should be the first argument?
Thanx alot.
vLocations = [];
for (var i = 0; i < vAddresses.length; i++) {
//pause to prevent OVER_QUERY_LIMIT issue
//geocode "free" usage limit is 5 requests per second
//setTimeout(PlotAddressesAsUnAssigned, 1000);
//sleep(500);
//this will resolve the address and store it in vLocations
AddWaypointAndUnassigned(vAddresses[i]);
var z = i % 4;
if (z==0 && i != 0) {
//sleep after every 5th geocode call
//alert('going to sleep...i: ' + i);
//sleep(3000);
}
}
Doing a pause (asynchronous execution) inside a loop (synchronous) will usually result in a lot of trouble.
You can use recursive calls that are done only when a timeout ends.
var vLocations = [];
// Manages the timeout and recursive calls
function AddWaypointAndUnassignedWithPause(index){
setTimeout(function(){
// When the timeout expires, we process the data, and start the next timeout
AddWaypointAndUnassigned(vAddresses[index]);
// Some other code you want to execute
var z = i % 4;
if (z==0 && i != 0) {
//sleep after every 5th geocode call
//alert('going to sleep...i: ' + i);
//sleep(3000);
}
if(index < vAddresses.length-1)
AddWaypointAndUnassignedWithPause(++index);
}, 1000);
}
// Start the loop
AddWaypointAndUnassignedWithPause(0);
JSFiddle example.
Try this, hope this will help
vLocations = [];
for (var i = 0; i < vAddresses.length; i++) {
//pause to prevent OVER_QUERY_LIMIT issue
setTimeout(function(){
//this will resolve the address and store it in vLocations
AddWaypointAndUnassigned(vAddresses[i]);
}, 500);
var z = i % 4;
if (z==0 && i != 0) {
//sleep after every 5th geocode call
//alert('going to sleep...i: ' + i);
//sleep(3000);
}
}
What about a waiting line, thats fired when an item is added and stopped when there are no items left.
With setTimeout:
var INTERVAL = 1000 / 5;
var to = null;
var vLocations = [];
function addAddress(vAddress) {
vLocations.push(vAddress);
startTimeout();
}
function startTimeout() {
if( to === null ) {
to = setTimout(processLocation, INTERVAL);
}
}
function processLocation() {
if( vLocations.length ) {
var vAddress = vLocations.shift();
AddWaypointAndUnassigned(vAddress);
to = setTimout(processLocation, INTERVAL);
} else {
to = null;
}
}
With setInterval:
var INTERVAL = 1000 / 5;
var to = null;
var vLocations = [];
function addAddress(vAddress) {
vLocations.push(vAddress);
startInterval();
}
function startInterval() {
if( to === null ) {
to = setInterval(processLocation, INTERVAL);
}
}
function processLocation(cb) {
if( vLocations.length ) {
var vAddress = vLocations.shift();
AddWaypointAndUnassigned(vAddress);
} else
clearInterval(to);
to = null;
}
}
I have a banner rotator that refreshes every 60 seconds however I would also like each banner to be displayed for 20 seconds as there are 3 of them. I know where to put the setInterval code as indicated by my comment but I can't seem to get it to work. Any help or suggestions are very much appreciated.
var banners1 = {
0:
{
'href': 'http://www.example.com/banner1.html',
'src': 'http://www.example.com/banner1.gif'
},
1:
{
'href': 'http://www.example.com/banner2.html',
'src': 'http://www.example.com/banner2.gif'
},
2:
{
'href': 'http://www.example.com/banner3.html',
'src': 'http://www.example.com/banner3.gif'
}
}
window.addEvent('domready', function() {
function banner1()
{
var banner = $('banner1');
var banner1Link = $('banner1').getElement('a');
var banner1Image = $('banner1').getElement('img');
for (var keys in banners1) {
var object = banners1[keys];
for (var property in object) {
if (property == 'href') {
var href = object[property];
}
if (property == 'src') {
var src = object[property];
}
}
banner1Link.setProperty('href', href);
banner1Image.setProperty('src', src);
console.log(href);
console.log(src);
/** wait 20 seconds **/
}
}
var periodical = banner1.periodical(60000);
});
Try this instead:
function banner1()
{
var banner = $('banner1');
var banner1Link = $('banner1').getElement('a');
var banner1Image = $('banner1').getElement('img');
var delay = 0;
for (var keys in banners1) {
var func = (function(key) { return function() {
var object = banners1[key];
for (var property in object) {
if (property == 'href') {
var href = object[property];
}
if (property == 'src') {
var src = object[property];
}
}
banner1Link.setProperty('href', href);
banner1Image.setProperty('src', src);
console.log(href);
console.log(src);
}; })(keys);
setTimeout(func, delay);
delay += 20000;
}
}
JavaScript does not have a sleep-style function, because the document will not respond while JavaScript is running. This means the user would not be able to interact with the page while the script is sleeping.
This approach schedules all three updates in one shot. They will execute at 0 seconds (immediately), 20 seconds, and 40 seconds from the time the script runs.
you could use the javascript timeout
setTimeout('', 20000);
This would be added right after the 20 sec comment. The first variable is any code/function you want called. and the second is the amount of time required before it is called in milliseconds (20 secs).
EDITED ANSWER
<script type="text/javascript">
var imgs1 = new Array("http://www.omniadiamond.com/images/jwplayer/enel_culture.png","http://www.omniadiamond.com/images/jwplayer/evas_srm.png","http://www.omniadiamond.com/images/jwplayer/jackolantern.png");
var lnks1 = new Array("http://test.com","http://test.com","http://test.com");
var alt1 = new Array("test","test","test");
var currentAd1 = 0;
var imgCt1 = 3;
function cycle1() {
if (currentAd1 == imgCt1) {
currentAd1 = 0;
}
var banner1 = document.getElementById('adBanner1');
var link1 = document.getElementById('adLink1');
banner1.src=imgs1[currentAd1]
banner1.alt=alt1[currentAd1]
document.getElementById('adLink1').href=lnks1[currentAd1]
currentAd1++;
}
window.setInterval("cycle1()",20000);
</script>
<a href="http://test.com" id="adLink1" target="_top">
<img src="http://www.omniadiamond.com/images/jwplayer/enel_culture.png" id="adBanner1" border="0"></a>
This does cycle through all three in 60 secs at a 20 sec interval. and can be viewed at http://jsfiddle.net/jawilliams346614/wAKGp/ (it is set to a 1 sec interval)