First of all, I know about the fact that a video on a website, under iOS, will play only on user action (unless a click event is set).
I have a HTML5 video element:
<video muted="muted" loop id="js-b2b-video">
<source src="my_video.mp4" type="video/mp4">
</video>
Then, in JS, there's a piece of code listening to the scroll event of the window:
$(window).on('scroll', function() {
var $video = $('#js-b2b-video');
// Check if the video is in the viewport
// and if YES, play it:
$video.get(0).play();
});
The code above works on Windows and macOS - in Chrome, Edge, Firefox and Safari. However, on any browser running on iOS9 (e.g. on iPad 1) - nothing happens, the video does not start.
So, I tried to simulate a click:
// Listener
$('#js-b2b-video').on('click', function() {
$(this).get(0).play();
});
// Modified scroll listener
$(window).on('scroll', function() {
var $video = $('#js-b2b-video');
// Check if the video is in the viewport
// and if YES, play it:
$video.click();
});
And nothing. Hitting the wall.
I need to be able to start the video on iOS without the user interaction. What am I missing?
PS. Tapping (clicking) on the video doesn't start it either.
Just like #CBroe told you in his comment, read the link he put (motivation part):
A note about the user gesture requirement: when we say that an action must have happened “as a result of a user gesture”, we mean that the JavaScript which resulted in the call to video.play(), for example, must have directly resulted from a handler for a touchend, click, doubleclick, or keydown event. So, button.addEventListener('click', () => { video.play(); }) would satisfy the user gesture requirement. video.addEventListener('canplaythrough', () => { video.play(); }) would not.
So it basicaly says that you cannot use any event or action from the user to start playing the video.
Then your 'scroll' event cannot work ^^
Related
I'm trying to get a small sound file to play automatically using an tag and javascript to initiate it.
<audio id="denied" preload="auto" controls="false">
<source src="sound/denied.wav" />
</audio>
And then through javascript, at the appropriate time:
$('#denied')[0].play()
Works fine on Chrome on my desktop. In Android 4.1.1, the sound will not play, unless I hit "play" on the HTML5 audio controls before javascript attempts to play it.
So basically the Android browser (stock or Dolphin) will not play the audio unless the user initiates it at some point before the javascript. Is this intended? Is there any way around this?
Well, for my purposes, here's what I did:
Luckily, before the user can trigger the behavior to start audio, they have to click a button. I set the volume of the element to 0.0, and have it "play" when they click this button.
After the sound is played silently, I simply set the volume property back to 1.0, and it plays without user intervention just fine.
In my case this was an easy solution:
https://stackoverflow.com/a/28011906/4622767
Copy & paste this in your chrome:
chrome://flags/#autoplay-policy
My web app has many page reload so I can't force the user to press a button every time; but it is for internal usage, so I can force the users to use chrome and configure that option.
I know that in mobile safari any javascript call to play() must be in the same call stack as a user initialted click event. Spoofing the the click with a javascript trigger won't work either.
On my nexus 7 I can confirm that unless the javascript was triggered by a user click, it does not play.
The main issue is that sound (on iOS and Android) must be triggered by a user click. My workaround was very simple. Tie the audio.play() to a click event listener and then immediately pause it. From that point and on the sound works perfect.
var myAudio = new Audio('assets/myAudio.mp3');
...
button.addEventListener("click", function() {
myAudio.play();
myAudio.pause();
...
});
Here is a blog-post on the reason and how to overcome it by loading all the audio files on first user interaction and play them later in programmed manner: https://blog.foolip.org/2014/02/10/media-playback-restrictions-in-blink/ Adopting this approach a small javascript module is available on GitHub https://github.com/MauriceButler/simple-audio
I tried this simple approach on my own - worked seamlessly & great!
Luckily for me the html5 app I'm working on only needs to work in Android 4.1, but if you're trying to make something cross-platform you'll need to adapt this slightly. Neither setting volume to 0.0 then back or autoplay on the control worked for me. I also tried muted and that didn't work either. Then I thought, what if I set the duration and only play a miniscule amount of the file? Here's yet another hacked-together script that actually did work:
function myAjaxFunction() {
clearTimeout(rstTimer); //timer that resets the page to defaults
var snd=document.getElementById('snd');
snd.currentTime=snd.duration-.01; //set seek position of audio near end
snd.play(); //play the last bit of the audio file
snd.load(); //reload file
document.getElementById('myDisplay').innerHTML=defaultDisplay;
var AJAX=new XMLHttpRequest(); //won't work in old versions of IE
sendValue=document.getElementById('myInput').value;
AJAX.open('GET', encodeURI('action.php?val='+sendValue+'&fn=find'), true);
AJAX.send();
AJAX.onreadystatechange=function() {
if (AJAX.readyState==4 && AJAX.status==200) {
document.getElementById('myDisplay').innerHTML=AJAX.responseText;
if (document.getElementById('err')) { //php returns <div id="err"> if fail
rstTimer = setTimeout('reset()', 5000); //due to error reset page in 5 seconds
snd.play(); //play error sound
document.getElementById('myInput').value=''; //clear the input
}
}
}
}
You don't need JavaScript for autoplay, and I see several issues in your code:
<audio id="denied" preload="auto" controls="false">
<source src="sound/denied.wav" />
</audio>
I'd change it to (in HTML5 you don't need attribute="value" syntax in some cases, that's for XHTML):
<audio id="denied" autobuffer controls autoplay>
<source src="sound/denied.wav" />
</audio>
play!
In iOS autoplay is disabled, you need to click play button or put a user click event on custom controls like this with JavaScript:
var audio = document.getElementById('denied');
var button = document.getElementById('play-button');
button.addEventListener('click',function(){
audio.play();
});
or with jQuery:
$('#play-button').click(function(){ audio.play(); });}
Edit
Keep in mind that HTML5 is pretty recent technology (at least some functionalities), so browser compatibility and functionality changes every once in a while.
I encourage everybody to keep informed about the current status, as sometimes things start to be less hack-dependant, or the other way around.
A good starting point: http://caniuse.com/#feat=audio (check the tabs at the bottom).
Also for a global audio solution I recommend using SoundManager2 JS library
I met the same problem in my app. My application usage scenario was like this:
click the button to show a table with information (user action)
show the table and refresh data in it by AJAX
play sound when data has been changed
Fortunately I had the user action (step 1) so I was able to "initiate" the sound by it and then play it by javascript without the user action needed. See the code.
HTML
<audio id="kohoutAudio">
<source src="views/images/kohout.mp3">
</audio>
<button id="btnShow">Show table</button>
Javascript
$("#btnShow").click(function () {
//some code to show the table
//initialize the sound
document.getElementById('kohoutAudio').play();
document.getElementById('kohoutAudio').pause();
});
function announceChange(){
document.getElementById('kohoutAudio').play();
};
It happened to me too. Here's how I hacked this:
I set the audio to autoplay and set the vol to 0.
When I need it to play, I use the load() method and it will autoplay.
It's a light mp3 file and it plays every one hour/two hour, so this works fine for me.
Right now I have a very basic HTML5 Audio player that play from webradio.
I use a old iPad to play the music but also to show some information from a webpage. The problem is that is happen sometime the wifi and the old iPad does not like to stay connect so it disconnect and reconnect and then the player stop play the webradio. I need somehow a script that notice in real time or every 1 min if the radio is not running and restart the player for me. I guess I can use Javascript but have search a Little and not find any good idea and it also have to work in Chrome. It looks that some javaScript does not work in specific webbrowser
Basically, your <audio> element will dispatch an error event. Just listen for that and then retry. You could also listen to the progress event to see when it starts receiving again and then call play from there. It'd be something roughly like this:
const audio = getAudioElement();
audio.addEventListener('error', () => { playing = false; /* handle error */ });
audio.addEventListener('progress', () => {
if (!playing) {
audio.play();
playing = true;
}
});
I need to detect only the clicks that don't fire any action. See example below.
http://jsfiddle.net/SdNL5/1/
<video id="video" width="320" height="240" src="http://vjs.zencdn.net/v/oceans.mp4" controls>
<source src="http://vjs.zencdn.net/v/oceans.mp4" type="video/mp4">
Your browser does not support the video tag.
</video>
var video = document.getElementById("video");
video.onclick = function(){
alert("This click is useless");
};
In that case when the user clicks the video he receives an alert message. BUT, problem is, I don't want the alert message to show up when the user clicks play button, pause button or volume control. I want it to show up only when the user clicks the video but not any button. Also I need to open a popup window after that so, I can't use setTimeout or similar in order for the browsers not to block the popup.
I'm aware that I can read coordinates and calculate whether there is a button or not, but I would prefer another solution.
I asked a question which is very similar to this one.(How to prevent event bubbling when clicking on HTML5 video controls in Firefox)
The only way I know to do that consistently in different browsers is to have an overlay over the video and then custom controls over that overlay.
There are several reasons why. For example 'useless' clicks will depend on the browser:
Firefox will toggle play/pause when clicking on the video.
IE will have a 'useless' click but on double click will go into
full-screen.
Chrome & Safari will have 'useless' clicks
BUT if you don't care about Firefox then this example would work for you but only in Chrome, Safari & IE : http://jsfiddle.net/me2loveit2/cSTGM/40/
<a href="javascript: alert('Nothing');">
<video ...
</video>
</a>
in this example im using setTimeout to let .paused, .volume And .muted to Be updated before I compare them with the last status (in the Variables).
var video = document.getElementById("video");
var isPaused = video.paused;
var lastVolume = video.volume;
var isMute = video.muted;
video.onclick = function(){
setTimeout(function() {
// if the user did not change: Play/Pause && Volume && mute
if(isPaused == video.paused && lastVolume == video.volume && isMute == video.muted) {
alert("This click is useless");
} else {
// if the user did change, update to current status
isPaused = video.paused;
lastVolume = video.volume;
isMute = video.muted;
}
}, 100);
}
demo: http://jsfiddle.net/SdNL5/22/
note: if the user click on volume control and did not make change, It will be considered as 'useless' click.
i know you already checked an answer, but please consider that you can still hide the default video controls and use your custom controls to play with.
if you don't want/have time to spend on it yourself you can just use some javascript lib like videojs
this way you'll have really full control both over buttons and video separately in a cross-browser way.
http://www.w3.org/2010/05/video/mediaevents.html
http://msdn.microsoft.com/en-us/library/ie/hh924822(v=vs.85).aspx
I’d like to place interaction controls above a youtube iframe video, and I got it working quite OK by just adding wmode=opaque as arguments and then position the elements absolute above the iframe.
My problem is that on mobile safari - the controls work fine first, but when I return from the fullscreen video, they are all disabled. It works fine on desktop though.
The HTML is basically:
<iframe src="http://www.youtube.com/embed/[ID]?wmode=opaque"></iframe>
<button id="btn">Click me</button>
And then the button is positioned absolute above the iframe.
For a demo, please visit this fiddle using your mobile safari: http://jsfiddle.net/SwGH5/embedded/result/
You’ll see that the button yields an alert when clicked. Now, play the video and click "done". Then try to click the button again...
If the movie was embedded using the <video> tag I could listen for a fullscreen-end event and do something, but now I’m stuck...
Here’s the fiddle: http://jsfiddle.net/SwGH5
So I played around with the iframe API a bit and found a solution. It's kind of hacky... but not really. When a user clicks on the video to play it, the document.activeElement becomes the iframe. When the user clicks the "done" button, the document.activeElement === document.body. So when the video starts playing, we periodically check to see if the active element returns to the body. At that point, the only solution I found was to redraw the iframe. In the example, I destroy() the video and recreate it using the iframe API. I also tried cloning the iframe and replacing it with success (I left that code there so you could see it):
http://jsfiddle.net/ryanwheale/SwGH5/105/
It's not the best solution, but it works. Also, to give an explanation of what [I think] is happening - iOS hijacks any video content (Safari or Chrome) and plays it using the native video player. Any type of OS functionality like this takes place "over" the browser if you will - higher than any z-index... completely outside the browser. When the user clicks "done" the native player kind of zooms out as if it is re-implanting itself on the page. My best guess is that the native player is still hanging out "over" the browser... thus making anything underneath it inaccessible. The fact that the button appears to be on top is just an anomaly... for lack of better description.
EDIT: Ryan Wheale's solution is a better workaround to this problem and will work in most cases.
From Apple’s documentation:
On iOS-based devices with small screens—such as iPhone and iPod touch—video always plays in fullscreen mode, so the canvas cannot be superimposed on playing video. On iOS-based devices with larger screens, such as iPad, you can superimpose canvas graphics on playing video, just as you can on the desktop.
Same goes for a "played video" either. This article clearly states it's not possible.
Workaround:
You could detach and re-attach the iframe element on webkitfullscreenchange. But it won't work for a foreign iframe. Browser will not allow you to manipulate iframe DOM bec. of the cross-domain policy. And even if it did, you could only hide the video overlay to reach your button anyway.
So, the only solution is watching for YT.PlayerState.ENDED state via YouTube iFrame API and then destroying and re-creating the player. But don't worry it plays smooth.
window.onYouTubeIframeAPIReady = function () {
var video = {
player: null,
create: function () {
// first destroy if we already have the player
if (video.player) { video.player.destroy(); }
// create the player
video.player = new YT.Player('ytp', {
videoId: '9u_hp7zPir0',
width: '620',
height: '290',
events: {
'onStateChange': video.onStateChange
}
});
},
onStateChange: function (event) {
// YT.PlayerState.ENDED » exiting full screen
if (event.data === 0) { video.create(); }
}
};
video.create();
};
Here is the working fiddle.
If you can't get it to work with the regular iframe, you might want to consider using mediaelement.js - which can wrap the Youtube API in a HTML5 Media API wrapper. That works fine on safari on ios - try the Youtube example at mediaelementjs.com.
All you need to do is to add the mejs js/css and put your youtube video as source in your video tag, here is some example markup taken from mediaelementjs.com:
<script src="jquery.js"></script>
<script src="mediaelement-and-player.min.js"></script>
<link rel="stylesheet" href="mediaelementplayer.css" />
<video width="640" height="360" id="player1" preload="none">
<source type="video/youtube" src="http://www.youtube.com/watch?v=nOEw9iiopwI" />
</video>
Then start the player like so:
jQuery(document).ready(function($) {
$('#player1').mediaelementplayer();
});
If you want to add your button ontop of the mejs player it will work fine, just set the z-index high enough. If you want a regular play button you could also consider styling the existing one that comes with mejs.
By the comments from #CullenJ's answer, possibly it might be due to some problem in iOS device browsers not triggering the click event on iframe elements. In that case, you would have to change from:
document.getElementById('btn').onclick = function() {
alert('clicked');
}
To something like this (as answered by #smnh):
$('#btn').on('click tap touchstart', function() {
alert('clicked');
});
I'm trying to get audio to work outside the app (I'm using the HTML5, Javascript approach) in Windows 8, so when you close the app the sound continues to work, from what I have researched on here and on other sites and I believe this is called in Windows 8 'background audio, I've followed all the tutorials on Microsoft Developer site, and have declared background audio in the app manifest as so:
<Extension Category="windows.backgroundTasks" StartPage="default.html">
<BackgroundTasks>
<Task Type="audio" />
<Task Type="controlChannel" />
</BackgroundTasks>
</Extension>
and where I have added the msAudioCategory="BackgroundCapableMedia" controls="controls" to my HTML5 audio tag as so:
<audio id="playback" msAudioCategory="BackgroundCapableMedia" controls="controls">
</audio>
and I've also added this to my default.js file which was apprently needed, although I'm not sure what this does
// Declare a variable that you will use as an instance of an object
var mediaControls;
// Assign the button object to mediaControls
mediaControls = Windows.Media.MediaControl;
// Add an event listener for the Play, Pause Play/Pause toggle button
mediaControls.addEventListener("playpausetogglepressed", playpausetoggle, false);
mediaControls.addEventListener("playpressed", playbutton, false);
mediaControls.addEventListener("pausepressed", pausebutton, false);
// The event handler for the play/pause button
function playpausetoggle() {
if (mediaControls.isPlaying === true) {
document.getElementById("playback").pause();
} else {
document.getElementById("playback").play();
}
}
// The event handler for the pause button
function pausebutton() {
document.getElementById("playback").pause();
}
// The event handler for the play button
function playbutton() {
document.getElementById("playback").play();
}
I have also tried changing the ID in the last part to have a hash tag as well but still when I press the start button to go back home the audio stops, am I doing something wrong?
Thanks
I believe you also need to handle the "stoppressed" event:
mediaControls.addEventListener("stoppressed", stop, false);
function stop() {
// Handle the stop event.
document.getElementById("playback").pause();
document.getElementById("playback").currentTime = 0;
}
The three steps for playing background audio in Windows 8 JavaScript apps are:
Decalare an audio background task in package.appxmanifest. Also list a StartPage. You did this correctly.
Set msAudioCategory="BackgroundCapableMedia". You did this.
Implement support for media controls. Media controls are buttons on remote controls or on certain keyboards that play, pause, or stop audio. See the Configure keys for media sample on MSDN for a working example. I was able to get the example to work when only handling "stoppressed" in addition to the 3 events you were already handling.
For more information, watch Your Metro style app, video and audio, part 2 from the 2011 Build conference. Background audio is covered beginning at around 31 minutes, 20 seconds into the video and lasts for about 10 minutes. Note that this video is from September, 2011, and covers the Developer Preview of Windows 8. The concepts still apply to the released versions of Windows 8 and Windows RT, but namespaces and attribute names are different in some places.