Having issues pausing the setInterval and then continuing - javascript

I have run into a little issue with a timer clock I was building. All went well but in short this is what it does:
user gets to set the time he/she wants to study or do any activity for
user presses start
the start button changes to a "stop" button
the timer counts down from the time chosen by the user
once the timer hits 0 the clock with stop and button changes back to "start" and everything is reset to 0:00
Note
Once the user taps the start button or the start button the device will vibrate if capable to notify the user.
The Issue
The problem I have is that when the clock is running and the user presses the "Stop Study" button, then the clock stops, yes thats good but when he/she presses the button again (which now should be a "start" button because its essentially paused), then the clock takes the time that was given and starts the clock over from that time the user gave and not continue until 0:00.
I have check out a few articles and I have used variables and switched between the Boolean state and checked if the clock is running or not.
isRunning = !isRunning
I have seen a few say that I should use:
clearInterval(name);
This doesn't work because I dont want to clear the state of the clock or maybe I am doing it wrong.
Code
The link to a fiddle can be found here: https://jsfiddle.net/ToreanJoel/c75vLf8b/
HTML
<br/>
<div class="timer" id="startingTimer">
<p class="title" id="state">Break</p>
<p id="time">00:00</p><span ng-style="{'height':fillHeight, 'background':fillColor }" class="fill" style="height: 0.933333%; background: rgb(153, 204, 0);"></span>
</div>
<br/>
<div class="session" id="toggleSessionStart">
<div class="timer control startingTimercontroller" id="startingTimercontroller">
<p class="title controlTitle" id="StartTimer">Start Study</p>
</div>
<!--<div class="timer control startingPauseTimercontroller" id="startingPauseTimercontroller">
<p class="title controlTitle" id="StartPauseTimer">Start Break</p>
</div>--></div>
<br/>
<header>
<div class="session">
<div class="sessionCtrl">
<p>session length</p>
<input type="number" class="time" id="valueTimerSession" value="10">
</div>
<!--<div class="breakCtrl">
<p>break length</p>
<input type="number" class="time" id="valueTimerBreak" value="5">
</div>--></div>
</header>
CSS
body {
background: #333333;
color: #fff;
}
#time {
font-size: 90px;
position: relative;
top: -40px;
}
#media (max-width: 500px) {
#time {
font-size: 90px;
position: relative;
top: -80px;
}
}
.plus {
background-color: #333333;
color: #fff;
border: none;
cursor: pointer;
font-size: 2em;
outline: none;
}
.time {
font-size: 2.5em;
padding-left: 10px;
padding-right: 10px;
width: 100%;
}
.minus {
background-color: #333333;
color: #fff;
border: none;
cursor: pointer;
font-size: 2em;
outline: none;
}
header {
display: flex;
justify-content: center;
text-align: center;
margin: 0 auto;
color: #fff;
text-transform: uppercase;
padding: 20px;
}
.session .breakCtrl, .session .sessionCtrl {
display: inline;
padding-left: 30px;
padding-right: 30px;
}
.session {
font-size: .8em;
display: flex;
}
.timer {
margin: 0 auto;
text-align: center;
width: 300px;
height: 300px;
font-size: 4em;
border: 2px solid #99CC00;
border-radius: 50%;
cursor: pointer;
position: relative;
z-index: 20;
overflow: hidden;
}
.control {
margin: 0 auto;
text-align: center;
width: 120px;
height: 120px;
font-size: 4em;
border: 2px solid #99CC00;
border-radius: 50%;
cursor: pointer;
position: relative;
z-index: 20;
overflow: hidden;
font-family: sans-serif;
}
.startingTimercontroller {
background: #37B703 !important;
border: 2px solid #fff;
}
.startingPauseTimercontroller {
background: #B70000 !important;
border: 2px solid #fff;
}
.title {
margin: 45px;
margin-bottom: -30px;
}
.controlTitle {
font-size: 28px;
position: relative;
top: 25px;
margin: 0;
}
.heading {
text-align: center;
font-size: 50px;
text-transform: uppercase;
font-family: sans-serif;
}
JS
//event Listener
var clickStart = document.getElementById("toggleSessionStart");
//pauseing the clock
var clockRunning = false;
var clicked = false;
//getting the user value ammount to study and break for
var valueTimerSession = parseInt(document.getElementById('valueTimerSession').value);
function pomodoro(studyTime) {
this.studyTime = studyTime;
this.seconds = 59;
this.timerDOM = document.getElementById("time");
this.state = document.getElementById("state");
this.toggleSessionStart = document.getElementById('toggleSessionStart');
}
pomodoro.prototype.startStudyTicker = function () {
var thisStudyTicker = this;
var seconds = this.seconds - 1;
var DOM = this.timerDOM;
var minutes = this.studyTime - 1;
var loopingSeconds = seconds;
var state = this.state;
var toggleSessionStart = this.toggleSessionStart;
if (clicked && clockRunning) {
console.log('We are runnung');
window.ticker = setInterval(function () {
//save the minutes to global variable
window.minSaved = minutes;
window.secSaved = loopingSeconds;
console.log("The time saved is " + window.minSaved + ":" + window.secSaved);
console.log(minutes + ":" + loopingSeconds);
var tick = loopingSeconds--;
if (loopingSeconds >= 0) {
tick;
DOM.innerHTML = minutes.toString() + ":" + (loopingSeconds < 10 ? '0' + loopingSeconds.toString() : loopingSeconds.toString());
} else {
if (minutes > 0) {
minutes--;
loopingSeconds = seconds;
tick;
DOM.innerHTML = minutes.toString() + ":" + (loopingSeconds < 10 ? '0' + loopingSeconds.toString() : loopingSeconds.toString());
}
if (minutes <= 0) {
//vibrate - Timer is Done
window.navigator.vibrate(300);
console.log('im finished');
clearInterval(ticker);
}
}
}, 1000);
} else {
if (!clicked && !clockRunning) {
clearInterval(ticker);
}
}
}
pomodoro.prototype.stopStudyTicker = function () {
var thisStudyTickerStop = this;
console.log('We are paused');
clearInterval(ticker);
thisStudyTickerStop.startStudyTicker();
}
//get the session title
var sessionTitle = document.getElementById('state');
//the DOM toggle
function toggleDOM(chosenTime) {
if (clicked && clockRunning) {
//started the session - the Title
sessionTitle.innerHTML = "Session";
clickStart.innerHTML =
'<div class="timer control startingPauseTimercontroller" id="startingPauseTimercontroller"><p class="title controlTitle" id="StartTimer">Stop Study</p></div>';
//vibrate
window.navigator.vibrate(300);
//prototype execution
var startStudy = new pomodoro(chosenTime);
startStudy.startStudyTicker();
} else {
sessionTitle.innerHTML = "Break";
clickStart.innerHTML =
'<div class="timer control startingTimercontroller" id="startingTimercontroller"><p class="title controlTitle" id="StartTimer">Start Study</p></div>';
//vibrate
window.navigator.vibrate([100, 100, 100]);
//prototype execution
var stopStudy = new pomodoro();
stopStudy.stopStudyTicker();
}
}
clickStart.addEventListener('click', function () {
//user clicked and the clock starts
clicked = !clicked;
clockRunning = !clockRunning;
//valueTimerBreak = parseInt(document.getElementById('valueTimerBreak').value);
valueTimerSession = parseInt(document.getElementById('valueTimerSession').value);
//the Toggle
toggleDOM(valueTimerSession);
});
I was looking at a few things on stack overflow but nothing really seemed to help as im not trying to use multiple buttons to pause or play but use one that toggles its states and the markup and the layout can be seen on jsFiddle (https://jsfiddle.net/ToreanJoel/c75vLf8b/).
I'm using Prototypal Pattern and I'm not use to it yet but I will be going over everything again just to refactor the code anyway to get use to it.
Thanks in advance

I didn't really understood your code but I made my own, basically if you click on a button and the seconds aren't stored in a variable - store them, else just continue looping. I think you'll understand my code, just replace your javascript with my.
var clickStart = document.getElementById("toggleSessionStart");
var pomodoro = function() {
this.inProgress = false;
this.studyTime = null;
this.timerInstance = null;
this.timerDOM = document.getElementById("time");
this.stateElement = document.getElementById("state");
this.toggleSessionStart = document.getElementById('toggleSessionStart');
}
pomodoro.prototype = {
start: function() {
var parent = this;
if(this.studyTime === null) this.studyTime = parseInt(document.getElementById('valueTimerSession').value, 10) * 60;
this.timerInstance = setInterval(function() {
parent.studyTime--;
if(parent.studyTime < 1) parent.destroy();
else parent.updateTime();
}, 1000);
return this;
},
pause: function() {
clearInterval(this.timerInstance);
this.timerInstance = null;
return this;
},
destroy: function() {
this.pause();
this.studyTime = null;
this.toogleState(false);
this.timerDOM.innerHTML = '00:00';
return this;
},
updateTime: function() {
var totalSec = this.studyTime,
minutes = Math.floor(totalSec / 60),
seconds = totalSec % 60;
this.timerDOM.innerHTML = (minutes < 10 ? "0" + minutes : minutes) + ":" + (seconds < 10 ? "0" + seconds : seconds);
return this;
},
toogleState: function(state) {
this.inProgress = (typeof state !== 'undefined') ? state : !this.inProgress;
if(this.inProgress) {
this.stateElement.innerHTML = "Session";
clickStart.innerHTML = '<div class="timer control startingPauseTimercontroller" id="startingPauseTimercontroller"><p class="title controlTitle" id="StartTimer">Stop Study</p></div>';
this.start();
}
else {
this.stateElement.innerHTML = "Break";
clickStart.innerHTML = '<div class="timer control startingTimercontroller" id="startingTimercontroller"><p class="title controlTitle" id="StartTimer">Start Study</p></div>';
this.pause();
}
window.navigator.vibrate(300);
return this;
}
};
var pomodoroInstance = new pomodoro();
clickStart.addEventListener('click', function () {
pomodoroInstance.toogleState();
});
BTW. there is one problem, you can't stop the timer manually so if user wants to set different time he will have to reload the page. You can add a little button which will trigger destroy() method.

I'd rather implement the logic for the clock in a separate class, after all a clock doesn't need anything but a time, our api will consist on methods to start/stop/pause the timer
The gui then creates a clock instance, whenever you click the start/stop button we just need to call the magic clock methods that control the timer, note that the clock doesn't have a method to render itself, it'd be better to have another class like ClockGUI that has an inner instance of Clock, this new class would just call methods of the Clock instance to update the timer and also update the gui
function Clock (time) {
this.timeLeft = time
this.paused = false
}
Clock.prototype.start = function () {
this.raf = requestAnimationFrame(
this._loop.bind(this)
)
}
Clock.prototype.stop = function () {
cancelRequestAnimationFrame(this.raf)
}
Clock.prototype.togglePause = function () {
this.paused = !this.paused
}
Clock.prototype._update = function (t) {
if (!this.paused) {
this.timeLeft -= t
if (this.timeLeft <= 0) {
this.stop()
}
}
}
Clock.prototype._loop = function () {
this.raf = requestAnimationFrame(this._loop.bind(this))
var now = Date.now()
var delta = now - (this.prev || now)
this._update(delta)
this.prev = now
}
// game
var timeLeft = document.querySelector('#time-left')
var input = document.querySelector('input')
var button = document.querySelector('button')
var started = false
var clock
button.addEventListener('click', function () {
button.innerText = button.innerText === 'start' ? 'pause' : 'start'
if (!started) {
started = true
clock = new Clock(input.value * 1000 * 60)
clock.start()
input.disabled = true
return
}
// toggle the state of the clock
clock.togglePause()
})
function render () {
requestAnimationFrame(render)
// render only if a clock was created
if (clock) {
var time = Math.floor(clock.timeLeft / 1000)
var minutes = Math.floor(time / 60)
var seconds = time % 60
var ms = clock.timeLeft % 1000
timeLeft.innerHTML = minutes + ':' + seconds + ':' + ms
}
}
requestAnimationFrame(render)
<div>
Time left: <span id="time-left"></span>
</div>
<button> start </button>
<input type="number" value="10">
As you've seen the clock is not controlled by setInterval but by requestAnimationFrame, the problem of having a fixed 1000 ms interval is the pause behavior you want:
pause: just call clearInterval
start: compute how much time is left that isn't part of a second e.g. timeLeft % 1000, set a timeout on that much time left and then call set interval again
You could use setInterval with a small frequency like 10ms, however it's not guaranteed that the function will be called with exactly 10ms but as close as 10ms therefore you still need to compute the time elapsed between two calls of the setInterval function, in the example above this is done on Clock.prototype._loop

Related

How to add a timer on page load that stops when button is clicked?

I want to add a timer to the page where it starts on page load.
Where it goes up in milliseconds.
Then stops when the mouse is clicked on the button.
How would I create a code example of that?
That is all I am trying to do in the code.
Add a timer that starts on page load.
Goes up in milliseconds.
Then stops when the button is clicked.
I want to be able to see the numbers going up.
https://jsfiddle.net/xvkwmndq/
// Counter
var enterDate = new Date();
function secondsSinceEnter()
{
return (new Date() - enterDate) / 1000;
}
// Usage example
document.querySelector('button').onclick = function() {
var sec = secondsSinceEnter();
if (sec < 10)
this.innerText = sec + " seconds";
else
this.innerText = 'You are here like for eternity';
};
.play {
-webkit-appearance: none;
appearance: none;
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
margin: auto;
display: flex;
justify-content: center;
align-items: center;
width: 90px;
height: 90px;
border-radius: 50%;
cursor: pointer;
border: 9px solid blue;
background: transparent;
filter: drop-shadow(3px 3px 3px #000000b3);
}
<button class="play" type="button" aria-label="Open"></button>
Related to the jsfiddle in your comment:
Don't use this to access the button. Instead, just use document.querySelector:
document.querySelector('button').onclick = function() {
var sec = secondsSinceEnter();
if (sec < 10)
document.querySelector('button').innerText = sec + " seconds";
else
document.querySelector('button').innerText = 'You are here like for eternity';
}
Then, you're just adding the time when the button is clicked. Additionally, you should call it every 0ms (every 'tick') using setInterval. So that you don't have to write the function twice, you could define it as a seperate function. Finally, remove the interval when the button is clicked.
Full script:
// Interval
var interval;
// Counter
var enterDate = new Date();
function secondsSinceEnter()
{
return (new Date() - enterDate) / 1000;
}
// Event function
function evtFct()
{
var sec = secondsSinceEnter().toFixed(3);
if (sec < 10)
document.querySelector('button').innerText = sec + " seconds";
else
document.querySelector('button').innerText = 'You are here like for eternity';
}
// Add interval to keep the current time uptodate
interval = setInterval(evtFct, 0); // Call evtFct every tick
// Usage example
document.querySelector('button').onclick = function()
{
evtFct();
clearInterval(interval); // Disable interval
}

Change If Statement to display none if text not number

I'm trying to pull through a value from the dataLayer to fire a piece of HTML. All pulling through fine, however if the value is 'undefined' rather than an actual number I don't want it to fire.
How can I solve this? Tried everything but it's returning true/false rather than not firing.
$(document).ready(function () {
for (var i = 0, len = dataLayer.length; i < len; i++) {
if (dataLayer[i].event === "productView")
var viewed = dataLayer[i].P2;
}
function isOnScreen(element) {
var curPos = element.offset();
var curTop = curPos.top - $(window).scrollTop();
var screenHeight = $(window).height();
return (curTop > screenHeight) ? false : true;
}
var intervalId = setInterval(function () {
var addtocart = $('#add-to-cart');
if (isOnScreen(addtocart) === true) {
$('.product-image.main-image').before("<div id='social-overlay' style='color: #fff;text-transform: uppercase;font-family: Muli,Arial,Helvetica,sans-serif; font-size: 11px; font-weight: 800;background: #867dae; opacity: 0.8; padding-top: 10px; padding-bottom: 10px; position: absolute;z-index: 1; width: 100%;'>" + viewed + " people viewed item in the last 24 hours</div>")
setTimeout(function () {
$('#social-overlay').fadeOut(1000);
}, 7000);
clearInterval(intervalId);
}
}, 500);
});
Check if the variable 'viewed' is undefined in the setInterval function you have and add the html value using before function only if the viewed is valid.
var intervalId = setInterval(function() {
var addtocart = $('#add-to-cart');
if ( typeof viewed !== 'undefined' && isOnScreen(addtocart) === true) {
$('.product-image.main-image').before("<div id='social-overlay' style='color: #fff;text-transform: uppercase;font-family: Muli,Arial,Helvetica,sans-serif; font-size: 11px; font-weight: 800;background: #867dae; opacity: 0.8; padding-top: 10px; padding-bottom: 10px; position: absolute;z-index: 1; width: 100%;'>" + viewed + " people viewed item in the last 24 hours</div>") ;
setTimeout(function(){
$('#social-overlay').fadeOut(1000);
}, 7000);
clearInterval(intervalId);
}
}, 500);

requestAnimationFrame gives the wrong timestamp when showing a confirm dialog (Chromium?)

I have a simple animation, which is done using requestAnimationFrame (for demo purposes adapted from the example on MDN). If before the animation I show a confirm dialog, the timestamp received by the animation function is wrong. The difference between the first and second timestamps is equal to the time from the moment the confirm message was shown, until the "OK" button was clicked. This behaviour (bug?) is visible in Chrome and Opera (both running Chromium). Firefox and Internet Explorer 11 run as expected. Check the fiddle or the example below.
const cache = {
start: null,
target: null
};
function animate(timestamp) {
console.log(timestamp);
if (cache.start === null) {
cache.start = timestamp;
}
var progress = timestamp - cache.start;
cache.target.style.left = Math.min(progress / 10, 100) + 'px';
if (progress < 1000) {
requestAnimationFrame(animate);
} else {
cache.target.style.left = 0;
cache.start = null;
}
}
(function() {
const target = document.getElementsByTagName("div")[0];
cache.target = target;
const cb = document.getElementsByTagName("input")[0];
const btn = document.getElementsByTagName("button")[0];
btn.addEventListener("click", function() {
if (cb.checked) {
if (confirm("Just click 'OK' to start the animation, ok?")) {
requestAnimationFrame(animate);
}
} else {
requestAnimationFrame(animate);
}
})
})();
html,
body {
padding: 0;
margin: 0;
}
div {
width: 50px;
height: 50px;
border: 1px solid black;
background-color: yellowgreen;
position: absolute;
top: 50px;
}
button {
margin-top: 20px;
}
<button type="button">Start</button>
<label>
<input type="checkbox" />use "confirm"</label>
<div>
</div>
Open the console to see the received timestamps. The animation is set to run for 2 seconds. When showing the confirm dialog, if the "OK" button gets clicked faster than 2 seconds, the animation runs for the "remaining" time. If the time needed to click the "OK" button is longer than the time animation time, the element will not be animated and there will be 2 values (timestamps) sent to the console; the difference of these 2 values is the time needed to click the "OK" button.
I assume that this is a bug in Chromium. Is there a workaround for this (still animating with requestAnimationFrame, not trough CSS)? I couldn't find anything regarding this in their tracker. Does anybody have additional info on this?
I have to say, I found this very interesting.
After spending to much time on it I may have found a workaround for you. You can see that here. https://jsfiddle.net/qtj467n0/13/
The basic gist of it is, I replaced the DOMHighResTimeStamp that requestAnimationFrame provides with performance.now() which also returns a DOMHighResTimeStamp.
const cache = {
start: null,
target: null,
time: 2000
};
function animate(timestamp) {
console.log(timestamp);
if (cache.start === null) {
cache.start = timestamp;
}
var progress = timestamp - cache.start;
cache.target.style.left = Math.min(progress / 10, cache.time / 10) + 'px';
if (progress < cache.time) {
requestAnimationFrame(animate);
} else {
cache.target.style.left = 0;
cache.start = null;
}
}
const render = () => {
requestAnimationFrame((timestamp) => {
const performanceNow = performance.now();
animate(performanceNow)
});
}
(function() {
const target = document.getElementsByTagName("div")[0];
cache.target = target;
const cb = document.getElementsByTagName("input")[0];
const btn = document.getElementsByTagName("button")[0];
btn.addEventListener("click", function() {
if (cb.checked) {
const confirmed = confirm("Just click 'OK' to start the animation, ok?");
if (confirmed) {
render();
}
} else {
requestAnimationFrame(animate);
}
})
})();
html,
body {
padding: 0;
margin: 0;
}
div {
width: 50px;
height: 50px;
border: 1px solid black;
background-color: yellowgreen;
position: absolute;
top: 50px;
}
button {
margin-top: 20px;
}
<button type="button">Start</button>
<label>
<input type="checkbox" />use "confirm"</label>
<div>
</div>

Custom audio player issues

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style type="text/CSS">
#custom{
font-family: monospace;
font-size: 16px;
max-width: 650px;
border-style: solid;
border-color: black;
border-width: 1px;
border-radius: 5px;
padding: 8px;
padding-bottom: 15px;
padding-left: 12px;
padding-right: 12px;
}
img{
margin-top: 3px;
float: left;
}
#bar, #currentTime, #duration, #skip{
display: inline-block;
}
#currentTime, #duration, #skip{
margin: 0px;
padding: 0px;
margin-top: 3px;
margin-left: 10px;
}
#bar{
margin-top: 10px;
height: 14px;
width: 400px;
background: lightgrey;
border-radius: 50px;
margin-left: 9px;
}
#slider{
height: 14px;
width: 15px;
background: black;
border-radius: 50px;
}
</style>
</head>
<body onLoad="count()">
<script type="text/javascript">
var track = 0;
function count(){
var music = document.getElementById("myAudio");
var duration = music.duration;
var durationInMins = Math.floor(duration/60);
var durationInSecs = Math.floor(duration-(durationInMins*60));
if(durationInSecs < 10){
var durationInSecs = "0" + durationInSecs;
}
if(durationInMins < 10){
var durationInMins = "0" + durationInMins;
}
document.getElementById("duration").innerHTML = durationInMins + ":" + durationInSecs;
var timer = setInterval(
function(){
var music = document.getElementById("myAudio");
var currentTime = music.currentTime;
if(currentTime > 60){
var min = Math.floor(currentTime/60);
var sec = Math.floor(currentTime-(min*60));
}else{
var min = "0";
var sec = Math.floor(music.currentTime); }
if(sec < 10){
var sec = "0" + sec;
}
if(min < 10){
var min = "0" + min;
}
document.getElementById("currentTime").innerHTML = min + ":" + sec; var percent = 100 * (music.currentTime/duration) - 2;
document.getElementById("slider").style.marginLeft=percent + "%";
}
, 1000);
}
function toggleP(){
var music = document.getElementById("myAudio");
if(music.paused == true){
music.play();
}else if(music.paused == false){
music.pause();
}
}
function skip(){
var trackList = ["http://fidelak.free.fr/reprises/The%20Doors%20-%20People%20are%20Strange.mp3", "http://mp3light.net/assets/songs/14000-14999/14781-december-1963-oh-what-a-night-four-seasons--1411568407.mp3"];
if(go == "back"){
track = track - 1;
}
if(go == "forward"){
track = track + 1;
}
var aa = clearInterval("timer");
var music = document.getElementById("myAudio");
music.pause();
music.src=trackList[track];
music.load();
var a = setTimeout(function(){music.play(); count();} , 6000);
}
</script>
<audio id="myAudio" src="http://fidelak.free.fr/reprises/The%20Doors%20-%20People%20are%20Strange.mp3">
</audio>
<br>
<div id="custom">
<img onClick="toggleP()" src="img/media-play-pause-resume.png" height="30px"/>
<p id="currentTime">00:00</p>
<div id="bar">
<div id="slider"></div>
</div>
<p id="duration">00:00</p>
<p id="skip"><strong><a onClick="go = 'back'; skip()"><<</a> <a onClick="go = 'forward'; skip()">>></a><strong></p>
</div>
</body>
Could anyone tell me why the song duration slider jumps forwards and backwards after you skip to the second song? And why the duration bar cannot be moved down with margin-top without moving everything with it. I just can't figure it out. Any help would be greatly appreciated, Thanks.
jsBin demo
Don't use inline JS in your HTML! Makes code hard to debug. Keep your logic away from your presentation/template.
To start from, how variables work?
Once you set a var, there's no need to instantiate the same var again using var inside your code. Simply use/modify it. So once you set at the top
function el(id){return document.getElementById(id);} // Helps get easily an element
var el_music = el("myAudio"), // see?
el_trackInfo= el("trackInfo"),
el_duration = el("duration"),
el_currTime = el("currentTime"),
el_slider = el("slider"),
el_prev = el("prev"), // assign ID to your prev/next buttons!
el_next = el("next"),
el_togg = el("toggle"),
currentTime,
trackList = [],
track = -1, // Later we'll set it to 0 index triggering auto start
totTrack = trackList.length;
...you're good to go. No more var statements further in your code.
You probably want to show some more info to the user.
A good way to store your data is to create Objects with the desired properties:
trackList = [
{
artist : "The Doors",
fileName : "People Are Strange",
file : "http://fidelak.free.fr/reprises/The%20Doors%20-%20People%20are%20Strange.mp3"
},
{
artist : "ACDC",
fileName : "Back In Black",
file : "http://upload.wikimedia.org/wikipedia/en/4/45/ACDC_-_Back_In_Black-sample.ogg"
},
{
artist : "Four Seasons",
fileName : "Oh What A Night",
file : "http://mp3light.net/assets/songs/14000-14999/14781-december-1963-oh-what-a-night-four-seasons--1411568407.mp3"
}
]
now you can not only get the desired audio path, but also show the user more info about a track.
Don't Repeat Yourself. Calculating times all over the place makes your code not modular but messy. Instead create a function that'll help you return the desired formatted time:
function getTime(t) { // `t` is some time value
var m = ~~(t / 60),
s = ~~(t % 60);
return (m<10?"0"+m:m) +':'+ (s<10?"0"+s:s); // returns i.e: "01:25"
}
Create a progress function like:
function progress() {
el_currTime.innerHTML = getTime(el_music.currentTime); // see how our getTime fn is used?
el_duration.innerHTML = getTime(el_music.duration);
el_slider.style.marginLeft = Math.floor(100/el_music.duration*el_music.currentTime) + "%";
}
than a play/pause one:
function playPause(){
var isPaused = el_music.paused;
el_music[isPaused ? "play" : "pause"]();
el_togg.innerHTML = isPaused ? "❚❚" : "►" ;
}
for the PREV/NEXT, assign IDs to your buttons id="prev" and id="next" and again create a function that will handle both click cases:
function skip(){ // This gets triggered by both prev / next buttons.
track = this.id==="next" ? ++track : --track; // Next clicked' increment, else decr.
track = track < 0 ? totTrack-1 : track%totTrack; // Allows you to loop infinitely the index
var trackToPlay = trackList[ track ]; // Get the Track Object "{}"
el_trackInfo.innerHTML = trackToPlay.artist+' '+trackToPlay.fileName;
el_music.src = trackToPlay.file;
el_music.addEventListener('canplaythrough', el_music.play);
}
Believe it or not - you're done!
Having all those nifty functions in place, what you need now is some event listeners:
el_prev.addEventListener("click", skip);
el_next.addEventListener("click", skip);
el_togg.addEventListener("click", playPause);
el_music.addEventListener("timeupdate", progress);
el_music.addEventListener("ended", playPause);
el_next.click(); // Auto Start playing!
Now you probably wonder where's your interval 1000 function gone? It's simply handled by el_music.addEventListener("timeupdate", progress);.
The skipping may be caused by the fact that the interval is never stopped, and is still running for the previous song.

