I have two buttons on a page that trigger two functions that make two html5 video play, hide and show some elements (including themselves), and call another simple function on ended (that causes the video to go to the first frame and pause, for the effect to work properly).
$('#rotate').click(function rotate(){
$('#rotate').hide();
$('#front_view').css('z-index','2');
$('#back_view').css('z-index','3');
//this is the video:
$('#body_animation').trigger("play").show().bind('ended', function () {
$('#back_view').show();
$('#front_view').fadeOut(500);
$(this).hide();
this.currentTime = 0;
this.pause();
});
$('#rotate_reverse').delay(2000).fadeIn(0);
});
This works fine in firefox and safari, but in chrome and IE something strange happens. The first time the page loads, the "ended" event doesn't seem work. It works fine if you refresh the site (or if you run it offline), though.
You can check the code in here, I narrowed all the site to this problem, so you can see it better:
http://www.hidden-workshop.com/test/
The actual videos and images are different, but the problem is the same. I'm busting my head trying to solve this thing, but I can't find the answer anywhere.
Thanks in advance!!
Your test page isn't live anymore, so I can't check this, but I found that if looping is enabled for the tag (e.g., <video loop="loop">), the "ended" event wasn't firing in Chrome or IE (I didn't test in Firefox). Once I removed the loop attribute, the "ended" event fired in both browsers.
HTML (with loop attribute, which will prevent the 'ended' event form firing):
Remove the loop attribute if you want the 'ended' event to fire...
<video id="video" loop="loop">
<source src="whatever.mp4" width="320" height="240" type="video/mp4" />
Your browser does not support this HTML5 video tag.
</video>
Javascript:
//NOTE: This only fires if looping is disabled for the video!
$("#video").bind("ended", function() {
alert('Video ended!');
});
If you are dynamically adding a <video> to your page that wasn't present in the HTML served from the server you may encounter a race condition that can result in the ended event not being recognized - even if it was added correctly.
I am using knockoutjs to add a different template for phone vs desktop.
<div data-bind="template: { name: videoTemplate, afterRender: initializeVideo }">
This dynamically creates me a <video> element and then calls initializeVideo() after the template is rendered (added to the DOM) to bind the events :
$('video#flexVideo').off('ended').on('ended', (evt) =>
{
alert('ended fired');
}).css('border', '2px solid yellow'); // add yellow border so we know element existed
On my screen the video gets a yellow border (proving the video was present in the DOM and there were no typos). However for some reason the browser isn't yet able to attach the ended event to it - I assume perhaps it was not initialized yet.
Chrome debugging tools shows that it is added, but it doesn't actually work!
It does this in Firefox, Chrome + IE10 which was slightly surprising.
One solution is this:
$('video#flexVideo').off('loadstart').on('loadstart', (evt) =>
{
$('video#flexVideo').off('ended').on('ended', (evt) =>
{
alert('ended fired');
}).css('border', '2px solid yellow');
}).css('border', '2px solid orange');
The loadstart event seems to be bindable right away.
Another is perhaps just a setTimeout - or just bind the event on play.
Related
I'm getting the error message..
Uncaught (in promise) DOMException: play() failed because the user didn't interact with the document first.
..when trying to play video on desktop using Chrome version 66.
I did find an ad that began playback automatically on a website however using the following HTML:
<video
title="Advertisement"
webkit-playsinline="true"
playsinline="true"
style="background-color: rgb(0, 0, 0); position: absolute; width: 640px; height: 360px;"
src="http://ds.serving-sys.com/BurstingRes/Site-2500/Type-16/1ff26f6a-aa27-4b30-a264-df2173c79623.mp4"
autoplay=""></video>
So is by-passing Chrome v66's autoplay blocker really as easy as just adding the webkit-playsinline="true", playsinline="true", and autoplay="" attributes to the <video> element? Are there any negative consequences to this?
To make the autoplay on html 5 elements work after the chrome 66 update you just need to add the muted property to the video element.
So your current video HTML
<video
title="Advertisement"
webkit-playsinline="true"
playsinline="true"
style="background-color: rgb(0, 0, 0); position: absolute; width: 640px; height: 360px;"
src="http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"
autoplay=""></video>
Just needs muted="muted"
<video
title="Advertisement"
style="background-color: rgb(0, 0, 0); position: absolute; width: 640px; height: 360px;"
src="http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"
autoplay="true"
muted="muted"></video>
I believe the chrome 66 update is trying to stop tabs creating random noise on the users tabs. That's why the muted property make the autoplay work again.
For me (in Angular project) this code helped:
In HTML you should add autoplay muted
In JS/TS
playVideo() {
const media = this.videoplayer.nativeElement;
media.muted = true; // without this line it's not working although I have "muted" in HTML
media.play();
}
Try to use mousemove event listener
var audio = document.createElement("AUDIO")
document.body.appendChild(audio);
audio.src = "./audio/rain.m4a"
document.body.addEventListener("mousemove", function () {
audio.play()
})
The best solution i found out is to mute the video
HTML
<video loop muted autoplay id="videomain">
<source src="videoname.mp4" type="video/mp4">
</video>
Answering the question at hand...
No it's not enough to have these attributes, to be able to autoplay a media with audio you need to have an user-gesture registered on your document.
But, this limitation is very weak: if you did receive this user-gesture on the parent document, and your video got loaded from an iframe, then you could play it...
So take for instance this fiddle, which is only
<video src="myvidwithsound.webm" autoplay=""></video>
At first load, and if you don't click anywhere, it will not run, because we don't have any event registered yet.
But once you click the "Run" button, then the parent document (jsfiddle.net) did receive an user-gesture, and now the video plays, even though it is technically loaded in a different document.
But the following snippet, since it requires you to actually click the Run code snippet button, will autoplay.
<video src="https://upload.wikimedia.org/wikipedia/commons/transcoded/2/22/Volcano_Lava_Sample.webm/Volcano_Lava_Sample.webm.360p.webm" autoplay=""></video>
This means that your ad was probably able to play because you did provide an user-gesture to the main page.
Now, note that Safari and Mobile Chrome have stricter rules than that, and will require you to actually trigger at least once the play() method programmatically on the <video> or <audio> element from the user-event handler itself.
btn.onclick = e => {
// mark our MediaElement as user-approved
vid.play().then(()=>vid.pause());
// now we can do whatever we want at any time with this MediaElement
setTimeout(()=> vid.play(), 3000);
};
<button id="btn">play in 3s</button>
<video
src="https://upload.wikimedia.org/wikipedia/commons/transcoded/2/22/Volcano_Lava_Sample.webm/Volcano_Lava_Sample.webm.360p.webm" id="vid"></video>
And if you don't need the audio, then simply don't attach it to your media, a video with only a video track is also allowed to autoplay, and will reduce your user's bandwidth usage.
Extend the DOM Element, Handle the Error, and Degrade Gracefully
Below I use the prototype function to wrap the native DOM play function, grab its promise, and then degrade to a play button if the browser throws an exception. This extension addresses the shortcoming of the browser and is plug-n-play in any page with knowledge of the target element(s).
// JavaScript
// Wrap the native DOM audio element play function and handle any autoplay errors
Audio.prototype.play = (function(play) {
return function () {
var audio = this,
args = arguments,
promise = play.apply(audio, args);
if (promise !== undefined) {
promise.catch(_ => {
// Autoplay was prevented. This is optional, but add a button to start playing.
var el = document.createElement("button");
el.innerHTML = "Play";
el.addEventListener("click", function(){play.apply(audio, args);});
this.parentNode.insertBefore(el, this.nextSibling)
});
}
};
})(Audio.prototype.play);
// Try automatically playing our audio via script. This would normally trigger and error.
document.getElementById('MyAudioElement').play()
<!-- HTML -->
<audio id="MyAudioElement" autoplay>
<source src="https://www.w3schools.com/html/horse.ogg" type="audio/ogg">
<source src="https://www.w3schools.com/html/horse.mp3" type="audio/mpeg">
Your browser does not support the audio element.
</audio>
I got this error
Uncaught (in promise) DOMException: play() failed because the user didn't interact with the document first.
And here's what I did in my Angular Project
Key Point: Don't ever assume a video will play, and don't show a pause button when the video is not actually playing.
You should always look at the Promise returned by the play function to see if it was rejected:
ngOnInit(): void{
this.ensureVideoPlays();
}
private ensureVideoPlays(): void{
const video = document.querySelector("video");
if(!video) return;
const promise = video.play();
if(promise !== undefined){
promise.then(() => {
// Autoplay started
}).catch(error => {
// Autoplay was prevented.
video.muted = true;
video.play();
});
}
}
Source: Autoplay policy
In my case, I had to do this
// Initialization in the dom
// Consider the muted attribute
<audio id="notification" src="path/to/sound.mp3" muted></audio>
// in the js code unmute the audio once the event happened
document.getElementById('notification').muted = false;
document.getElementById('notification').play();
According to the new browser policy, the user must interact with DOM first before playing the Audio element.
If you want to play the media on page load then you can simply add autoplay property to audio element in HTML like this
<video id="video" src="./music.mp4" autoplay>
or if you don't want to do autoplay then you can handle this using Javascript. Since the autoplay property is set to true, media will be played, we can simply mute the media.
document.getElementById('video').autoplay = true;
document.getElementById('video').muted = true;
Imp: Now Whenever you play the media don't forget to turn the muted property to false. Like this
document.getElementById('video').muted = false;
document.getElementById('video').play();
Or you can also show a simple popup where the user will click the allow button in the modal. So he interacts with DOM first, then you don't need anything to do
I had a similar problem, I need to play the video without muting it. The way i did this, wait for a second then triggered the event by button. Here is my code
if (playVideo == '1') {
setTimeout(function() {
$("#watch_video_btn").trigger('click');
}, 1000);
}
Chrome needs a user interaction for the video to be autoplayed or played via js (video.play()).
But the interaction can be of any kind, in any moment.
If you just click random on the page, the video will autoplay.
I resolved then, adding a button (only on chrome browsers) that says "enable video autoplay". The button does nothing, but just clicking it, is the required user interaction for any further video.
I changed my UI to have the user press a button to load the website (and when the website loads after they click the button, the audio plays)
Since they interact with the DOM, then the audio plays!!!
In my case it's just a click sound which is automatically invoked at the start (which I don't mind if it's silenced). So I use:
const clickSound = new Audio('click.wav');
clickSound.play().catch(function (error) {
console.log("Chrome cannot play sound without user interaction first")});
to get rid of the error.
I had some issues playing on Android Phone.
After few tries I found out that when Data Saver is on there is no auto play:
There is no autoplay if Data Saver mode is enabled. If Data Saver mode is enabled, autoplay is disabled in Media settings.
Source
I encountered a similar error with while attempting to play an audio file. At first, it was working, then it stopped working when I started using ChangeDetector's markForCheck method in the same function to trigger a re-render when a promise resolves (I had an issue with view rendering).
When I changed the markForCheck to detectChanges it started working again. I really can't explain what happened, I just thought of dropping this here, perhaps it would help someone.
You should have added muted attribute inside your videoElement for your code work as expected. Look bellow ..
<video id="IPcamerastream" muted="muted" autoplay src="videoplayback%20(1).mp4" width="960" height="540"></video>
Don' t forget to add a valid video link as source
Open chrome://settings/content/sound
Setting No user gesture is required
Relaunch Chrome
Audio Autoplay property does not work in MS Edge
Type Chrome://flags in the address-bar
Search: Autoplay
Autoplay Policy
Policy used when deciding if audio or video is allowed
to autoplay.
– Mac, Windows, Linux, Chrome OS, Android
Set this to "No user gesture is required"
Relaunch Chrome and you don't have to change any code
I am showing a couple a number of videos in succession.
When the first video finishes I ask the user a question and then show another video following their response. After the second video I ask the user a final question.
The problem is that when the second video ends both .question-one and .question-two are displayed as a block again, as it appears the code from the first on('ended..) method is being triggered again.
I tried to use unbind to remove the binding from the videos, but this didn't work.
Here is my code below.
HTML
<video>
<source src="1.mp4" type="video/mp4">
</video>
JS
$('.video-one').bind('ended',function(){
$(this).removeClass('video-one');
$(this).addClass('video-two');
$('#video-background').css('display', 'none');
$('.question-one').fadeIn('slow');
$('.video-one').unbind('ended');
});
$('.question-one').click(function(){
$('.question-one').css('display', 'none');
$('#video-background').css('display', 'block');
$('video').attr('src', '2.mp4');
});
$('.video-two').bind('ended',function(){
$(this).removeClass('video-two');
$(this).addClass('video-three');
$('#video-background').css('display', 'none');
$('.question-two').fadeIn('slow');
$('.video-two').unbind('ended');
});
Is there something else that I am missing?
instead of changing order of instructions like #Offbeatmammal said, I would suggest unbinding event based on ID of element, because you want to unbind only this one, specific - not everyone with this class probably. it's only semantic reason. you would then have to add an event parameter to your function, and get id by event.target.id if I remember well.
I'm creating a website with some animations. One of them is the logo animation. It's called 'lbv.mp4'. As it has some weight, there's a lag with autoplay, so I decided to show content when it is fully loaded.
The plan is:
the video is checked for loading
start the video
add a class to the page element to trigger the animation
setTimeout for the length of the video which will make visibility: hidden for the clip to open a static image underneath.
This is better described in the following code:
video.addEventListener('loadeddata', function () {
if (video.readyState === 4) {
video.play();
$('#page').addClass('transition');
document.setTimeout(function(){
video.attr('style', 'visibility: hidden');
}, 750);
}
The only problem is that I can't get it working neither with pure JS, neither with JQuery. The video isn't loading, the classes aren't given. Tested in Safari, Chrome and Firefox.
So the final question is: 'Is there another to make it easier, or how to fix my solution?'
Event loadeddata means :
The first frame of the media has finished loading.
readyState === 4 means :
Enough data is available—and the download rate is high enough—that the media can be played through to the end without interruption.
You have chances that loadeddata is triggered but readyState is not 4.
Since loadeddata is triggered only once, the video won't ever play.
You should try to add logs to verify this assumption.
What I would try is the following :
Use <video> with autoplay
Listen for the playing event on the video to start transition
Listen for the ended event on the video to hide it
Reference for attributes, events and readyState:
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Video
https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Media_events
https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/readyState
In Firefox when a video tag is wrapped in an a tag, using the standard video controls when clicking on the video to pause it also re-directs. How can I make it behave like the other browsers where for example clicking on pause only pauses the video and does NOT re-direct as well. This is what I need.
Here is a simple demo: http://jsfiddle.net/me2loveit2/cSTGM/
<a href="http://www.google.com" target="_blank">
<video controls="" muted="" preload="auto" id="testid" width="500">
<source src="http://www.w3schools.com/html/mov_bbb.mp4" type="video/mp4"/>
<source src="http://www.w3schools.com/html/mov_bbb.ogg" type="video/ogg"/>
<source src="http://www.w3schools.com/html/mov_bbb.webm" type="video/webm"/>
<img src="http://dummyimage.com/1044x585/000/fff"/>
</video>
</a>
What you've got there is invalid markup, the HTML5 spec clearly states that
The a element may be wrapped around entire paragraphs, lists, tables, and so forth, even entire sections, so long as there is no interactive content within (e.g. buttons or other links).
and the video navigation is in fact interactive content containing buttons.
For some reason clicking the controls in Chrome does not trigger the anchor, while in Firefox it does.
This is dependant on how the browser constructs the controls with the Shadow DOM, and as the markup is invalid and there is no real standard for this, it's anyone's guess.
What you should have done is to remove the anchor and use javascript to redirect when the video is clicked, something like this
$('#testid').on('click', function() {
var win = window.open('http://www.google.com', '_blank');
win.focus();
});
That would have given you valid markup as you could just remove the wrapping anchor, but it doesn't solve the problem with not redirecting when clicking the controls either, it's exactly the same, as the controls are still inside the video and triggers the click handler in Firefox, but not in Chrome.
In webkit the controls could potentially have been targeted somehow with the -webkit-media-controls pseudo class, however Firefox doesn't seem to have any such pseudo class, so that won't work either.
What you're left with is relying on the fact that the controls seem to always be at the bottom, and they are around 30 pixels high, so you can just overlay the anchor on top of the video and leave out a little part of the bottom.
This will work in all browsers, and you'll have valid markup.
<video controls="" muted="" autoplay preload="auto" id="testid" width="500">
<!-- stuff -->
</video>
To make sure the anchor is placed correctly and has the correct size, a little javascript can be used
$('.overlay').each(function() {
var vid = $(this).prev('video');
$(this).css({
position : 'fixed',
top : vid.offset().top + 'px',
left : vid.offset().left + 'px',
width : vid.width() + 'px',
height : (vid.height() - 30) + 'px',
});
});
FIDDLE
Other than using custom controls, I am not sure it's possible to get around the control behavior in a truly elegant way, given that the video events (play, pause, etc) trigger after the click events. This is a solution that hardcodes the approximate height of the default controls. I don't like the hardcoding, but in other respects I think it is OK. It applies to all a and video elements and doesn't do any excessive iterating through elements. The setTimeout bit is a workaround for event.preventDefault() killing both the link behavior and the play/pause behavior.
$(document).on('click', 'a', function(event) {
var video = $('video:hover').first();
if (video.length && video.offset().top + video.height() - event.pageY < 35) {
var anchor = $(this);
var href = anchor.attr('href');
var target = anchor.attr('target');
anchor.attr('href', 'javascript:;');
anchor.attr('target', null);
setTimeout(function() {
anchor.attr('href', href);
anchor.attr('target', target);
}, 1);
}
});
You can accomplish this by creating custom controls for your video and wrap only the video tag with the a tag and not the controls. This gives you the option of having consistent looking controls for your video across browsers, but you have to have a good understanding of CSS to make it look good and consistent across browsers. I have included a CodePen project of what you wanted, with some custom controls. The controls don't look very good across browsers, but I think you can get the idea.
http://codepen.io/anon/pen/dtHsb
Very ugly, but the usual solutions don't works because event.stropPropagation() only work for event handlers and event.preventDefault() breaks the controls.
http://jsfiddle.net/cSTGM/28/
$('#testid').click(function() {
link = $(this).parent();
originalHref = link.attr('href');
originalTarget = link.attr('target');
link.attr('href', 'javascript:void(0)');
link.attr('target', '_self');
setTimeout(function() {
link.attr('href', originalHref);
link.attr('target', originalTarget);
}, 0);
});
We just need to prevent redirecting if it is VIDEO tag
$('#testid').click(function() {
if (event.target.tagName !=== 'VIDEO') {
//redirect
}
});
I currently have an HTML5 video event issue in Safari. I am playing a single video on my page. The video loads and plays correctly. However, the play event does not always fire. If the user:
Clicks play
Watches the video to the end (ended event fires)
Clicks play again
The play event does not fire on the second click. If I pause/play the movie at that time, the correct events fire.
How can I make the video tag's play event fire if the video has completed and the user presses play again?
drawVidPlayer is called with the videos index as part of the page render
function drawVidPlayer(vindex){
var turl=vidList[vindex]['thumbUrl'];
var vurl=vidList[vindex]['url'];
var valias=vidList[vindex]['type'];
destroyVidPlayer();
$('#mediaspot').css('backgroundColor', '#000000');
$('#mediaspot').show();
$('#mediaspot').html('<video controls="controls" id="twnvideo" poster="'+turl+'" style="height:225px; width:460px;"><source src="'+vurl+'" type="video/ogg" /><source src="'+vurl+'" type="video/mp4" /><source src="'+vurl+'" type="video/webm" />Your browser does not support the video tag.</video>').appendTo('#wrap_media_vod');
var velem=document.getElementsByTagName('video')[0];
velem.addEventListener('play', initVidTimer, false);
velem.addEventListener('pause', killVidTimer, false);
velem.addEventListener('ended', killVidTimer, false);
}
function destroyVidPlayer(){
var velem=document.getElementsByTagName('video')[0];
if(velem!=undefined){
velem.removeEventListener('play', initVidTimer);
velem.removeEventListener('pause', killVidTimer);
velem.removeEventListener('ended', killVidTimer);
}
$('#mediaspot').empty();
$('#mediaspot').html('');
}
function initVidTimer(){
if(activityTimer==null){
external.OnUserActivity(19);
activityTimer=setInterval(function(){
external.WriteLog('activity timer running');
external.OnUserActivity(19);
}, 5000);
}
}
function killVidTimer(){
clearInterval(activityTimer);
activityTimer=null; // Kill keepAlive timer
var velem=document.getElementsByTagName('video')[0];
external.WriteLog(velem.ended);
}
HTML5 now specifies that the browser must throw the timeupdate, paused, and ended events when the playback position reaches the end of a media file, but the spec wasn't always that clear. As a result, this behavior is inconsistent between browsers. Some don't set paused=true or fire the paused event when the file ends.
In your Safari issue, paused is still equal to false when the video starts to play for the second time - so there is no reason for the browser to fire the play event again.
This may no longer be an issue in Safari 6, but it still breaks in IE 9. Take a look at the End Events column in this chart from longtailvideo.com - they outline the inconsistencies well.
It would easy to normalize this issue with a couple lines of code - like this:
$("video").on("ended", function () {
if (!this.paused) this.pause();
});
This puts the video in the paused state on ended, so it will throw the play event correctly on replay.
You can try this working sample in IE 9 or to see what I mean on this jsfiddle: http://jsfiddle.net/PWnUb/
I had the same issue, I solved with a bit of jquery:
function videoend(){
var duration = $("video").get(0).duration;
var current = $("video").get(0).currentTime;
if(current==duration){
//Whatever you wanna do when video ends
};
}
$(document).ready(function(){
setInterval("videoend()", 200); //or any other time you wanna use
});
Hope this helps.