Stopwatch in seconds and milliseconds - javascript

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>

Related

javascript - repeat action at regular interval after onlick has been triggered

I have a div which is displayed with onclick then which disappear with setTimeout:
css
#light {
position:absolute;
left:40px;
top:45px;
border-left:50px solid transparent;
border-right:50px solid transparent;
border-bottom:20px solid red;
opacity:0;
}
js (don't know if the syntax is correct but it works)
function change() {
var element = document.getElementById("light");
element.style.opacity = "1";
element.style.transitionDelay = "4s", // only the 1stime with onclick
setTimeout(() => {
element.style.opacity = "0";
}, 5000)
}
html
<button onclick="change()">light</button>
<div id="light"></div>
What I would like is that this action repeats then by itself every 2 minutes:
- after 2min, #light is displayed again for 5s (opacity="1")
- then hidden again (opacity="0")and so on, and so every 2 min.I know about the setInterval() method but it's too difficult for me to use it correctly - the script doesn't work at regular interval (every 2 min).ps: I've looked similar questions but all this is beyond my competence (ie, 0).
I'm not sure why you can't use timeouts?
Wouldn't something like this work?
( you can adjust the timers... I didn't want to wait minutes to see the light blink so I set it to few seconds)
let timer;
let started = false;
let delayTimer;
const lightOn = (clicked) => {
// do nothing if clicked for the second time
if (clicked && started) {return;}
const fn = () => {
const element = document.getElementById("light");
element.classList.add('light-on');
timer = setTimeout(lightOff, 1000);
};
if (clicked) {
delayTimer = setTimeout(fn, 3000);
} else {
fn();
}
started = true;
}
const lightOff = () => {
const element = document.getElementById("light");
element.classList.remove('light-on');
timer = setTimeout(lightOn, 2000);
}
const stop = () => {
clearTimeout(timer);
clearTimeout(delayTimer);
timer = undefined;
delayTimer = undefined;
started = false;
}
.light {
background-color: gray;
opacity: 0.1;
width: 2em;
height: 2em;
display: inline-block;
border: 1px solid black;
border-radius: 2em;
}
.light-on {
background-color: yellow;
opacity: 1;
}
<div id="light" class="light"></div>
<button onclick="lightOn(true)" style="display: block;">start</button>
<button onclick="stop()" style="display: block;">stop</button>

How to achieve this typing & deleting effect?

I’d like to replicate this website’s typing & deleting effect: http://www.cantercompanies.com.
I’ve been trying to figure this out using code blocks, HTML, CSS, and JS. However, I can’t seem to get this to work after hours and days of trying.
It’s obviously on my end, as I am fairly new to coding.
I really want this exact typing & deleting effect with blinking cursor. I of course will be using my own logo and fixed text, but need some guidance and help to replicate Canter's example above in the link provided… :-)
You don't need any library,
HTML
<div class="flex">
<p class="header-sub-title" id="word"></p><p class="header-sub-title blink">|</p>
</div>
CSS
#import 'https://fonts.googleapis.com/css?family=Roboto';
html, body {
background-color: #000;
}
h2, a, p {
color: #fff;
font-family: Roboto;
text-decoration: none;
}
p>a {
color: #fd084a;
}
.blink {
animation: blink 0.5s infinite;
}
#keyframes blink{
to { opacity: .0; }
}
.flex {
display: flex;
}
.header-sub-title {
color: #fff;
font-family: "Courier";
font-size: 20px;
padding: 0.1em;
}
JS
const words = ["CSS3.", "HTML5.", "javascript."];
let i = 0;
let timer;
function typingEffect() {
let word = words[i].split("");
var loopTyping = function() {
if (word.length > 0) {
document.getElementById('word').innerHTML += word.shift();
} else {
deletingEffect();
return false;
};
timer = setTimeout(loopTyping, 500);
};
loopTyping();
};
function deletingEffect() {
let word = words[i].split("");
var loopDeleting = function() {
if (word.length > 0) {
word.pop();
document.getElementById('word').innerHTML = word.join("");
} else {
if (words.length > (i + 1)) {
i++;
} else {
i = 0;
};
typingEffect();
return false;
};
timer = setTimeout(loopDeleting, 200);
};
loopDeleting();
};
typingEffect();
Reference:https://codepen.io/haaswill/pen/VKzXvZ

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>

Having issues pausing the setInterval and then continuing

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

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.

Categories