Stopwatch in seconds and milliseconds

Hi I'm working on a reaction test that checks how fast it takes for the user before they react, and I can't really find what I'm looking for. I was just wondering how to make a simple stopwatch in seconds and milliseconds so I can call the function later in HTML onclick.
Here is my code so far:
HTML
<input type="button" class="first" value="Call" onclick="call function" />
and that's pretty much it... I know HTML, CSS, JavaScript and a little bit jQuery.
Thanks in advance !
As a basic starting point you want to use setInterval to increment the stopwatch, with an additional function to toggle/reset it.
You can then either bind the click handler directly to the button (per below), or - I would tend to recommend you bind it in the Javascript itself (e.g. with .onclick=function(){....})
var s = 0,
ms = 0,
sEl = document.getElementById('s'),
msEl = document.getElementById('ms'),
play = false;
stopwatch = setInterval(function() {
if (!play) return;
if (ms === 99) {
s += 1;
ms = 0;
} else {
ms += 1;
}
update();
}, 1);
function update() {
sEl.innerText = s;
msEl.innerText = ms;
}
function toggle() {
if (!play) {
s = 0, ms = 0;
update();
}
play = !play;
}
body {
font-family: arial;
}
#s {
font-size: 50px;
}
#ms {
font-size: 30px;
display:inline-block;
width:35px;
}
button {
border: 1px solid grey;
background: lightgrey;
padding: 10px 40px;
}
<span id="s">0</span>s <span id="ms">00</span>ms
<br />
<button onclick="toggle();">Toggle</button>

Categories