I have a page with a JavaScript countdown and Bootstrap's tooltip JavaScript. They work very well, until I reload the page with jQuery's load(); function.
After I trigger load(); function couple of seconds after page load, other JavaScript stops working.
Check my page: http://areafordemos.freeoda.com/reload.html
I don't understand why simple refresh is causing that, and how to solve this problem? I tried to place JavaScript code to other places but no help.
Here is my Javascript code:
//JavaScript code for load() after few seconds.
setTimeout(function(){
$().ready(function() {
$(".reloadthis").load("reload.html .reloadthis");
});
}, 5000);
//JavaScript code for tooltip.
$(document).ready(function(){
$('[data-toggle="tooltip"]').tooltip();
});
//JavaScript code for countdown
function getTimeRemaining(endtime) {
var t = Date.parse(endtime) - Date.now();
var seconds = Math.floor((t / 1000) % 60);
var minutes = Math.floor((t / 1000 / 60) % 60);
var hours = Math.floor((t / (1000 * 60 * 60)) % 24);
var days = Math.floor(t / (1000 * 60 * 60 * 24));
return {
'total': t,
'days': days,
'hours': hours,
'minutes': minutes,
'seconds': seconds
};
}
function initializeClock(id, endtime) {
var clock = document.getElementById(id);
var daysSpan = clock.querySelector('.days');
var hoursSpan = clock.querySelector('.hours');
var minutesSpan = clock.querySelector('.minutes');
var secondsSpan = clock.querySelector('.seconds');
function updateClock() {
var t = getTimeRemaining(endtime);
if (t.total <= 0) {
document.getElementById("clockdiv").className = "hidden-div";
document.getElementById("timeIsNow").className = "visible-div";
clearInterval(timeinterval);
return true;
}
daysSpan.innerHTML = t.days;
hoursSpan.innerHTML = ('0' + t.hours).slice(-2);
minutesSpan.innerHTML = ('0' + t.minutes).slice(-2);
secondsSpan.innerHTML = ('0' + t.seconds).slice(-2);
}
updateClock();
var timeinterval = setInterval(updateClock, 1000);
}
var deadline = '2016-01-01T12:00:00+02:00';
console.log(deadline);
initializeClock('clockdiv', deadline);
When you bind an event to an element in javascript, the handler will live on the element itself rather than keeping a reference to it's class so when you reload new contents in the page, the JS won't be bound to them anymore.
You can do (atleast) two things to fix your issue.
One of them would be to re-run your JS by redoing the function calls.
e.g. Whenever you do $.load you can add a callback that should run after it's done fetching the contents of the page.
For instance:
$('.reloadthis').load('reload.html .reloadthis', function(resp, status, xhr) {
call_functions_again(); // psuedo, you'd want to re-call your functions here to make them work with ajax loaded content
});
You could also try looking into event delegation but this serves different purposes like automatically binding events to newly added elements in the DOM by listening on the parent instead and letting the event bubble to children.
Related
I am trying to create a countdown website which runs a set of schedules as countdowns one after another. Each schedule is an array of a pair of strings which represent the start time and end time of each schedule.
Expected Behaviour:
I want the second schedule to start when the first one ends.
Current Behaviour:
After the first schedule is completed, the date shown on the webpage shows NaN for all the values.
Please help me understand what I am doing wrong as I am new to programming.
Codepen of Minimum Complete Verifiable Example [You may have to change the start time and end times of the schedules to be in the future depending on when you open it to reproduce the issue]
Code Sample:
function getTimeRemaining(endtime) {
var t = Date.parse(endtime) - Date.parse(new Date());
var seconds = Math.floor((t / 1000) % 60);
var minutes = Math.floor((t / 1000 / 60) % 60);
var hours = Math.floor((t / (1000 * 60 * 60)) % 24);
var days = Math.floor(t / (1000 * 60 * 60 * 24));
return {
'total': t,
'days': days,
'hours': hours,
'minutes': minutes,
'seconds': seconds
};
}
function initializeClock(id, endtime) {
var clock = document.getElementById(id);
clock.style.display = 'block';
var daysSpan = clock.querySelector('.days');
var hoursSpan = clock.querySelector('.hours');
var minutesSpan = clock.querySelector('.minutes');
var secondsSpan = clock.querySelector('.seconds');
function updateClock() {
var t = getTimeRemaining(endtime);
daysSpan.innerHTML = t.days;
hoursSpan.innerHTML = ('0' + t.hours).slice(-2);
minutesSpan.innerHTML = ('0' + t.minutes).slice(-2);
secondsSpan.innerHTML = ('0' + t.seconds).slice(-2);
if (t.total <= 0) {
clearInterval(timeinterval);
}
}
updateClock();
var timeinterval = setInterval(updateClock, 1000);
}
// var deadline = new Date(Date.parse(new Date()) + 15 * 24 * 60 * 60 * 1000);
initializeClock('clockdiv', schedule);
var schedule = [
['2018-05-01', '2018-05-02 13:36:00'],
['2018-05-02 13:36:01', '2018-05-09']
];
// iterate over each element in the schedule
for(var i=0; i<schedule.length; i++){
var startDate = schedule[i][0];
var endDate = schedule[i][1];
// put dates in milliseconds for easy comparisons
var startMs = Date.parse(startDate);
var endMs = Date.parse(endDate);
var currentMs = Date.parse(new Date());
// if current date is between start and end dates, display clock
if(endMs > currentMs && currentMs >= startMs ){
initializeClock('clockdiv', endDate);
}
}
The problem I think is mainly in two areas:
The for loop runs only once so the second schedule is never started after the first one ends (the for loop would have obviously finished executing and does not startup again). That loop should ideally run again once the first schedule ends.
The function to clear the timeout should be moved before the DOM update as the function getTimeRemaining may return inconsistent values when the schedule is over.
Solutions:
Wrap the schedule start code in a function startNewScheduleIfNeeded and run that new function each time a schedule ends and we clear the timer
Move the check to see if we should clear the timer above the DOM update in updateClock to avoid NaN values
Sample Code:
function getTimeRemaining(endtime) {
var t = Date.parse(endtime) - Date.parse(new Date());
var seconds = Math.floor((t / 1000) % 60);
var minutes = Math.floor((t / 1000 / 60) % 60);
var hours = Math.floor((t / (1000 * 60 * 60)) % 24);
var days = Math.floor(t / (1000 * 60 * 60 * 24));
return {
total: t,
days: days,
hours: hours,
minutes: minutes,
seconds: seconds
};
}
function initializeClock(id, endtime) {
var clock = document.getElementById(id);
clock.style.display = "block";
var daysSpan = clock.querySelector(".days");
var hoursSpan = clock.querySelector(".hours");
var minutesSpan = clock.querySelector(".minutes");
var secondsSpan = clock.querySelector(".seconds");
function updateClock() {
var t = getTimeRemaining(endtime);
// Solution Part 2: Move check before DOM update and return early if needed
if (t.total <= 0) {
clearInterval(timeinterval);
// Schedule has ended, run function to start another if needed.
startNewScheduleIfNeeded();
return;
}
daysSpan.innerHTML = t.days;
hoursSpan.innerHTML = ("0" + t.hours).slice(-2);
minutesSpan.innerHTML = ("0" + t.minutes).slice(-2);
secondsSpan.innerHTML = ("0" + t.seconds).slice(-2);
}
updateClock();
var timeinterval = setInterval(updateClock, 1000);
}
// var deadline = new Date(Date.parse(new Date()) + 15 * 24 * 60 * 60 * 1000);
//initializeClock('clockdiv', schedule);
// Solution Part 1: Wrap schedule start in a function to resuse when schedules end
function startNewScheduleIfNeeded() {
var schedule = [
["2018-05-03", "2018-05-03 11:17:00"],
["2018-05-03 11:17:00", "2018-05-09"]
];
// iterate over each element in the schedule
for (var i = 0; i < schedule.length; i++) {
var startDate = schedule[i][0];
var endDate = schedule[i][1];
// put dates in milliseconds for easy comparisons
var startMs = Date.parse(startDate);
var endMs = Date.parse(endDate);
var currentMs = Date.parse(new Date());
// if current date is between start and end dates, display clock
if (endMs > currentMs && currentMs >= startMs) {
initializeClock("clockdiv", endDate);
}
}
}
startNewScheduleIfNeeded();
Other observations:
there was a random call to initializeClock which I have commented out
You only need to use Date.parse if your input is a string (like your schedule list). calling Date.parse(new Date()) is redundant. You can just use new Date(). Also, Date.parse DOES NOT return an integer (milliseconds). It returns a Date object which gets casted to a number when you do comparison like <
The end time of one schedule must be equal to the start time in the next schedule for this method to work. If you have gaps (even one second) the function to start the schedule will execute before the check to see if the current time is between the start time and end time can pass. (in the example in your question, end time of first schedule is '2018-05-02 13:36:00' and the start time for the next one is '2018-05-02 13:36:01'. This won't work - the start time of the second schedule has to be '2018-05-02 13:36:00').
I have a simple JS code to do countdown on the Client browser but there is a short coming the timer depends on the client machine so i want to get the Current Time and elapsed time from the Server.
Below is my working but it doesn't work.
function getTimeRemaining(endtime) {
var t = Date.parse(endtime) - Date.parse(current_time);
var seconds = Math.floor((t / 1000) % 60);
var minutes = Math.floor((t / 1000 / 60) % 60);
var hours = Math.floor((t / (1000 * 60 * 60)) % 24);
var days = Math.floor(t / (1000 * 60 * 60 * 24));
return {
'total': t,
'days': days,
'hours': hours,
'minutes': minutes,
'seconds': seconds
};
}
function initializeClock(id, endtime, current_time) {
var clock = document.getElementById(id);
var daysSpan = clock.querySelector('.days');
var hoursSpan = clock.querySelector('.hours');
var minutesSpan = clock.querySelector('.minutes');
var secondsSpan = clock.querySelector('.seconds');
function updateClock() {
var t = getTimeRemaining(endtime, current_time);
daysSpan.innerHTML = t.days;
hoursSpan.innerHTML = ('0' + t.hours).slice(-2);
minutesSpan.innerHTML = ('0' + t.minutes).slice(-2);
secondsSpan.innerHTML = ('0' + t.seconds).slice(-2);
if (t.total <= 0) {
clearInterval(timeinterval);
$('#clockdiv').html('You can\'t Reserve anymore')
}
}
updateClock();
var timeinterval = setInterval(updateClock, 1000);
}
var deadline = '2017-02-24 18:12:29';
var current_time = new EventSource('Server/script/example.php');
current_time.onopen = function(event) {
//console.log(event)
current_time.onmessage = function(event)
{
return event.data;
}
current_time.onerror = function (error) {
//console.log('we have an error')
}
}
console.log(current_time)
initializeClock('clockdiv', deadline, current_time);
So am trying to use HTML SSE server Sent Event so as Listen to response instead of making the request.
current_time variable is empty and the output of the Time is NAN NAN NAN
So is there a better of achieving the above or can some work this code for me.
I tried to build a countdown timer and it kind of worked, but when I changed the code to make it more readable and when I added the stop button function it got a bit buggy. I fiddled around a lot, but I can't get it working again.
The problem I have is, that the countdown starts from the time when the page has loaded and not from the number assigned to "sessTime".
Here is the code (i know it is a lot, sorry):
var startButton = document.getElementById('btnStart');
var stopButton = document.getElementById('btnStop');
var sessionTime = parseInt(document.getElementById('sessNum').innerHTML); //gets duration number
var sessLength = Date.parse(new Date()) + sessionTime * 60 * 1000;
startButton.onclick = function() {
if (!sessTimer) {
startButton.value="Stop";
var sessTimer = setInterval(runSess, 1000);
}else{
startButton.value="Start";
clearInterval(sessTimer);
}
};
function runSess() {
var timeLeft = sessLength - Date.parse(new Date());
var seconds = Math.floor((timeLeft / 1000) % 60);
var minutes = Math.floor((timeLeft / 1000 / 60) % 60);
var hours = Math.floor((timeLeft / (1000 * 60 * 60)) % 24);
document.getElementById("hours").innerHTML = ('0' + hours).slice(-2);
document.getElementById("minutes").innerHTML = ('0' + minutes).slice(-2);
document.getElementById("seconds").innerHTML = ('0' + seconds).slice(-2);
if (timeLeft <= 0) {
clearInterval(sessTimer);
}
}
This code is part of a codepen project of mine. Maybe the context helps to answer this question.
Thanks in advance, I really appreciate your help.
You have declared sessTimer inside the scope of your function.
startButton.onclick = function() {
if (!sessTimer) {
startButton.value="Stop";
var sessTimer = setInterval(runSess, 1000);
With the code above, every time you enter the function, sessTimer is undefined and you declare a new one. What you can do is the following :
var sessTimer = null;
startButton.onclick = function() {
if (!sessTimer) {
startButton.value="Stop";
sessTimer = setInterval(runSess, 1000);
}else{
startButton.value="Start";
clearInterval(sessTimer);
sessTimer = null;
}
};
Into you function, you also need to change your button :
if (timeLeft <= 0) {
startButton.value="Start";
clearInterval(sessTimer);
}
I'm trying to make this count down not reset on load or refresh. Can anyone help me with this? I have heard I can use local storage but surly I will need to change this code to be server sided with PHP and MySQL. If you do have any resources which show you the PHP version could you send them to me or post as an answer?
var updateTimer = function() {
timer = localStorage.getItem('timer') || 0;
if ( timer === 0 ) {
$("div#timer").html("Timer is unset");
} else {
timer--;
localStorage.setItem('timer', timer);
$("div#timer").html(timer);
}
};
$(function() {
setInterval(updateTimer, 1000);
$("#start").click( function() {
localStorage.setItem('timer', 500);
});
});
function getTimeRemaining(endtime) {
var t = Date.parse(endtime) - Date.parse(new Date());
var seconds = Math.floor((t / 1000) % 60);
var minutes = Math.floor((t / 1000 / 60) % 60);
var hours = Math.floor((t / (1000 * 60 * 60)) % 24);
var days = Math.floor(t / (1000 * 60 * 60 * 24));
return {
'total': t,
'days': days,
'hours': hours,
'minutes': minutes,
'seconds': seconds
};
}
function initializeClock(id, endtime) {
var clock = document.getElementById(id);
var daysSpan = clock.querySelector('.days');
var hoursSpan = clock.querySelector('.hours');
var minutesSpan = clock.querySelector('.minutes');
var secondsSpan = clock.querySelector('.seconds');
function updateClock() {
var t = getTimeRemaining(endtime);
daysSpan.innerHTML = t.days;
hoursSpan.innerHTML = ('0' + t.hours).slice(-2);
minutesSpan.innerHTML = ('0' + t.minutes).slice(-2);
secondsSpan.innerHTML = ('0' + t.seconds).slice(-2);
if (t.total <= 0) {
clearInterval(timeinterval);
}
}
updateClock();
var timeinterval = setInterval(updateClock, 1000);
}
var deadline = new Date(Date.parse(new Date()) + 15 * 24 * 60 * 60 * 1000);
initializeClock('clockdiv', deadline);
Thank you
If you want to completely avoid server sided coding, you can save values using Cookies.
In javascript this is easily done by document.cookie = "yourValues, expiryDate".
JQuery also has some nice functions to handle cookies.
You may want to adjust your script to calculate the passed time between loads or not having the clock run, but that is optional.
http://www.w3schools.com/js/js_cookies.asp <- Here is a simple tutorial on how to use them.
With php you can use the cookie's to achieve the same.
setcookie($timer); // Set timer
$_COOKIE['timer']; // Get timer
You can start inserting into mysql in every refresh but use $_COOKIE to show. Mysql is just for tracking and getting correct time after refresh the page.
mysql_query("UPDATE table SET timer = '$timer'"); // Inserting every refresh
So when page loads either get form stored cookie or retrieve from mysql table
But COOKIES will be faster than using only mysql method(It won't work properly as it need to request every second)
I am building a Ruby on Rails app that lets a user create a workout for various durations (i.e. 30 minutes). A workout has_many exercises. Each exercise has a duration (i.e. 30 seconds). I want the workout to loop through a random array of exercises until the timer runs out.
I am trying to get an event to trigger when the timer (in JS) reaches 00:00 - so when the exercise reaches 0 seconds, it renders a new exercise and it's corresponding new countdown.
When the workout.duration = 0, redirect to root_path.
I have the basic app up here: https://morning-garden-55692.herokuapp.com/workouts/3
This all is called from the WorkoutsController and takes place in the Workout#show view.
This is the ruby in the show view:
<% #exercises.each do |ex| %>
<%= ex.name %> <br><br> <div id="exclock"> <span class="hours"></span> :
<span class="minutes"></span> :
<span class="seconds"></span>
This is the js that runs the clock:
<script>
function getTimeRemaining(endtime) {
var t = Date.parse(endtime) - Date.parse(new Date());
var seconds = Math.floor((t / 1000) % 60);
var minutes = Math.floor((t / 1000 / 60) % 60);
var hours = Math.floor((t / (1000 * 60 * 60)) % 24);
return {
'total': t,
'hours': hours,
'minutes': minutes,
'seconds': seconds
};
}
function initializeClock(id, endtime) {
var clock = document.getElementById(id);
var hoursSpan = clock.querySelector('.hours');
var minutesSpan = clock.querySelector('.minutes');
var secondsSpan = clock.querySelector('.seconds');
function updateClock() {
var t = getTimeRemaining(endtime);
hoursSpan.innerHTML = ('0' + t.hours).slice(-2);
minutesSpan.innerHTML = ('0' + t.minutes).slice(-2);
secondsSpan.innerHTML = ('0' + t.seconds).slice(-2);
if (t.total <= 0) {
clearInterval(timeinterval);
}
}
updateClock();
var timeinterval = setInterval(updateClock, 1000);
}
var timeInSeconds = <% #exercises.each do |ex| %>
<%= ex.duration %>; <% end %>
var currentTime = Date.parse(new Date());
var deadline = new Date(currentTime + timeInSeconds*1000);
initializeClock('exclock', deadline);
</script>
In the workouts controller:
def show
#exercises = Exercise.limit(1).order("RANDOM()")
end
I think in the js I need to just include some
until: +Exercise.duration == 0, onExpiry: nextEx
Any help in triggering a redirect upon expiry or rendering another exercise?
You can redirect the page with javascript by assigning a new url to window.location:
window.location = "http://www.yoururl.com/path-to-exercise";
If you are looking to reload the whole page for the next exercise you can just do this when your timer runs out.
If you are looking to replace an element on the page to load the next exercise you can fire off an XHR when your timer ends to retrieve the new content and then place it into the DOM or replace an existing element.