Starting and Stopping Audio in Javascript - javascript

I have a function tied to an onClick event. It's supposed to play a song. If there's a song already playing it's supposed to stop the current song and start playing the new one. The only problem is that as far as I know there is only a pause method and it means that the previous song will resume from the paused position and not the beginning. Is there any way around this (like a .stop() method)?
Here's my code:
var currSong="";
function playSong(newSong){
alert(newSong);
if (newSong != ""){
if(currSong != "" ) {
document.getElementById(currSong).pause();
}
document.getElementById(newSong).play();
currSong = newSong;
}
}

From looking at the MDC documentation for the audio element, it appears that the correct way to do this is by setting the currentTime property:
function playSong(newSong){
alert(newSong);
if (newSong != ""){
if(currSong != "" ) {
document.getElementById(currSong).pause();
}
var newSongEl = document.getElementById(newSong);
newSongEl.currentTime = 0;
newSongEl.play();
currSong = newSong;
}
}
Also, it would be better to store the relevant element in currSong, rather than its ID, unless you are using the ID for something else.

Related

How to add eventListener function to elements with unique id?

I have some dynamic cards:
and I want to add eventListener to all play buttons (their code is):
<a href="link">
<img src="play.svg">
<audio src="myMusic.mp3"></audio>
</a>
When I click to "play", it should look like below:
(it should change image and should also play audio)
My JS code so far done is:
<script>
var imgsPlay = document.getElementsByClassName('play-img-loop');
var audioPlayers = document.getElementsByClassName('audio-player');
window.onload = function() {
for(var i = 0; i < imgsPlay.length; i++) {
imgsPlay[i].addEventListener('click', function(event) {
if(this.getAttribute('src') == "img/icons/play.svg") {
this.setAttribute('src', "img/icons/play_red.svg");
audioPlayers[i].play();
} else if(this.getAttribute('src') == "img/icons/play_red.svg") {
this.setAttribute('src', "img/icons/play.svg");
audioPlayers[i].pause();
}
});
}
}
</script>
(I can do it manually, but) can not dynamically. How can I do this ?
You need to delegate! If not, then your question is just a dupe of JavaScript closure inside loops – simple practical example
Here I assume you have a container called cardContainer
I am a little confused as to _red means playing or paused, so change the code below to match. Your example HTML does not match the code you show, there are no classes on the player and image, I therefor assume you do have those in the actual code
window.addEventListener('load', function() {
document.getElementById('cardContainer').addEventListener('click', function(e) {
const tgt = e.target;
if (tgt.classList.contains('play-img-loop')) {
const src = tgt.getAttribute('src');
const running = src.includes('_red');
const audioPlayer = tgt.closest('a').querySelector('audio');
tgt.setAttribute('src', `img/icons/play$(running?'':'_red').svg`);
if (running) audioPlayer.pause();
else audioPlayer.play();
}
});
});
Your problem is the incorrect handling of closures. The event listener call back will never get the correct value of i by the way you have written it. The value of i will always be the last one when the events are fired.
The easiest workaround is to define the variable i only for the scope of a single iteration - which is what the let do.
for(let i = 0; i < imgsPlay.length; i++) {
imgsPlay[i].addEventListener('click', function(event) {
if(this.getAttribute('src') == "img/icons/play.svg") {
...
}
Useful reference: setTimeout in for-loop does not print consecutive values

JavaScript audio object stopping in the middle of playing

In a project I'm working on, I have a JS file that should play a piece of music, and when that piece is done playing, the next one takes its place.
var soundtrack = {
0: new Audio("./music/piece1.ogg"),
2: new Audio("./music/piece2.ogg"),
1: new Audio("./music/piece3.ogg"),
3: new Audio("./music/piece4.ogg")
}
var currentTrack = 0;
function startMusic(){
soundtrack[currentTrack].play();
soundtrack[currentTrack].addEventListener("ended", function(){ nextSong()});
}
function nextSong(){
currentTrack += 1;
if(currentTrack == 4){
currentTrack = 0;
}
soundtrack[currentTrack].play();
}
$( document ).ready(function() {
setTimeout(function() { startMusic(); }, 15500);
});
My use of the object "soundtrack" may be unconventional, but there was a bug using an array so I tried this and it worked.
The first piece plays through fine, and the next one starts up fine. About maybe 10 seconds in, the playing just stops.
Any help to solve this issue would be greatly appreciated, thanks!
Note: I am aware not all pieces will play after one other, I'm waiting until this issue is fixed before continuing.

Onended switch to Next track

I'm trying to build an MP3 player. Here I want to perform a switch to the next song once the current tack ends.
Here is a part of my code:
function clicki(ID, norrnd) {
if (norrnd == 'nor') {
$('.liActive').removeClass('liActive');
$('#' + ID).next().addClass('liActive');
// Set track Data
$('audio').attr('id', 'audioNor');
$('audio').attr('src', $('.liActive').attr('rel'));
// Play
var player = $('.tbd').get(0);
player.play();
$('#pause').removeClass('hi');
$(player).bind('ended', clicki(ID++, 'nor')); //as well as .onfinish result in TOO MUCH RECURSION error.
}
}
<audio controls id="" class="tbd" type="audio/mpeg" src="Ella Fitzgerald - It Don't Mean a Thing.mp3"></audio>
So when I just initiate clicki() first time the page instantly freezes:
Too Much Recursion.
How do I make the track be automatically switched with no Recursion flood?
You're calling the function immediately -- you need to create a function around it:
function clicki(ID, norrnd) {
if (norrnd === 'nor') {
$('.liActive').removeClass('liActive');
$('#' + ID).next().addClass('liActive');
// Set track Data
$('audio').attr('id', 'audioNor');
$('audio').attr('src', $('.liActive').attr('rel'));
// Play
var $player = $('.tbd');;
$player.get(0).play();
$('#pause').removeClass('hi');
// if you call it now, it's going to keep calling itself
$player.bind('ended', function () {
clicki(ID++, 'nor');
});
}
}

Is there a way to have jPlayer's timeupdate execute only once a second?

I am trying to create a pop out div (related-products) of items related to specific times in a video with jPlayer. Here is my code:
$("#jquery_jplayer_1").bind($.jPlayer.event.timeupdate, function(event) {
var requestProducts = xml.getElementsByTagName("product");
for(var i = 0; i < requestProducts.length; i++){
var mark = (requestProducts[i].getElementsByTagName("mark")[0].childNodes[0] == undefined) ? '' : requestProducts[i].getElementsByTagName("mark")[0].childNodes[0].nodeValue;
var item_name = (requestProducts[i].getElementsByTagName("item_name")[0].childNodes[0] == undefined) ? '' : requestProducts[i].getElementsByTagName("item_name")[0].childNodes[0].nodeValue;
var item_url = (requestProducts[i].getElementsByTagName("item_url")[0].childNodes[0] == undefined) ? '' : requestProducts[i].getElementsByTagName("item_url")[0].childNodes[0].nodeValue;
var item_thumb_url = (requestProducts[i].getElementsByTagName("item_thumb_url")[0].childNodes[0] == undefined) ? '' : requestProducts[i].getElementsByTagName("item_thumb_url")[0].childNodes[0].nodeValue;
var newItem = ("<li><a href='"+item_url+"' target='_blank'><img src='"+item_thumb_url+"'><br /><strong>"+item_name+"</strong></a></li>");
if ($.jPlayer.convertTime(event.jPlayer.status.currentTime) == mark){
if (i < 1){
$("#related-products").css('display','block');
$("#related-products").html("<h4>Related Products</h4><ul class='slider'></ul>");
}
$(".slider").append(newItem);
$("#related-products").scrollTop(208*i);
}
}
});
I am using the jPlayer event timeupdate along with convertTime to get the time in seconds to match a preset time (mark) for each item to be displayed. The problem I am having is that timeupdate returns 8 times per second, so the items are being added 8 times instead of just once. If I change the code to if ($.jPlayer.convertTime(event.jPlayer.status.currentTime) > timecode){ it will add each item only once, but then it will continually execute $("#related-products").scrollTop(208*i); so the scrolling of items in the div doesn't work properly. Is there a way to either get timeupdate to happen only once a second or to get $("#related-products").scrollTop(208*i); to only happen once? I am a bit of a noob at this, so any help would be greatly appreciated!
You can use a boolean to know if you already did your function call or not.

How do I stop all currently playing MediaElement players?

I'm wondering how to stop all the MediaElement players currently in the DOM. I've tried this:
$('video,audio').each(function() {
$(this)[0].player.pause();
});
Let me know if that works.
A quick and dirty one, but neither work.
$(".mejs-play").live('click',function(){
$(".mejs-pause").trigger('click');
});
Tried to do my homework on this one but can't seem to find a response anymore.
Try this...
$('video, audio').each(function() {
$(this)[0].pause();
});
Here's a simple way to do a "stop all".
When first creating your MediaElement players, create them each explicity (as opposed to using $('video,audio').mediaelementplayer()):
var mediaElementPlayers = [];
$('video,audio').each(function(){
mediaElementPlayers.push(new MediaElementPlayer(this));
});
And then, when you want to stop them:
for (var i=0; i<mediaElementPlayers.length; i++){
mediaElementPlayers[i].pause(); // pause
if (mediaElementPlayers[i].getCurrentTime()){
mediaElementPlayers[i].setCurrentTime(0); // rewind
}
}
Very nice #Bart
And here is an addition to Barts function to stop audio in a certain context (within a certain element). Useful to stop media playing in a popup when it is closed for example:
function stopAudio(context) {
for (var i=0; i<mediaElementPlayers.length; i++){
if($(mediaElementPlayers[i].container).parents().find(context).length === 0) {
mediaElementPlayers[i].pause(); // pause
if (mediaElementPlayers[i].getCurrentTime()){
mediaElementPlayers[i].setCurrentTime(0); // rewind
}
}
}
}
Trigger the click event of pause button will work....check it here
$('.mejs-pause button').trigger("click");

Categories