Все ответы, которые вы получили до сих пор, не могут обработать один простой факт: setInterval
не не происходит надежно в то время, которое вы установили.Он может быть отложен или даже пропущен , если браузер занят чем-то другим.Это означает, что вы не можете полагаться на свою функцию обновления, чтобы уменьшить количество оставшихся секунд, вы начнете очень быстро отходить от реальности.Может это нормально, а может и нет.В любом случае не нужно для этого: просто рассчитайте, когда истечет время ожидания (например, через минуту и пять секунд для вашего счетчика «обувь»), а затем при каждом обновлении вычисляйте, как долго выоставили.Таким образом, если интервал упал или что-то, вы не дрейфуете;вы откладываете часы компьютера.
Вот процедурная версия:
// 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;
}
}
};
Живой пример
Вот версия ООП (с использованием модулятаким образом, Counter
может иметь с именами функций и частными [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();
}
};
Живой пример
Подробнее о замыканиях здесь .