Google changed something last days, so I have strange problem with Youtube embeded as iFrame in mobile safari. When iFrame is hidden (some parent div has set display:none) during page load and later is changed to be visible, Youtube player do not play.
Any solution? I've tried to reload iframe, when it turns to visible state, it works. But it is not comfortable solution…
I am assuming you are using the third party youtube Iframe API to load the youtube player into a div. The code from youtube actually has a race condition in it where if the div is not loaded/ parsed by the browser prior to onYouTubeIframeAPIReady being called, the video load will fail. In firefox it will just hang and for a second and Chrome is smart enough to deal with the problem.
The solution is to make sure that this code. Is run after the container holding the video is parsed.
var tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
And this code...
function onYouTubeIframeAPIReady() {
Has global scope and is called after the script is created.
The way I typically solve this to to create a function 'class' with the tag creator and event listener and create the object in my document.ready function. Then right below the document.ready function I put.
function onYouTubeIframeAPIReady() {
myVideo.apiReady();
}
Where 'myVideo' is the class I have create and apiReady(); contains.
this.apiReady = function () {
player = new YT.Player('player', {
height: '548',
width: '900',
videoId: 'VIDEO234324',
events: {
'onReady': onPlayerReady
}
});
}
I hope this helps.
Related
We are a small nonprofit organization and desperately need assistance as follows:
We have an issue; click the submit button, and the ReCaptcha forces the frame to reload. In that interval, the comment section sort of goes down a layer.
Question: How does our developer remove the blue reCAPTCHA row upon selecting the comments button?
See the screenshot images below for details.
Note: The issue did not exist without the IFrame Player API.
Hal
Screenshot image of the Error Message below:
GitHub Gist link to Front-End Browser code, here.
Front-End Browser Code
<script src="https://gist.github.com/CforED/62c7013e51dc85f49180c3308e742d26.js"></script>
IFrame Player API Script
<script>
// 2. This code loads the IFrame Player API code asynchronously.
var tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
// 3. This function creates an <iframe> (and YouTube player)
// after the API code downloads.
var player;
function onYouTubeIframeAPIReady() {
player = new YT.Player('player', {
videoId: 'WLkkzqzo4p0',
playerVars: { 'autoplay': 1, 'playsinline': 1,'loop': 1, 'controls': 0, 'playlist': 'WLkkzqzo4p0'},
events: {
'onReady': onPlayerReady
}
});
}
// 4. The API will call this function when the video player is ready.
function onPlayerReady(event) {
event.target.mute();
event.target.playVideo();
}
JavaScript
<script type="text/javascript">
(function(e,t,o,n,p,r,i){e.visitorGlobalObjectAlias=n;e[e.visitorGlobalObjectAlias]=e[e.visitorGlobalObjectAlias]||function(){(e[e.visitorGlobalObjectAlias].q=e[e.visitorGlobalObjectAlias].q||[]).push(arguments)};e[e.visitorGlobalObjectAlias].l=(new Date).getTime();r=t.createElement("script");r.src=o;r.async=true;i=t.getElementsByTagName("script")[0];i.parentNode.insertBefore(r,i)})(window,document,"https://diffuser-cdn.app-us1.com/diffuser/diffuser.js","vgo");
vgo('setAccount', '224274263');
vgo('setTrackByDefault', true);
vgo('process');
A link to the page where it is deployed
After attempting to control using Session, the YouTube embedded player stops being responsive, and returns the error: Uncaught TypeError: Cannot call method 'postMessage' of null. This doesn't happen everytime, but can be reproduced by entering a song in the link and clicking on the play button.
Here is the setting up of the YouTube API temporarily placed in the HTML file:
mixtape.html
<script>
// Called automatically when JavaScript client library is loaded.
function onClientLoad() {
gapi.client.load('youtube', 'v3', onYouTubeApiLoad);
}
// Called automatically when YouTube API interface is loaded
function onYouTubeApiLoad() {
gapi.client.setApiKey('AIzaSyD1VcsNnysOY6_Za-8kE-BK6Zh8jQwvo4w');
}
</script>
<script src="https://apis.google.com/js/client.js?onload=onClientLoad"></script>
<script src= "https://www.youtube.com/iframe_api"></script>
<script>
// Load the IFrame Player API code asynchronously.
var tag = document.createElement('script');
tag.src = "https://www.youtube.com/player_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
// Replace the 'ytplayer' element with an <iframe> and
// YouTube player after the API code downloads.
var player;
function onYouTubePlayerAPIReady() {
console.log("playerAPIready");
player = new YT.Player('ytplayer', {
height: '400',
width: '640',
videoId: 'M7lc1UVf-VE',
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
//console.log("loaded");
$("#player").hide();
}
function onPlayerReady(){
//console.log("ready");
}
function onPlayerStateChange(){
//console.log("changed");
}
function generatePlaylist(list){
console.log("list: ", list)
player.loadPlaylist(list);
}
</script>
And the printing out occurs here. The nav_playlist reads from a Session variable which contains what it should, but the printing out below pretens that nav_playlist is empty.
<template name="player">
<div id="player">
<div id="ytplayer"></div>
<div id="nav">
<br>
{{#each nav_playlist}}
{{> unremovable_track}}
{{/each}}
</div>
</div>
</template>
```
An in mixtape.js
```
Template.player.nav_playlist = function(){
return Session.get("current_list");
}
```
And here are the Session variables being given their values. These contain exactly what they should. They update when the user clicks the play button.
/*Update List on generate button*/
Template.list.updateList = function(){
var ret = [];
$( "#playlist .list_element" ).each(function() {
if($(this).is(':visible')){
ret.push( Links.findOne({_id:$(this).attr('id')}) );
}
});
var urls = [];
for (var i = 0; i < ret.length; i++){
urls[i] = ret[i].videoId;
}
Session.set("current_list",ret);
Session.set("current_urls",urls);
}
And finally this is where the toggling happens to unhide the YouTube player and start the video.
Template.header.events({
'click #generate_button': function (evt, template){
//bad code below:
Template.list.updateList();
if (Template.list.my_playlist().fetch().length == 0){
alert('Your tape is empty!');
}
else{
generatePlaylist( Session.get("current_urls") );
$("#playlist").css('display','none');
$("#player").fadeIn(1000);
$(".absolute_center").hide();
/*Things to hide*/
$(".absolute_center2").fadeIn();
$("#query").hide();
$("#share").fadeOut(1000);
$("#playlist_container").fadeOut(1000);
$('body').animate({backgroundColor: 'rgb(53,53,53)'}, 'slow');
$('#title').animate({color: '#fff'}, 'slow');
}
},
'click #close_player': function (evt, template){
player.stopVideo();
$(".absolute_center2").hide();
$("#player").hide();
$("#playlist").css('display','block');
/*Things that must reappear*/
$("#query").show();
$("#share").fadeIn(1000);
$(".absolute_center").fadeIn(1000);
$("#playlist_container").fadeIn(1000);
$('body').animate({backgroundColor: '#fff'}, 'slow');
$('#title').animate({color: '#000'}, 'slow');
}
});
Here is the GitHub page if you would like to look at the source and dig deeper
The problem seems to be that although you're waiting for the YouTube API to load before adding the iFrame, you're not waiting for the DOM to finish rendering, so the element you're trying to replace with the iFrame (ytplayer) doesn't actually exist when new YT.Player is called in the script tag. Any time you try to play, stop, change video or anything else on the player you've initialised will result in this error under these circumstances, even if the relevant element is subsequently added to the DOM. You can manually run the code in the script tag in the console, and you should find that everything then works as the element will by that point exist at player reinitialisation.
I would advise moving this whole code segment into a Template.player.rendered function in the js file. You'll need to check whether the player has already been created (and only run the code if it hasn't), but putting it all here rather than a script tag will guarantee that the element exists as well as that the API has been loaded. Anyway, I'm sure you can work it out.
UPDATE:
Since you're doing most of the work in the script tags through callbacks, it's actually much easier to add this to the js file:
Template.player.created = function() {
var tag = document.createElement('script');
tag.src = "https://apis.google.com/js/client.js?onload=onClientLoad";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
};
and take this line out of the html:
<script src="https://apis.google.com/js/client.js?onload=onClientLoad"></script>
Apologies if what I said above isn't clear. In your version, the YouTube API is trying to replace the div ytplayer with an iFrame, but since the Javascript is in a script tag, it's being run before Meteor has finished rendering all your templates, which means that ytplayer doesn't yet exist in the DOM and so it doesn't get replaced. So although player exists, you can't do anything with it, since there's no iFrame in the DOM. By making the change above, you will only load the YouTube API once the template containing ytplayer has been loaded for the first time, guaranteeing a successful replacement.
Note that if you have any significant latency, the YouTube API might not get round to firing the callbacks until the DOM has fully rendered, in which case the whole thing would work, which I assume it did sporadically.
I was having trouble isolating a bug in my JavaScript code for controlling Youtube videos via the iframe/HTML5 API.
I went back to the demo code on the main documentation page:
https://developers.google.com/youtube/iframe_api_reference
...and to my surprise, I see the same inconsistent behavior even when I use the demo code from there and don't change anything.
The behavior is that some of the time, when I load this page, the player autoplays (which is the intent of the code), and other times it doesn't. It always does succeed in loading. Also, the player never seems to stop after 6 seconds, in contrary to what the comments say.
Breakpoints verify that part of the issue at least is that onPlayerReady() is not always being called. When I set a breakpoint in onPlayerReady(), it usually is not reached, and is only reached the times that the player goes on to succeed in autoplaying.
Of course, this behavior may be dependent on the speed and reliability of my internet connection, which is wired and otherwise seems decently fast. I just tested it -- 24 Mbps, and it seems pretty consistent.
If I make superficial changes to the html, that seems to sometimes prompt the page on loading to autoplay, but not always. Sometimes I will reload every few seconds 5 times in a row with no autoplay or onPlayerReady breakpoint being hit, then do it a 6th time and that time it will autoplay fine.
I'm using Chrome v30.0.1599.101 on Mac OS 10.8.4.
I know that code is in beta and in flux, and isn't supposed to be production level yet, but I was hoping there was something i can try.
Here is the code I'm using, FYI, in case the code posted on the api reference page above changes. Again, I'm not altering a single character.
<!DOCTYPE html>
<html>
<body>
<!-- 1. The <iframe> (and video player) will replace this <div> tag. -->
<div id="player"></div>
<script>
// 2. This code loads the IFrame Player API code asynchronously.
var tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
// 3. This function creates an <iframe> (and YouTube player)
// after the API code downloads.
var player;
function onYouTubeIframeAPIReady() {
player = new YT.Player('player', {
height: '390',
width: '640',
videoId: 'M7lc1UVf-VE',
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
}
// 4. The API will call this function when the video player is ready.
function onPlayerReady(event) {
event.target.playVideo();
}
// 5. The API calls this function when the player's state changes.
// The function indicates that when playing a video (state=1),
// the player should play for six seconds and then stop.
var done = false;
function onPlayerStateChange(event) {
if (event.data == YT.PlayerState.PLAYING && !done) {
setTimeout(stopVideo, 6000);
done = true;
}
}
function stopVideo() {
player.stopVideo();
}
</script>
</body>
</html>
I was opening the page locally, which makes for different behavior in all sorts of little ways from how it works when delivered from a server. The browser client itself handles things like cacheing differently when content is local. When I put the same page on a server, it started to work every time.
In addition, FYI, for my bigger issue that led me down this rabbithole, I found that I needed to follow this advice: https://stackoverflow.com/a/14952334/2308190 ...in order to get my YouTube callback functions to be called with jQuery loaded.
I had a situation where my first YouTube iframe would load correctly, and subsequent ones would not.
The issue was caused by creating a new YT.Player before its <div> had been added to the DOM. This issue didn't occur the first time I created a YT.Player, because the YouTube API had to be loaded first. This caused a microsecond delay which allowed the rest of my code to execute, so the <div> was added to the DOM before the YouTube API finished loading.
I have an embedded youtube video with hidden controls:
<iframe id="ytplayer" type="text/html" width="400" height="225"
src="http://www.youtube.com/embed/dMH0bHeiRNg?rel=0&controls=0&showinfo=0
&loop=1&hd=1&modestbranding=1&enablejsapi=1&playerapiid=ytplayer"
frameborder="0" allowfullscreen></iframe>
I can control it with the youtube Javascript API.
var tag = document.createElement('script');
tag.src = "//www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
var player;
function onYouTubeIframeAPIReady() {
player = new YT.Player('ytplayer', {
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
}
Things like player.playVideo() and so on work perfectly. Now I am looking for a way to make the video play in fullscreen mode with a Javascript call but I couldn't find any method in the API.
Is it even possible (without the controls) and if so - how?
This worked perfect in my case. You can find more details on this link: demo on CodePen
var player, iframe;
var $ = document.querySelector.bind(document);
// init player
function onYouTubeIframeAPIReady() {
player = new YT.Player('player', {
height: '200',
width: '300',
videoId: 'dQw4w9WgXcQ',
events: {
'onReady': onPlayerReady
}
});
}
// when ready, wait for clicks
function onPlayerReady(event) {
var player = event.target;
iframe = $('#player');
setupListener();
}
function setupListener (){
$('button').addEventListener('click', playFullscreen);
}
function playFullscreen (){
player.playVideo();//won't work on mobile
var requestFullScreen = iframe.requestFullScreen || iframe.mozRequestFullScreen || iframe.webkitRequestFullScreen;
if (requestFullScreen) {
requestFullScreen.bind(iframe)();
}
}
You can use html5 fullscreen api:
node.requestFullScreen();
document.cancelFullScreen();
document.fullScreen; // bool
But note that:
this usually requires vendor specific prefix like mozRequestFullScreen() or webkitRequestFullScreen
requestFullScreen has to be called on user events (like onclick)
in firefox the fullscreen will be black until "Allow" is clicked by user
I've added code to do fullscreen (real full screen, not full window) to my answer on Auto-Full Screen for a Youtube embed.
YouTube don't expose fullscreen in their API, but you can call the native browser requestFullScreen() function from the playerStateChange() event from the YouTube API or make your own custom play button like I have.
Looking at the API reference for the iframe player, I don't think it's possible to set it to fullscreen (with the iframe API alone)- ctrl f didn't find fullscreen, so either it's a secret method hidden inside the API or it doesn't exist. However, as you probably know, you can set the size of the player player.setSize(width:Number, height:Number):Object, then make the window fullscreen.
Maybe have a look at this similar question. It looks as though this might work for you (using the javascript fullscreen api rather than the Youtube API):
Full Screen Option in Chromeless player using Javascript?
You can try setting the iframe dimension to the window dimension through javascript.
Something like this (using jQuery):
$('ytplayer').attr('width', $(window).width());
$('ytplayer').attr('height', $(window).height());
I hope this can help you.
I have set up some test code that loads in a youtube video and then a jquery click event on a link that I plan to stop the video, at the moment however when I click the link I get the error: Uncaught TypeError: Object [object Object] has no method 'stopVideo'
Can anyone suggest where I might be going wrong with this?
<div id="container">
This is the link
<br /><br />
<br /><br />
<div id="player"></div>
</div>
<script>
var tag = document.createElement('script');
tag.src = "http://www.youtube.com/player_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
// 3. This function creates an <iframe> (and YouTube player)
// after the API code downloads.
var player;
function onYouTubePlayerAPIReady() {
player = new YT.Player('player', {
height: '390',
width: '640',
videoId: 'u1zgFlCw8Aw'
});
}
function onPlayerReady(event) {
event.target.playVideo();
}
$(document).ready(function() {
$('a').click(function(e) {
player.stopVideo();
e.preventDefault();
});
});
</script>
Your code snippet works as intended in a fiddle. Also, the YouTube Frame API won't function at a file:/// protocol, because of limitations at the postMessage implementation.
I have previously created a custom implementation of the YouTube API, available at YouTube iframe API: how do I control a iframe player that's already in the HTML?. I have successfully executed the stopVideo method at a file:/// protocol.
If you're running your code online, I can only think of one other cause: You're trying to click at the link before the player is ready. To fix it, merge your functions:
function onPlayerReady(event) {
event.target.playVideo();
$('a').click(function(e) { //<--- Binds event to ALL anchors! Are you sure?
player.stopVideo();
e.preventDefault();
});
});