If you have used Carbon Class in PHP, then you might have heard about the method Carbon::diffForHumans()
. It returns the difference between 2 dates in a Human Readable Form.
So if difference is less than 60 seconds, the output would be in seconds. If the difference is more than 60 seconds, the output would be in minutes. If the difference is more than 60 minutes, the output would be in hours and so on.
Recently I had to built a Timer in JS which should the elapsed Time in a similar manner. I choose to built the Timer using AlpineJS.
I choose to call my Component moment
because I have been a big fan of moment.js
. The Component had a prop of seconds
which would hold the number of seconds that timer would need to display.
<span x-data="moment">
</span>
<script>
function moment() {
return {
seconds: 1,
}
}
</script>
Next I created an init
method which would set the initial value of the timer.
<span x-data="moment" x-init="init(40)">
</span>
<script>
function moment() {
return {
seconds: 1,
init(seconds) {
this.seconds = seconds;
},
}
}
</script>
Next, within this init method, I would use setInterval
to call a closure after each second. Within the closure, I would increment the value of seconds
prop. I would also create an interval
prop which I could use to close the Timer.
interval: "",
init(seconds) {
this.seconds = seconds;
this.interval = setInterval(() => {
this.seconds++;
}, 1000);
},
Next I will create a method to display the Timer, I will call it getTimeElapsed
and use it as below:
<span x-data="moment" x-init="init(40)">
<span x-text="getTimeElapsed"></span>
</span>
.
.
.
getTimeElapsed() {
return this.seconds;
}
At this stage the Timer would be working well and it will increment after each second. Now, we would format the getTimeElapsed
method so that it would return the data similar to Carbon method.
In order to do so, I created an intervals
property like below:
intervals: [
{ label: "hour", seconds: 3599 },
{ label: "minute", seconds: 59 },
{ label: "second", seconds: 1 }
],
And then I used this property within the getTimeElapsed
as follows:
getTimeElapsed() {
const interval = this.intervals.find((i) => i.seconds < this.seconds);
const count = Math.floor(this.seconds / interval.seconds);
return `${count} ${interval.label}${count !== 1 ? "s" : ""} ago`;
}
This will display the difference in seconds and as soon as the difference crosses 59 seconds, it would display the difference in minutes and so on. I only needed difference till hours so I only defined interval
props till 3600. If you need to display days, you can define further.
My last requirement was to Stop the Timer as soon as it crossed 2 hours. So I used the following check in getTimeElapsed
.
if (this.seconds > 7200) {
clearInterval(this.interval);
}
The beauty of AlpineJS is that you can define multiple of these Components on your page and each will behave independently of each other. You can check the implementation at the CodePen.
Hope you have enjoyed this tutorial. For similar articles, you can follow me on Twitter
Top comments (0)