I have an <audio> object that might need to load before it can play, is there a way to trigger a function when either the sound begins after downloading, or when the user clicks the play button?
There is a playing event you can listen to like so.
audio.addEventListener("playing", function() {
console.log("playing");
});
http://w3c.github.io/html/semantics-embedded-content.html#eventdef-media-playing
Yes, there is onplay event for audio and video, you should use <audio> tag to do it
var audio = document.getElementById("Your_audio");
audio.onplay = function() {
//your code
};
with the javascript audio object you can add some event listener, like :
var horn = new Audio('car_horn.wav');
horn.addEventListener('loadeddata', () => {
let duration = horn.duration;
// The duration variable now holds the duration (in seconds) of the audio clip
})
as you can see here : https://developer.mozilla.org/en-US/docs/Web/API/HTMLAudioElement
you can simply add an evenListner like this:
<audio|video onplay="myScript">
e.g. you can do like this:
var aud = document.getElementById("myAudio");
aud.onplay = function() {
alert("The audio has started to play");
};
Related
I am trying to figure out how to trigger an audio file when a button is hovered and has a specific number displayed it will play a specific audio file.
The first code does not work for me.
The second code works but not correctly and some how doesnt take into consideration what is being displayed on the button.
//First-Code - Assigns answer-buttons with an event listener
document.getElementById('answer-buttons').addEventListener("mouseover", answerHoverButtonAudio);
//Button Function to call answerHoverButtonAudio
function answerHoverButtonAudio() {
if (document.getElementById('answer-buttons').innerText == ('1')) {
var audio = document.getElementById('Number-1')
audio.play();
}
}
//Second-Code - This code below triggers the audio but does it no matter what text is showing on the button somehow.
//Assigns answer-buttons with an event listener
document.getElementById('answer-buttons').addEventListener("mouseover", answerHoverButtonAudio);
//Button Function to call answerHoverButtonAudio
function answerHoverButtonAudio() {
if (answerButtonsElement.innerText.includes('1')) {
var audio = document.getElementById('Number-1')
audio.play();
}
}
You can use event passed via mouseevent listener which can check innerText to play particular audio.
code sandbox - https://codesandbox.io/s/amazing-hypatia-j76ggx?file=/src/index.js
const answerHoverButtonAudio = (event) => {
if (event.currentTarget.innerText.includes("1")) {
debugger;
var audio = document.getElementById("Number-1");
audio.play();
}
};
document
.getElementById("answer-buttons")
.addEventListener("mouseover", answerHoverButtonAudio);
<div id="answer-buttons">1</div>
<audio controls id="Number-1">
<source src="https://www.w3schools.com/tags/horse.ogg" type="audio/ogg" />
Your browser does not support the audio element.
</audio>
I have created a site with image thumbnails of people I have photographed. When a visitor clicks on one of the thumbnails the full image is revealed using jQuery, and an audio introduction plays. I have a different audio introduction for each thumbnail/image combination - 15 at present with more being added daily.
I would like to ensure that if a visitor clicks on another thumbnail before the previous audio file has completed, that the previous audio file is stopped/paused to allow the new audio file to be played - thereby ensuring two or more tracks do not play simultaneously.
I am currently using the following snippet of code, wrapped in an anonymous function, to play each audio file individually when the appropriate thumbnail is clicked - so this snippet is duplicated for each audio file, but don't know how to ensure they do not play over one another.
$(".bridget-strevens").click(function(){
var audio = $('#bridget-strevens-intro')[0];
if (audio.paused){
audio.play();
} else {
audio.pause();
}
});
Any help you could give me would be very grateful, as I am just starting to learn jQuery, and don't have the knowledge to come up with a workable solution.
Thanks in advance for your help!
Add a .audio class to all your audio elements and loop through all of them when an audio is clicked.
$(".bridget-strevens").click(function () {
$('.audio').each(function (index, value) {
if (!value.paused) {
value.pause();
}
});
var audio = $('#bridget-strevens-intro')[0];
if (audio.paused) {
audio.play();
} else {
audio.pause();
}
});
If that seems too heavy for you then simply add the audio element in a global variable such as:
var currentAudio;
Then when a new audio is clicked, simply pause that one, play the new one and update the currentAudio variable with the new element currently being played.
var currentAudio = null;
$(".bridget-strevens").click(function () {
if(currentAudio != null && !currentAudio.paused){
currentAudio.pause();
}
var audio = $('#bridget-strevens-intro')[0];
if (audio.paused) {
audio.play();
currentAudio = audio;
} else {
audio.pause();
}
});
Update:
Thanks for the prompt responses! Grimbode, I've tried what you
suggest, and that seems to work. However is there the ability to stop
and reset rather than just pause - so if they clicked on 1 then [2]
before 1 finished, then clicked 1 again, that 1 would start from
the beginning again rather than the point at which it was paused? And
is there any way of check the state 'globally', and then add code for
each individual audio file - just to keep the amount of code and
duplication down? Thanks again!! –
Yes. Play audio and restart it onclick explains in detail how to do this. The final result would look something like this:
var currentAudio = null;
$(".bridget-strevens").click(function () {
if(currentAudio != null && !currentAudio.paused && currentAudio != this){
currentAudio.pause();
//Here we reset the audio and put it back to 0.
currentAudio.currentTime = 0;
}
var audio = $('#bridget-strevens-intro')[0];
if (audio.paused) {
audio.play();
currentAudio = audio;
} else {
audio.pause();
}
});
You can't really optimize the code much more. You're going to have apply the click event on every audio element. You're going to have to keep the current playing audio element memorized so you don't have to loop through all the audio files.
If you really want to take this further you could create a library to handle everything. Here is an example:
(function(){
var _ = function(o){
if(!(this instanceof _)){
return new _(o);
}
if(typeof o === 'undefined'){
o = {};
}
//here you set attributes
this.targets = o.targets || {};
this.current = o.current || null;
};
//create fn shortcut
_.fn = _.prototype = {
init: function(){}
}
//Here you create your methods
_.fn.load = function(){
//here you load all the files in your this.targets.. meaning you load the source
//OR you add the click events on them.
//returning this for chainability
return this
};
//exporting
window._ = _;
})();
//here is how you use it
_({
targets: $('.audio')
}).load();
Description of Problem (Fiddle):
Clicking the #test box multiple times creates layers of audio playing simultaneously and briefly pauses or staggers the .fadeOut() effect with each press. I want to prevent subsequent mousedown events from firing so that the .fadeOut() effect completes seamlessly and doesn't trigger additional audio plays.
Code:
$('#test').mousedown(function() {
var sound = new Audio("http://sounddogs.com/previews/3668/mp3/824357_SOUNDDOGS__ri.mp3");
sound.play();
$('#test').stop(true).fadeOut(2000);
});
This will fire an event a single time: http://api.jquery.com/one/
The easy way:
var clicked = 0;
$('#test').mousedown(function () {
if (clicked == 0) {
clicked = 1;
var sound = new Audio("http://sounddogs.com/previews/3668/mp3/824357_SOUNDDOGS__ri.mp3");
sound.play();
$('#test').stop(true).fadeOut(2000);
setTimeout(function(){clicked = 0;}, 2000);
}
});
http://jsfiddle.net/yxDSL/1/
$('#test').one('click',function() { //would only trigger on first click
var sound = new Audio("http://sounddogs.com/previews/3668/mp3/824357_SOUNDDOGS__ri.mp3");
sound.play();
$('#test').stop(true).fadeOut(2000);
$('#test').click(function(){ // for subsequent click write the function inside this
});
});
This'll do the trick...
$('#test').mousedown(function() {
var sound = new Audio("http://sounddogs.com/previews/3668/mp3/824357_SOUNDDOGS__ri.mp3");
sound.play();
$('#test').unbind("mousedown").stop(true).fadeOut(2000);`
});
I want the user to have the option to skip the preroll ad after a specified time (say 5 secs into the ad). Then the normal video would play. How can I achieve this? Currently I have something inline of this:
var adManager = function () {
var adSrc = url,
src = "http://media.w3.org/2010/05/sintel/trailer.mp4";
var adEnded = function () {
videoPlayer.removeEventListener("ended", adEnded, false);
videoPlayer.removeEventListener("playing", adPlaying, false);
$("#companionad").hide();
videoPlayer.src = src;
videoPlayer.load();
videoPlayer.play();
console.log(videoPlayer.currentTime);
};
var adPlaying = function () {
var companionad = $(responseXML).find("HTMLResource").text();
$("#companionad").html(companionad);
console.log(videoPlayer.currentTime);
}
return {
init: function () {
videoPlayer.src = adSrc;
videoPlayer.load();
videoPlayer.addEventListener("ended", adEnded, false);
videoPlayer.addEventListener("playing", adPlaying, false);
if (videoPlayer.currentTime > 5) {
$("#skip").show();
}
console.log(videoPlayer.currentTime);
}
};
}();
adManager.init();
What I am trying to do is:
if (videoPlayer.currentTime > 5) {
$("#skip").show();
}
show the skip button and continue to normal video. Is there any event that is continually fired as the video play progresses?
Do your check against the media.currentTime property in a handler for the timeupdate event. See documentation:
The timeupdate event is fired when the time indicated by the
currentTime attribute has been updated.
Just as an aside, this HTML5 video demo page is a really handy reference for playing around with the various properties and events available to you.
I have this audio tag playing in the background, and I'm able to store the progress in seconds to a cookie.
But in no way I'm able to start the audio from that cookie. (for continuing on other pages)
$("p#sound audio").currentTime = $.cookie("audioTime");
<audio autoplay="autoplay" loop="loop" ontimeupdate="document.getElementById('tracktime').innerHTML = Math.floor(this.currentTime); $.cookie('audioTime', Math.floor(this.currentTime));">
<source src="audio/song.ogg" type="audio/ogg" />
<source src="audio/song.mp3" type="audio/mp3" />
Your browser does not support the audio tag.
</audio>
<span id="tracktime">0</span>
Does this have to do with the song being loaded again from start?
Thanks!
EDIT:
$("p#sound audio").get[0].currentTime
With .get[0], it doesn't work either.
Can someone please clear things up for me? Greatly appreciated!
You need to wait until audio source loads before you set the current time.
$(function(){
$('audio').bind('canplay', function(){
$(this)[0].currentTime = $.cookie('audioTime');
});
});
You can set the start time by adding t=<time> to the URL, as documented here: https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Using_HTML5_audio_and_video#Specifying_playback_range
E.g. <audio src="http://example.com/audio.mp3#t=50></audio>
At first there is an error in your code because currentTime is not a part of jQuery (but you already know this)
$("p#sound audio").currentTime // is incorrect (refers to a property of jQuery)
$("p#sound audio")[0].currentTime // is correct (refers to a property of DOM element)
I discover that the audio tag has some strange things and can be operate differently from browser to browser, for example in Chrome.
At first you have to wait for the 'durationchange' event to be sure the length is known by the object.
After this you have to start the stream with 'play()' (if not already started) and pause it (sometimes after a short delay) with the 'pause()' function. Then you can change the 'currentTime' property with the value. After this you have to start the stream again by using the 'play()' function.
Also it is sometimes needed to load the stream by yourself by using the 'load()' function.
Something like this:
$(document).ready( function()
{
var a = $('audio:first'),
o = a[0];
a.on( 'durationchange', function(e)
{
var o = e.target;
if( o )
{
o.pause();
o.currentTime = parseInt( $.cookie("audioTime"));
o.play();
}
});
if( o )
{
o.load();
o.play();
}
});
You have to play with it to be sure what is the best in your situation, for example the resume (play again) method to delay it for a second or so.
When using this method you don't have to use the autoplay feature because most of the time it doesn't work.
Hope it helps, greetz,
Erwinus
what I found in my case is that there is an issue with context somewhere. I initialize audio under the window context but when I try to change currentTime from XMLHttpRequest response it does NOT work. I don't know the answer yet but I'm providing a clue maybe an expert in Javascript will know how to make it work.
/* initialize this.audio player */
Audio = function() {
var that = this;
// keep track of playback status
var AudioStatus = {
isPlaying : false
};
// define references to this.audio, pulldown menu, play-button, slider and time display
that.audio = document.querySelector("AUDIO");
/* load track by menu-index */
var loadTrack = function() {
if(that.audio == null){
log("audio null"); return;
}
that.audio.src = '../sounds/400.mp3';
that.audio.load();
};
/* callback to play or pause */
that._play = function() {
if(that.audio == null){
log("audio null"); return;
}
that.audio.play();
AudioStatus.isPlaying = true;
};
that._pause = function() {
if(that.audio == null){
log("audio null"); return;
}
that.audio.pause();
AudioStatus.isPlaying = false;
};
that.playPause = function() {
if (that.audio.paused) {
self._play();
}
else {
self._pause();
}
};
/* callback to set or update playback position */
that.updateProgress = function(value) {
if(that.audio == null){
log("audio null"); return;
}
that.audio.currentTime = value; // <<<--- it does NOT work if I call from XMLHttpRequest response but it DOES if it is called from a timer expired call back
};
that.isAudioPlaying = function(){
return AudioStatus.isPlaying;
};
};
This works for me.
if (!isNaN(audio.duration)) {
audio.currentTime = 0;
}
Hope it helps!
This solved it for me:
$("p#sound audio").on('loadedmetadata', () => {
$("p#sound audio").get(0).load();
});
So I could later set the currentTime without worrying about whether the audio was loaded or not.
I had a similar problem when trying to play a HLS stream through an HTML audio element.
No matter where and how I tried to set the currentTime property on the audio element, it wouldn't work until about 2.5s after calling audioElement.play().
//Calling this
htmlElement.currentTime = 5.5;
console.log('currentTime: '+htmlElement.currentTime);
//Would return 'currentTime: 0';
What did work tho, was if I called that in a timeout with 2500-3000ms delay.
After a day of debugging to pinpoint the element state in which it would allow me to set the currentTime property, so I wouldn't have to rely on a fixed timeout.
My solution was to listen to 3 HTMLMediaElement events (https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement#events): loadedmetadata, loadeddata and canplaythrough and setting custom flags when they were first reported.
When all 3 were reported and the HTMLMediaElement.duration property was bigger than 0, I set the HTMLMediaElement.currentTime to my desired value inside the first HTMLMediaElement timeupdate event callback.
This can be summed up in the following code snippet:
let audioElementStates = {
'metadataloaded': false,
'dataloaded': false,
'canplaythrough': false
};
let shouldSetOldTime = true;
let audioElement = document.getById('audio-element');
audioElement.src = 'https://somedomain.com/hlsplaylist.m3u8';
audioElement.addEventListener('loadedmetadata',()=>{audioElementStates.metadataloaded = true;});
audioElement.addEventListener('loadeddata',()=>{audioElementStates.dataloaded = true;});
audioElement.addEventListener('canplaythrough',()=>{audioElementStates.canplaythrough = true;});
audioElement.addEventListener('timeupdate',()=>{
if(shouldSetOldTime &&
audioElement.duration>0 &&
audioElementStates.metadataloaded &&
audioElementStates.dataloaded &&
audioElementStates.canplaythrough){
audioElement.currentTime = 5.5;
shouldSetOldTime=false;
}
});
audioElement.play();
For reference I was using this with Vue.js in a Quasar framework mobile app packaged with Apache Cordova.
NOTE: The loadedmetadata check can probably be skipped, since loadeddata would presumably never fire before loadedmetadata.