I want to create a clock using java script. For this i'm using classes.
const htmlMarkup = (hours = 0, minutes = 0, seconds = 0) => {
console.log('render html')
return (
`<div class="clock">
<h2>clock: ${hours} ${minutes} ${seconds}</h2>
</div>`
)
};
class Clock {
constructor() {
this.time = new Date();
setInterval(() => {
this.getSeconds()
}, 1000)
}
renderHTML() {
return htmlMarkup(this.hours, this.minutes, this.getSeconds())
}
getSeconds() {
return this.seconds = this.time.getSeconds()
}
}
const runClock = new Clock();
document.querySelector(".app").innerHTML = runClock.renderHTML();
<div class="app"></div>
Even if i set:
setInterval(() => {
this.getSeconds()
}, 1000)
...the seconds are not changing. So, why the seconds still the same after first render of the application and how to ssolve the issue?
There are a couple of problems:
Your timer just calls getSeconds. getSeconds doesn't do anything to update the HTML. For that, you need to repeat the document.querySelector(".app").innerHTML = runClock.renderHTML(); part. (In fact, the Clock#getSectonds method doesn't do anything useful and you can just get rid of it.)
Your code does this.time = new Date() and then just uses this.time throughout. That Date object is unchanging, it doesn't continually update itself. You don't need this.time at all, just use new Date() in renderHTML.
Your code never sets the hours or minutes, so it just shows 0s for those.
Here's an example that modifies the constructor so it accepts the element to update, and then calls renderHTML in the timer callback, using new Date in renderHTML to get the current date/time. (I've also added various missing semicolons. You should either use them, nor not use them and rely on ASI instead, but not use them sporatically.)
const htmlMarkup = (hours = 0, minutes = 0, seconds = 0) => {
console.log('render html');
return (
`<div class="clock">
<h2>clock: ${hours} ${minutes} ${seconds}</h2>
</div>`
);
};
class Clock {
constructor(element) {
this.element = element;
setInterval(()=> {
this.element.innerHTML = this.renderHTML();
},1000);
}
renderHTML() {
const dt = new Date();
return htmlMarkup(dt.getHours(), dt.getMinutes(), dt.getSeconds());
}
}
const runClock = new Clock(document.querySelector(".app"));
<div class="app"></div>
ur not calling the new Date() again. check this.
const htmlMarkup = (hours = 0, minutes = 0, seconds = 0) => {
console.log("render html");
return `<div class="clock">
<h2>clock: ${hours} ${minutes} ${seconds}</h2>
</div>`;
};
class Clock {
constructor() {
setInterval(() => {
this.getSeconds();
}, 1000);
}
renderHTML() {
return htmlMarkup(this.hours, this.minutes, this.getSeconds());
}
getSeconds() {
this.time = new Date();
this.seconds = this.time.getSeconds();
console.log(this.time);
}
}
const runClock = new Clock();
document.querySelector(".app").innerHTML = runClock.renderHTML();
I did the following changes:
setInterval(()=> {
document.querySelector(".app").innerHTML = runClock.renderHTML();
},1000)
And
getSeconds() {
return (new Date()).getSeconds()
}
Now only the seconds are changing. You'll have to handle for minutes and hours.
Related
I want to implement a function as follows
There is a function such as
const FloatRed = (startDate, endDate, imgUrl) => {}
StartDate is the start date. When the start date arrives, insert an img element into the body
EndDate is the end date. When the end date arrives, destroy the img element
How to realize it? Thank you
For example,
FloatRed ("2023-02-06 14:00", "2023-02-06 14:05", "xxx. com/xxx/xxx. jpg")
I wrote a simple version
But I think it's always strange. Although it can basically meet the needs, I hope to give some suggestions and help improve it. thank you
let Is = false
let t = null;
const FloatingRed = (startDate, endDate, Imgurl) => {
let nowDate = new Date().getTime()
let startDate_s = new Date(startDate).getTime()
let endDate_s = new Date(endDate).getTime()
let ImageObj = new Image();
let Dom;
ImageObj.onload = (e) => {
Dom = `<div id="FloatRedImg"><img src="${Imgurl}" width=${ImageObj.width} height="${ImageObj.height}"></div>`
let Dom_old = $("#FloatRedImg").html()
if (nowDate >= endDate_s) {
$("#FloatRedImg").remove()
clearInterval(t)
}
if (Dom_old) {
return
}
if (nowDate > startDate_s) {
$("body").prepend(Dom)
}
}
ImageObj.src = Imgurl;
}
t = setInterval(() => {
FloatingRed("2023-02-06 16:49", "2023-02-06 16:50", "2022102201.jpg")
}, 1000)
If you put new Date().getTime() in a variable, it will make the time value constant. I prefer you call new Date().getTime() in setInterval, so that time will be updated continuously. Try this with change the startDate and endDate value :
let Is=false
let t;
const FloatingRed = (startDate, endDate, Imgurl) => {
let startDate_s = new Date(startDate).getTime()
let endDate_s = new Date(endDate).getTime()
let ImageObj = new Image();
let Dom;
ImageObj.onload=(e) => {
Dom = `<div id="FloatRedImg"><img src="${Imgurl}" width=${ImageObj.width} height="${ImageObj.height}"></div>`;
if (new Date().getTime() > startDate_s && new Date().getTime() <= endDate_s) {
$("body").prepend(Dom)
}
t = setInterval(() => {
if(new Date().getTime() > endDate_s) {
$("#FloatRedImg").remove();
clearInterval(t);
}
}, 1000);
}
ImageObj.src=Imgurl;
}
FloatingRed("2023-02-07 08:45", "2023-02-07 09:00", "https://picsum.photos/200");
setInterval(()=>{
$('#now').html(new Date().toLocaleString());
},1000);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="now"></div>
I want to call the function "displayTime" in "startTimer" but for some reason I get "Uncaught TypeError: this.displayTime is not a function" in the console.
let endTimer = "0";
let isRunning = false;
//! CLASS
class Timer {
constructor(endTimer, isRunning) {
this.endTimer = endTimer;
this.isRunning = isRunning;
}
startTimer() {
if (this.endTimer === "0") {
this.endTimer = new Date().getTime() + 1500000;
}
displayTime();
if (this.isRunning === true) {
this.pauseTimer();
this.isRunning
}
this.isRunning = true;
}
displayTime() {
let now = new Date().getTime();
let remainingTime = this.endTimer - now;
let minutes = Math.floor(remainingTime / 1000 / 60);
let seconds = Math.floor((remainingTime / 1000) % 60);
if (seconds < 10) {
seconds = "0" + seconds;
}
timer.innerHTML = `<h1>${minutes}:${seconds}</h1>`;
start.textContent = "STOP"
this.update = setInterval(this.displayTime, 100);
}
}
let newTimer = new Timer(endTimer, isRunning);
//! EVENT LISTENERS
start.addEventListener("click", newTimer.startTimer);
I think that I'm missing something obvious, but I don't understand what...
start.addEventListener("click", newTimer.startTimer.bind(newTimer));
Calling displayTime(); without the prepended keyword 'this' is the main issue (line 16 below) as mentioned by GenericUser and Heretic Monkey in the comments above.
You probably already know this but you'll want to define a pauseTimer() method/function as well.
let endTimer = "0";
let isRunning = false;
//! CLASS
class Timer {
constructor(endTimer, isRunning) {
this.endTimer = endTimer;
this.isRunning = isRunning;
}
}
Timer.prototype.startTimer = function() {
if (this.endTimer === "0") {
this.endTimer = new Date().getTime() + 1500000;
}
this.displayTime(); // this is the important line
if (this.isRunning === true) {
this.pauseTimer();
this.isRunning
}
this.isRunning = true;
}
Timer.prototype.displayTime = function() {
let now = new Date().getTime();
let remainingTime = this.endTimer - now;
let minutes = Math.floor(remainingTime / 1000 / 60);
let seconds = Math.floor((remainingTime / 1000) % 60);
if (seconds < 10) {
seconds = "0" + seconds;
}
//timer.innerHTML = `<h1>${minutes}:${seconds}</h1>`;
//start.textContent = "STOP"
this.update = setInterval(this.displayTime, 100);
}
Timer.prototype.pauseTimer = function() {
this.isRunning = false;
}
let newTimer = new Timer("0", true);
newTimer.startTimer();
//! EVENT LISTENERS
//start.addEventListener("click", newTimer.startTimer);
class Timer {
startTimer() {
// You forgot this keyword
this.displayTime();
}
displayTime() {
console.log('do something');
}
}
let newTimer = new Timer();
// addEventListener changes the context of this.
// Using an arrow function will keep 'this' keyword in tact inside startTimer method
start.addEventListener('click', () => newTimer.startTimer)
I want to create a 3 hours countdown that runs in the background and repeats when it reaches 00:00:00. How can I do it using HTML and javascript? Thank you!
You can use Web Worker:
Run in background
No throttled ( settimeout-setinterval-on-inactive-tab )
// index.html
<div id="box">60</div>
<script>
let box = document.getElementById('box');
let worker = new Worker('countdown.js');
worker.postMessage(60);
worker.onmessage = function (event) {
box.innerHTML = event.data;
}
</script>
// countdown.js
onmessage = function (e) {
let num = e.data;
let count = setInterval(function () {
postMessage(--num);
if (num <= 0) {
clearInterval(count);
close();
}
}, 1000);
}
For background countdown. I think the big issue that you need to face is when you are leave the page or open a new tabs. right?
Do you want to make the countdown work even the tab is not active?
maybe requestAnimationFrame is helpful for you.
function showTime() {
remainingTime = getRemainingTime(endTime);
var seconds = pad((remainingTime / 1000));
console.log('remain time is: ', seconds + " sec")
if (remainingTime >= 1000) {
window.requestAnimationFrame(showTime);
} else {
console.log('Time up!!!')
}
}
function getRemainingTime(deadline) {
var currentTime = new Date().getTime();
return deadline - currentTime;
}
function pad(value) {
var sl = (""+Math.floor(value)).length;
if(sl > 2){
return ('0' + Math.floor(value)).slice(-sl);
}
return ('0' + Math.floor(value)).slice(-2);
}
endTime = new Date().getTime() + 1000*60;
window.requestAnimationFrame(showTime);
Demo here: https://codepen.io/quanhv/pen/oNZWxvB
As I understood from MDN, I am supposed to make variable and assign setInterval function to it, so that I could use that variable and call clearInterval for it, but for some reason, my code is now working. It is properly fetching data with buttonStart, but will not stop fetching data with buttonStop.
Thank you for your time.
const buttonStart = document.querySelector('#start')
const buttonStop = document.querySelector('#stop')
const list = document.querySelector('#list')
class Price {
constructor(time, price) {
this.time = time
this.price = price
}
}
const fetchBitcoin = async () => {
try {
const res = await fetch('https://api.cryptonator.com/api/ticker/btc-usd');
const data = await res.json();
const newPrice = new Price(data.timestamp, data.ticker.price)
return newPrice
} catch (e) {
console.log("Something went wrong in downloading price", e)
}
}
const addNewPrice = async () => {
const newLI = document.createElement('LI')
const newElement = await fetchBitcoin()
const newTime = convertTime(newElement.time)
newLI.append(newTime, ' ', newElement.price.slice(0, 8))
list.append(newLI)
}
function convertTime(time) {
let unix_timestamp = time
var date = new Date(unix_timestamp * 1000);
var hours = date.getHours();
var minutes = "0" + date.getMinutes();
var seconds = "0" + date.getSeconds();
var formattedTime = hours + ':' + minutes.substr(-2) + ':' + seconds.substr(-2);
return formattedTime
}
let interval = buttonStart.addEventListener('click', () => {
setInterval(addNewPrice, 2000)
})
buttonStop.addEventListener('click', () => clearInterval(interval));
You need to create interval variable and assign the return value of the setInterval method rather than addEventListener because addEventListener does not return anything,
let interval;
buttonStart.addEventListener('click', () => {
interval = setInterval(addNewPrice, 2000)
})
You need to adjust the example below to your use case but this is what you need in general:
var timerEl = document.querySelector('.js-timer');
var startBtn = document.querySelector('.js-start');
var stopBtn = document.querySelector('.js-stop');
var intervalId;
var timer = 0;
startBtn.addEventListener('click', function() {
stopTimer();
console.log('start timer');
intervalId = setInterval(execTimer, 100);
});
stopBtn.addEventListener('click', stopTimer);
function stopTimer() {
timer = 0;
console.log('stop timer');
clearInterval(intervalId);
renderTimer();
}
function execTimer() {
timer++;
renderTimer();
console.log('timer score', timer);
}
function renderTimer() {
timerEl.textContent = timer;
}
<span class="js-timer"></span><br />
<button class="js-start">Start</button>
<button class="js-stop">Stop</button>
I have THIS timer in my project.
When it runs out, it shows a Time Up screen, which works fine.
But when the player is Game Over, i show the Game Over screen, but the timer keeps running and when it hits 00:00 then it switches to the Time Up screen.
How can i make this timer stop counting down and set to 00:00 again?
I tried adding a function like this:
CountDownTimer.prototype.stop = function() {
diff = 0;
this.running = false;
};
I also tried to change the innerHTML but its obvious that its just changing the numbers without stopping the timer and after a second it will show the count down again... I don't know what to call.
//Crazy Timer function start
function CountDownTimer(duration, granularity) {
this.duration = duration;
this.granularity = granularity || 1000;
this.tickFtns = [];
this.running = false;
}
CountDownTimer.prototype.start = function() {
if (this.running) {
return;
}
this.running = true;
var start = Date.now(),
that = this,
diff, obj;
(function timer() {
diff = that.duration - (((Date.now() - start) / 1000) | 0);
if (diff > 0) {
setTimeout(timer, that.granularity);
} else {
diff = 0;
that.running = false;
}
obj = CountDownTimer.parse(diff);
that.tickFtns.forEach(function(ftn) {
ftn.call(this, obj.minutes, obj.seconds);
}, that);
}());
};
CountDownTimer.prototype.onTick = function(ftn) {
if (typeof ftn === 'function') {
this.tickFtns.push(ftn);
}
return this;
};
CountDownTimer.prototype.expired = function() {
return !this.running;
};
CountDownTimer.parse = function(seconds) {
return {
'minutes': (seconds / 60) | 0,
'seconds': (seconds % 60) | 0
};
};
window.onload = function () {
var display = document.querySelector('#countDown'),
timer = new CountDownTimer(timerValue),
timeObj = CountDownTimer.parse(timerValue);
format(timeObj.minutes, timeObj.seconds);
timer.onTick(format).onTick(checkTime);
document.querySelector('#startBtn').addEventListener('click', function () {
timer.start();
});
function format(minutes, seconds) {
minutes = minutes < 10 ? "0" + minutes : minutes;
seconds = seconds < 10 ? "0" + seconds : seconds;
display.textContent = minutes + ':' + seconds;
}
function checkTime(){
if(this.expired()) {
timeUp();
document.querySelector('#startBtn').addEventListener('click', function () {
timer.start();
});
}
}
};
Instead of recursively calling setTimeout, try setInterval instead. You could then store a reference to the timer:
this.timer = setInterval(functionToRunAtInterval, this.granularity);
and kill it when the game finishes::
clearInterval(this.timer)
(see MDN's docs for more info on setInterval)
It's been a while and I'm not sure if you've figured it out but check out the fiddle below:
https://jsfiddle.net/f8rh3u85/1/
// until running is set to false the timer will keep running
if (that.running) {
if (diff > 0) {
setTimeout(timer, that.granularity);
} else {
diff = 0;
that.running = false;
}
obj = CountDownTimer.parse(diff);
that.tickFtns.forEach(function(ftn) {
ftn.call(this, obj.minutes, obj.seconds);
}, that);
}
I've added a button that causes running to be set to false which stops the timer.
Button:
<button id="stop">Game Over</button>
Code:
$( "#stop" ).click(function() {
timer.running = false;
});
So that should hopefully get you to where you need to be.
Similar to Tom Jenkins' answer, you need to cancel the next tick by avoiding the diff > 0 branch of your if statement. You could keep your code as it stands and use your suggested stop method, however, you'd need to change your logic around handling ticks to check that both running === true and a new param gameOver === false.
Ultimately, I think your problem is that no matter whether the timer is running or not, you'll always execute this code on a tick:
obj = CountDownTimer.parse(diff);
that.tickFtns.forEach(function(ftn) {
ftn.call(this, obj.minutes, obj.seconds);
}, that);
If you have a game over state, you probably don't want to call the provided callbacks, so add some conditional check in there.