I have making a text-based space rpg game. In the battle system, the player and AI take turns firing at each other. I can't figure out how to make the system loop with a while loop without making the browser crash via infinite loop. Here is my code:
function battle(enemy) {
var battleOver = false;
console.log(enemy.name + " appears!");
//problem loop here.
while(battleOver === false){
console.log("This enemy has " + enemy.health + " health.");
for (var i = 0; i < userWeapons.length; i++) {
var ibumped = i + 1;
console.log("Press " + ibumped + " to fire the " + userWeapons[i].name + ".");
}
var weaponChosen;
setTimeout(function() {
var weaponChoice = prompt("Which weapon do you choose?");
switch (weaponChoice) {
case 1:
weaponChosen = userWeapons[0];
console.log(userWeapons[0].name + " chosen.");
break;
case 2:
weaponChosen = userWeapons[1];
console.log(userWeapons[1].name + " chosen.");
break;
default:
weaponChosen = userWeapons[0];
console.log(userWeapons[0].name + " chosen.");
};
}, 1000);
setTimeout(function() {
if (enemy.shields > 0 && weaponChosen.ignoreShield === false) {
enemy.shields = enemy.shields - weaponChosen.damage;
weaponChosen.fire(enemy);
if (enemy.shields < 0) {
enemy.health = enemy.health + enemy.shields;
console.log("Enemy shields destroyed and enemy took " + -1 * enemy.shields + " damage!")
} else {
console.log("Enemy shields have been reduced to " + enemy.shields + ".");
}
} else {
enemy.health = enemy.health - weaponChosen.damage;
weaponChosen.fire(enemy);
console.log("Enemy takes " + weaponChosen.damage + " damage!");
}
if (enemy.health <= 0 && battleOver === false) {
console.log("Enemy destroyed!");
battleOver = true;
}
}, 3000);
setTimeout(function() {
if (enemy.health > 0 && battleOver === false) {
if (enemy.weapons != null) {
console.log(enemy.weapons.name + " fired at you.");
health = health - enemy.weapons.damage;
console.log("You have " + health + " health left.");
if (health <= 0) {
console.log("Game over... You were destroyed");
battleOver = true;
}
} else {
console.log("The enemy did nothing...");
}
};
}, 5000);
}
}
All help is appreciated!
Things start getting very tricky when you use setTimeout. Most games will have a "main game loop" that runs 60 times a second.
Try using a main game loop and cooldowns.
Here's an example of how you could restructure the program.
var timeForOneFrame = 1000/60 // 60 Frames per second
var enemy;
var battleOver;
function initGame() {
/*
Initialize the enemy etc.
*/
}
function gameLoop() {
battle(enemy);
}
function battle(enemy) {
/*
Do all the battle stuff, this function is called 60 times a second-
there's no need for setTimeout!
*/
// If you want an enemy to only be able to attack every 2 seconds, give them
// a cooldown...
if (enemy.cooldown <= 0) {
// Attack!
enemy.cooldown += 2000
} else {
enemy.cooldown -= timeForOneFrame
}
if (enemy.health <= 0 && battleOver === false) {
console.log("Enemy destroyed!");
battleOver = true;
clearInterval(gameLoopInterval)
}
}
initGame();
var gameLoopInterval = setInterval(gameLoop, timeForOneFrame);
A while loop blocks the whole page until it ends, and as your loop never exits its infinite. You may replace it with a high speed interval:
const loop = setInterval( function(){
if( battleOver ) return clearInterval(loop);
console.log("This enemy has " + enemy.health + " health.");
for (var i = 0; i < userWeapons.length; i++) {
console.log("Press " + (i + 1) + " to fire the " + userWeapons[i].name + ".");
}
},10);
I would take that loop out and use a recursive call on the end of the interactions.
Let's say:
function battle(enemy) {
//All your code here
if (HeroAlive) {
battle(enemy);
}
}
Related
When you run this script it shows the HP for both of the pokemon when you press 1 and click enter it subtracts your attack hit points to the enemies hit points. When you or the ememy hits 0 or less than 0 hit points it is supposed to stop and just show who won in the console log. Instead it takes an extra hit to for it to show the message.
So if you are at -10 hp it takes one more hit.
let firstFight = false;
while (!firstFight) {
let fightOptions = prompt("1. Fight, 2.Items, 3.Potions " + wildPokemon[0].name + ":" + wildPokemon[0].hp + " " + pokeBox[0].name + ":" + pokeBox[0].hp);
if (fightOptions == 1) {
if (!firstFight) {
if (wildPokemon[0].hp <= 0) {
console.log("You have won!");
firstFight = true;
} else {
let attack1 = wildPokemon[0].hp -= pokeBox[0].attack.hp;
console.log(wildPokemon[0].hp);
}
if (pokeBox[0].hp <= 0) {
console.log(wildPokemon[0] + " has killed you");
firstFight = true;
} else {
let attack2 = pokeBox[0].hp -= wildPokemon[0].attack.hp;
console.log(pokeBox[0].hp);
}
}
} else if (fightOptions == 2) {
} else if (fightOptions == 3) {
} else {
}
}
Are there any ways I can make this code more efficient?
you can simply add another if condition to check whether life of the player is still greater then '0' or less then '0' in the same turn like this.
in this way you don't have to go for next turn to check for the players life plus it rids off the extra conditional statements...
if (fightOptions == 1) {
let attack1 = wildPokemon[0].hp -= pokeBox[0].attack.hp;
console.log(wildPokemon[0].hp);
if (wildPokemon[0].hp <= 0) {
console.log("You have won!");
firstFight = true;
}
if (!firstFight){
let attack2 = pokeBox[0].hp -= wildPokemon[0].attack.hp;
console.log(pokeBox[0].hp);
if (pokeBox[0].hp <= 0) {
console.log(wildPokemon[0] + " has killed you");
firstFight = true;
}
}
}
The problem is, the points are getting subtracted after you check if they are equal to or below zero. Here is a way you can check before:
let firstFight = false;
while (!firstFight) {
let fightOptions = prompt("1. Fight, 2.Items, 3.Potions " + wildPokemon[0].name + ":" + wildPokemon[0].hp + " " + pokeBox[0].name + ":" + pokeBox[0].hp);
if (fightOptions == 1) {
wildPokemon[0].hp -= pokeBox[0].attack.hp;
if (wildPokemon[0].hp <= 0) {
console.log("You have won!");
firstFight = true;
} else {
console.log(wildPokemon[0].hp);
}
pokeBox[0].hp -= wildPokemon[0].attack.hp;
if (!firstFight && pokeBox[0].hp <= 0) {
console.log(wildPokemon[0] + " has killed you");
firstFight = true;
} else {
console.log(pokeBox[0].hp);
}
} else if (fightOptions == 2) {
} else if (fightOptions == 3) {
} else {
}
}
While loops stops when the condition is false, in you case, you set it to not false, it is not stopping because you did not explicitly determine it. There are 2 ways you can do.
1st:
while(!firstFight == false)
2nd:
var firstFight = true;
while(firstFight)
then set the firstFight to false inside your if else statements.
I have a javascript function to countdown a timer. So I want to add pause option to this function. I tried this way,
function countdownTimeStart() {
var el = document.getElementById('demo');
var pause= document.getElementById('pause');
var time = [10,10,10];
var x = setInterval(function () {
var hours = time[0];
var minutes = time[1];
var seconds = time[2]--;
if (time[2] == -1) {
time[1]--;
time[2] = 59 }
function pauseTimer() {
savedTime = time;
clearInterval(x);
}
pause.addEventListener( 'click', pauseTimer);
if( seconds == 0 && minutes == 0 && hours == 0 ){
clearInterval(x);
el.innerHTML = "00:00:00";
} else if (seconds < 10) {
el.innerHTML = hours + ": " + minutes + ": " + "0" + seconds + " ";
} else {
el.innerHTML = hours + ": " + minutes + ": " + seconds + " ";
}
}, 1000);
}
countdownTimeStart();
<button id="pause" class="pause">Pause</button>
<div id="demo"></div>
The countdown timer working correctly. But the pause option not working. So how can I correct this script. Can someone help me.
Although your code is working, I would like to note a couple of things: adding your pause handler inside your interval isn't a good idea, you will be adding a pause handler every interval, so in the end you are just stacking up the amount of functions to handle when clicking. I have made your button toggle and separated out the event listener into a handler function so you can attach it to any button. These changes will keep your code working fluently while also making it easier to understand:
function initCountdown() {
function event_click( event ){
// If our interval is null, we need to start the counter
// And also change the innerText so its obvious what the button will do next
if( interval === null ){
start();
event.target.innerText = 'pause';
} else {
pause();
event.target.innerText = 'start';
}
}
function start(){
// First use pause() to be sure all intervals are cleared
// it prevents them from doubling up
pause();
interval = setInterval( count, 1000 );
}
function pause() {
clearInterval( interval );
interval = null;
}
function count(){
// By doing this before declaring your variables
// you make it so the variables actually hold the new calculated values.
time[2]--;
if( time[2] == -1 ){
time[1]--;
time[2] = 59;
}
// Lets use some cool new syntax here to reduce the amount of code needed
// this will destructure an array assigning their indexed values to the index of the variable
var [ hours, minutes, seconds ] = time;
if( seconds == 0 && minutes == 0 && hours == 0 ){
clearInterval( interval );
}
// We always want to print something, and if the values are 0
// the output is still the same, so lets seperate that.
if (seconds < 10) {
outputElement.innerHTML = hours + ": " + minutes + ": " + "0" + seconds + " ";
} else {
outputElement.innerHTML = hours + ": " + minutes + ": " + seconds + " ";
}
}
// Lets also clearly name our things.
var outputElement = document.getElementById('demo');
var toggleElement = document.getElementById('toggle');
var interval = null;
var time = [10,10,10];
// Add event listener once
toggleElement.addEventListener( 'click', event_click );
toggleElement.click();
}
initCountdown();
<button id="toggle">start</button>
<div id="demo"></div>
Update Adding a cancel button:
function initCountdown() {
function event_click_cancel( event ){
pause();
time = [ 0, 0, 0 ];
print();
}
function event_click_startpause( event ){
// If our interval is null, we need to start the counter
// And also change the innerText so its obvious what the button will do next
if( interval === null ){
start();
event.target.innerText = 'pause';
} else {
pause();
event.target.innerText = 'start';
}
}
function start(){
// First use pause() to be sure all intervals are cleared
// it prevents them from doubling up
pause();
interval = setInterval( count, 1000 );
}
function pause() {
clearInterval( interval );
interval = null;
}
function print(){
// I have separated out the print function as we want to use it
// in the count and the cancel function
var [ hours, minutes, seconds ] = time;
if( seconds == 0 && minutes == 0 && hours == 0 ){
clearInterval( interval );
}
if (seconds < 10) {
outputElement.innerHTML = hours + ": " + minutes + ": " + "0" + seconds + " ";
} else {
outputElement.innerHTML = hours + ": " + minutes + ": " + seconds + " ";
}
}
function count(){
// By doing this before declaring your variables
// you make it so the variables actually hold the new calculated values.
time[2]--;
if( time[2] == -1 ){
time[1]--;
time[2] = 59;
}
print();
}
// Lets also clearly name our things.
var outputElement = document.getElementById('demo');
var toggleElement = document.getElementById('toggle');
var cancelElement = document.getElementById('cancel');
var interval = null;
var time = [10,10,10];
// Add event listener once
toggleElement.addEventListener( 'click', event_click_startpause );
toggleElement.click();
cancelElement.addEventListener( 'click', event_click_cancel );
}
initCountdown();
<button id="toggle">start</button>
<button id="cancel">cancel</button>
<div id="demo"></div>
function start() {
var arrNums = [18,23,20,17,21,18,22,19,18,20];
var lowValue, highValue, index, count;
lowValue = Number(document.getElementById("lowValue").value);
highValue = Number(document.getElementById("highValue").value);
index = 0;
count = 0;
document.getElementById("msg").innerHTML+="The values in the array are: ";
while(index < arrNums.length) {
document.getElementById("msg").innerHTML+= arrNums[index] + " ";
index++;
}
index = 0;
if(validateLowAndHigh(lowValue, highValue) == "true") {
while(index < arrNums.length) {
if(arrNums[index] >= lowValue && arrNums[index] <= highValue) {
count++;
}
index++;
}
document.getElementById("msg").innerHTML+= "<br/> There are " + count + " values that exist in this range";
}
}
function validateLowAndHigh(low, high) {
if(low <= high) {
return("true");
} else {
document.getElementById("msg").innerHTML+= "<br/> Low value must be less than or equal to the high vaules";
return("false");
}
}
function clearOutput() {
document.getElementById("msg").innerHTML=" ";
}
I was told that I am not allowed to use true/false as strings, only as boolean. How do I go about fixing this? I am not sure how this works, thank you for the help.
Change your code as like this
Change return true instead of return ("true") or ("false")
And change if (validateLowAndHigh(lowValue, highValue)) instead of if (validateLowAndHigh(lowValue, highValue) == 'true')
function start() {
var arrNums = [18, 23, 20, 17, 21, 18, 22, 19, 18, 20];
var lowValue, highValue, index, count;
lowValue = Number(document.getElementById("lowValue").value);
highValue = Number(document.getElementById("highValue").value);
index = 0;
count = 0;
document.getElementById("msg").innerHTML += "The values in the array are: ";
while (index < arrNums.length) {
document.getElementById("msg").innerHTML += arrNums[index] + " ";
index++;
}
index = 0;
if (validateLowAndHigh(lowValue, highValue)) {
while (index < arrNums.length) {
if (arrNums[index] >= lowValue && arrNums[index] <= highValue) {
count++;
}
index++;
}
document.getElementById("msg").innerHTML += "<br/> There are " + count + " values that exist in this range";
}
}
function validateLowAndHigh(low, high) {
if (low <= high) {
return true;
} else {
document.getElementById("msg").innerHTML += "<br/> Low value must be less than or equal to the high vaules";
return false;
}
}
function clearOutput() {
document.getElementById("msg").innerHTML = " ";
}
Instead of using return("true") and return("false"), use return true and return false
Also, replace if(validateLowAndHigh(lowValue, highValue) == "true") with
if(validateLowAndHigh(lowValue, highValue))
OR
if(validateLowAndHigh(lowValue, highValue) == true)
I've been working on a simple script I can use for a JavaScript/HTML RPG game, but when building the battle script, I no getting any console output. I have tried everything I can think of but still no luck and I started to wonder if I've made a coding error somewhere.
var player = {hp:100, attack:10, defence:10, speed:20};
var enemy = {hp:100, attack:10, defence:10, speed:20};
function playeratk(){
console.log("enemy " + enemy.hp);
var pAtk = Math.floor(Math.random() * player.attack) + 1;
console.log(pAtk);
enemy.hp = enemy.hp - pAtk;
console.log("enemy " + enemy.hp);
console.log("-----");
}
function enemyatk(){
console.log("player " + player.hp);
var eAtk = Math.floor(Math.random() * enemy.attack) + 1;
console.log(eAtk);
player.hp = player.hp - eAtk;
console.log("player " + player.hp);
console.log("-----");
}
function battle() {
if (player.hp < 1) {
console.log("Enemy wins");
}
else if (enemy.hp < 1){
console.log("Player Wins");
}
else {
var pSpeed = Math.floor(Math.random() * player.speed) + 1;
var eSpeed = Math.floor(Math.random() * enemy.speed) + 1;
if (player.speed > enemy.speed){
playeratk();
enemyatk();
battle();
}
else if(enemy.speed > player.speed){
enemyatk();
playeratk();
battle();
}
else {
playeratk();
enemyatk();
battle();
}
}
}
You have to call the battle() function at least once outside of itself. Maybe at the end of the script.
in a related question I have posted this code. It almost works, but the counter doesn't.
Can we fix it? (no jQuery, please)
<script type="text/javascript">
var intervals = [];
var counters = {
"shoes":{"id":"shoe1","minutes":1,"seconds":5},
"trousers":{"id":"trouser1","minutes":10,"seconds":0}
}; // generate this on the server and note there is no comma after the last item
window.onload = function() {
for (var el in counters) { countdown(counters[el]) };
}
function countdown(element) {
intervals[element.id] = setInterval(function() {
var el = document.getElementById(element.id);
var minutes = element.minutes;
var seconds = element.seconds;
if(seconds == 0) {
if(minutes == 0) {
el.innerHTML = "countdown's over!";
clearInterval(intervals[element.id]);
return;
} else {
minutes--;
seconds = 60;
}
}
if(minutes > 0) {
var minute_text = minutes + (minutes > 1 ? ' minutes' : ' minute');
} else {
var minute_text = '';
}
var second_text = seconds > 1 ? 'seconds' : 'second';
el.innerHTML = minute_text + ' ' + seconds + ' ' + second_text + ' remaining';
seconds--;
}, 1000);
}
</script>
shoes: <span id="shoe1"></span><br />
trousers: <span id="trouser1"></span><br />
You just need to take the minutes and seconds variable declarations out of the inner function, like this:
function countdown(element) {
var minutes = element.minutes;
var seconds = element.seconds;
intervals[element.id] = setInterval(function() {
var el = document.getElementById(element.id);
if(seconds == 0) {
if(minutes == 0) {
el.innerHTML = "countdown's over!";
clearInterval(intervals[element.id]);
return;
} else {
minutes--;
seconds = 60;
}
}
if(minutes > 0) {
var minute_text = minutes + (minutes > 1 ? ' minutes' : ' minute');
} else {
var minute_text = '';
}
var second_text = seconds > 1 ? 'seconds' : 'second';
el.innerHTML = minute_text + ' ' + seconds + ' ' + second_text + ' remaining';
seconds--;
}, 1000);
}
When you call countdown, you want to fetch the initial values for each countdown and then slowly decrease them (closure makes sure that the values will stay available as long as the anonymous function requires them). What you were doing before was reset the countdown values at the start of each tick, so the countdown never had a chance to... well, count down.
Update:
If you need to update the values inside window.counters while the countdowns are active (although I don't see why you would want to do that; if you want to do anything meaningful with the "current" countdown values, just do it inside the anonymous function), you can simply add this at the end:
var second_text = seconds > 1 ? 'seconds' : 'second';
el.innerHTML = minute_text + ' ' + seconds + ' ' + second_text + ' remaining';
seconds--;
// ADD THIS:
element.minutes = minutes;
element.seconds = seconds;
Please check this
<html>
<body>
<script type="text/javascript">
var intervals = [];
var counters = {
"shoes":{"id":"shoe1","minutes":1,"seconds":5},
"trousers":{"id":"trouser1","minutes":10,"seconds":0}
}; // generate this on the server and note there is no comma after the last item
window.onload = function() {
for (var el in counters) { countdown(counters[el]) };
}
function countdown(element) {
intervals[element.id] = setInterval(function() {
var el = document.getElementById(element.id);
var minutes = element.minutes;
var seconds = element.seconds;
if(seconds == 0) {
if(minutes == 0) {
el.innerHTML = "countdown's over!";
clearInterval(intervals[element.id]);
return;
} else {
minutes--;
seconds = 60;
}
}
if(minutes > 0) {
var minute_text = minutes + (minutes > 1 ? ' minutes' : ' minute');
} else {
var minute_text = '';
}
var second_text = seconds > 1 ? 'seconds' : 'second';
el.innerHTML = minute_text + ' ' + seconds + ' ' + second_text + ' remaining';
seconds--;
element.seconds = seconds;
element.minutes = minutes;
}, 1000);
}
</script>
shoes: <span id="shoe1"></span><br />
trousers: <span id="trouser1"></span><br />
</body>
</html>
Working solution is here.
EDIT: There was a minor issue with the script. I've fixed it.
After recalculating the seconds and minutes you have to set the new values back in the element object.
You're decrementing the wrong variable inside your interval callback. Instead of doing seconds-- and minutes-- you need to reference the element members.
intervals[element.id] = setInterval(function() {
var el = document.getElementById(element.id);
if(element.seconds == 0) {
if(element.minutes == 0) {
el.innerHTML = "countdown's over!";
clearInterval(intervals[element.id]);
return;
} else {
element.minutes--;
element.seconds = 60;
}
}
if(element.minutes > 0) {
var minute_text = element.minutes + (element.minutes > 1 ? ' minutes' : ' minute');
} else {
var minute_text = '';
}
var second_text = element.seconds > 1 ? 'seconds' : 'second';
el.innerHTML = minute_text + ' ' + element.seconds + ' ' + second_text + ' remaining';
element.seconds--;
}, 1000);
All of the answers you've been given so far fail to handle one simple fact: setInterval does not happen reliably at the time you set. It can be delayed or even skipped if the browser is busy doing something else. This means you can't rely on your update function to decrement the number of seconds left, you'll start drifting from reality very quickly. Maybe that's okay, maybe not. There's no need for it, in any case: Just calculate when the timeout occurs (e.g., a minute and five seconds from now for your "shoes" counter), and then at each update, calculate how long you have left. That way if an interval got dropped or something, you won't drift; you're deferring the computer's clock.
Here's a procedural version:
// Note that to prevent globals, everything is enclosed in
// a function. In this case, we're using window.onload, but
// you don't have to wait that long (window.onload happens
// *very* late, after all images are loaded).
window.onload = function() {
// Our counters
var counters = {
"shoes":{
"id": "shoe1",
"minutes": 1,
"seconds":5
},
"trousers":{
"id": "trouser1",
"minutes": 10,
"seconds":0
}
};
// Start them
var name;
for (name in counters) {
start(counters[name]);
}
// A function for starting a counter
function start(counter) {
// Find the time (in ms since The Epoch) at which
// this item expires
counter.timeout = new Date().getTime() +
(((counter.minutes * 60) + counter.seconds) * 1000);
// Get this counter's target element
counter.element = document.getElementById(counter.id);
if (counter.element) {
// Do the first update
tick(counter);
// Schedule the remaining ones to happen *roughly*
// every quarter second. (Once a second will look
// rough).
counter.timer = setInterval(function() {
tick(counter);
}, 250);
}
}
// Function to stop a counter
function stop(counter) {
if (counter.timer) {
clearInterval(counter.timer);
delete counter.timer;
}
delete counter.element;
}
// The function called on each "tick"
function tick(counter) {
var remaining, str;
// How many seconds left?
remaining = Math.floor(
(counter.timeout - new Date().getTime()) / 1000
);
// Same as last time?
if (remaining != counter.lastRemaining) {
// No, do an update
counter.lastRemaining = remaining;
if (remaining <= 0) {
// Done! Stop the counter.
str = "done";
alert("Stopped " + counter.id);
stop(counter);
}
else {
// More than a minute left?
if (remaining >= 120) {
// Yup, output a number
str = Math.floor(remaining / 60) + " minutes";
}
else if (remaining >= 60) {
// Just one minute left
str = "one minute";
}
else {
// Down to seconds!
str = "";
}
// Truncate the minutes, just leave seconds (0..59)
remaining %= 60;
// Any seconds?
if (remaining > 0) {
// Yes, if there were minutes add an "and"
if (str.length > 0) {
str += " and ";
}
// If only one second left, use a word; else,
// a number
if (remaining === 1) {
str += "one second";
}
else {
str += Math.floor(remaining) + " seconds";
}
}
// Finish up
str += " left";
}
// Write to the element
counter.element.innerHTML = str;
}
}
};
Live example
Here's an OOP version (using the module pattern so Counter can have named functions and a private one [tick]):
// A Counter constructor function
var Counter = (function() {
var p;
// The actual constructor (our return value)
function Counter(id, minutes, seconds) {
this.id = id;
this.minutes = minutes || 0;
this.seconds = seconds || 0;
}
// Shortcut to the prototype
p = Counter.prototype;
// Start a counter
p.start = Counter_start;
function Counter_start() {
var me = this;
// Find the time (in ms since The Epoch) at which
// this item expires
this.timeout = new Date().getTime() +
(((this.minutes * 60) + this.seconds) * 1000);
// Get this counter's target element
this.element = document.getElementById(this.id);
if (this.element) {
// Do the first update
tick(this);
// Schedule the remaining ones to happen *roughly*
// every quarter second. (Once a second will look
// rough).
this.timer = setInterval(function() {
tick(me);
}, 250);
}
}
// Stop a counter
p.stop = Counter_stop;
function Counter_stop() {
if (this.timer) {
clearInterval(this.timer);
delete this.timer;
}
delete this.element;
}
// The function we use to update a counter; not exported
// on the Counter prototype because we only need one for
// all counters.
function tick(counter) {
var remaining, str;
// How many seconds left?
remaining = Math.floor(
(counter.timeout - new Date().getTime()) / 1000
);
// Same as last time?
if (remaining != counter.lastRemaining) {
// No, do an update
counter.lastRemaining = remaining;
if (remaining <= 0) {
// Done! Stop the counter.
str = "done";
alert("Stopped " + counter.id);
stop(counter);
}
else {
// More than a minute left?
if (remaining >= 120) {
// Yup, output a number
str = Math.floor(remaining / 60) + " minutes";
}
else if (remaining >= 60) {
// Just one minute left
str = "one minute";
}
else {
// Down to seconds!
str = "";
}
// Truncate the minutes, just leave seconds (0..59)
remaining %= 60;
// Any seconds?
if (remaining > 0) {
// Yes, if there were minutes add an "and"
if (str.length > 0) {
str += " and ";
}
// If only one second left, use a word; else,
// a number
if (remaining === 1) {
str += "one second";
}
else {
str += Math.floor(remaining) + " seconds";
}
}
// Finish up
str += " left";
}
// Write to the element
counter.element.innerHTML = str;
}
}
// Return the constructor function reference. This
// gets assigned to the external var, which is how
// everyone calls it.
return Counter;
})();
// Note that to prevent globals, everything is enclosed in
// a function. In this case, we're using window.onload, but
// you don't have to wait that long (window.onload happens
// *very* late, after all images are loaded).
window.onload = function() {
// Our counters
var counters = {
"shoes": new Counter("shoe1", 1, 5),
"trousers": new Counter("trouser1", 10, 0)
};
// Start them
var name;
for (name in counters) {
counters[name].start();
}
};
Live example
More about closures here.
I think it'd make your code a lot cleaner and save you some ifs if you would keep the timeout in code as seconds, rather than minutes and seconds:
var intervals = [];
var counters = {
"shoes":{"id":"shoe1","seconds":65},
"trousers":{"id":"trouser1","seconds":600}
}; // generate this on the server and note there is no comma after the last item
window.onload = function() {
for (var el in counters) { countdown(counters[el]) };
}
function countdown(element) {
intervals[element.id] = setInterval(function() {
var el = document.getElementById(element.id);
if(element.seconds == 0) {
el.innerHTML = "countdown's over!";
clearInterval(intervals[element.id]);
return;
}
var minutes = (element.seconds - (element.seconds % 60)) / 60;
if(minutes > 0) {
var minute_text = minutes + (minutes > 1 ? ' minutes' : ' minute');
} else {
var minute_text = '';
}
var second_text = (element.seconds%60) > 1 ? 'seconds' : 'second';
el.innerHTML = minute_text + ' ' + (element.seconds%60) + ' ' + second_text + ' remaining';
element.seconds--;
}, 1000);
}
(I'd post as a comment if it weren't for all the code...)