I'm developing a Rails4 app which will be able to embed and play back videos hosted on Youtube in my application's pages. I need to hook into some of the video player's events, so I'm creating the Youtube object via an API call:
<script id="video_script" ></script>
<div id="player"></div>
<%= javascript_tag do %>
var tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
var scriptTag = $("script#video_script");
scriptTag.parent()[0].insertBefore(tag, scriptTag[0]);
var youtube_player;
function onYouTubeIframeAPIReady() {
youtube_player = new YT.Player('player', {
videoId: <%= #video.id %>,
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
}
function onPlayerReady(event) {
....
}
function onPlayerStateChange(event) {
....
}
<% end %>
This works perfectly well when the page is first loaded, but doesn't work when a user navigates between pages with turbolinks enabled, since the document ready event doesn't fire, and consequently the Youtube code doesn't know to issue the callback to onYouTubeIframeAPIReady().
So, I was wondering if there are any workaround that would get the Youtube API to call the API Ready method for the page:change event to make it work with turbolinks, in addition to the document.ready event? I've tried digging through the api script that gets downloaded from Youtube, but couldn't find anything in there that would solve the issue, and I'm also a little hesitant to be running a local version of Youtube's script.
Turbolinks reload only content of title and body (so watch out for polluting head with too many scripts). To avoid it I check if YT object exists. Plus I do additional call to onYouTubeIframeAPIReady on turbolinks 'page:load' event.
<script type="text/javascript">
/* load youtube js api */
var reloadYoutube = function () {
/* if YT already initialized return */
if (window.YT) { return; };
var tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
};
reloadYoutube();
$(document).on('page:load', function() {
onYouTubeIframeAPIReady && onYouTubeIframeAPIReady();
});
/* youtube callback */
var ytplayer = null;
function onYouTubeIframeAPIReady() {
// it is important to return when window.ytplayer
// is already created - because you will be missing
// certain methods like getCurrentTime
if (!window.YT || window.ytplayer) {
return;
}
ytplayer = new YT.Player('main_player_div', {
height: '315',
width: '560',
videoId: getVideoId(),
events: {
'onReady': onPlayerReady
}
});
}
Use the jquery-turbolinks gem, https://github.com/kossnocorp/jquery.turbolinks . Then just make sure to wrap your code in $( document ).ready() .
Related
I am new to coding and I am trying to learn how to use the youtube IFRAME to control embded videos. The following code is mostly from youtube's api documentation. The only thing I added were the buttons and event listeners that are attached to them.
Can someone explain to me why "play" button that I have created can't start the video?
// 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: '250',
width: '300',
videoId: 'M7lc1UVf-VE',
events: {
'onReady': onPlayerReady,
}
});
}
// 4. The API will call this function when the video player is ready.
function onPlayerReady(event) {
event.target.playVideo();
}
document.getElementById('pause').onclick = function() {
player.pauseVideo();
};
document.getElementById('play').onclick = function() {
player.playVideo();
};
<html>
<body>
<div><button id= "pause">Pause</button></div>
<div><button id= "play">Play</button></div>
<!-- 1. The <iframe> (and video player) will replace this <div> tag. -->
<div id="player"></div>
</body>
It seems that the documentation of the iFrame API is either outdated or completely wrong.
The function which should initialize the player onYouTubeIframeAPIReady() will never fire thus a click on the play/pause buttons won't work because the variable player is undefined.
To workaround this, you have to wait until the script you're loading into the freshly created <script> element has finished loading and inside the callback function you need to initialize the YT component by a call to it's own ready() function. This isn't mentioned anywhere.
Well, get rid of the complete onYouTubeIframeAPIReady function and replace it with this:
tag.onload = function() {
YT.ready(function() {
player = new YT.Player('player', {
height: '250',
width: '300',
videoId: 'M7lc1UVf-VE',
events: {
'onReady': onPlayerReady,
}
});
});
};
I am having issues getting the youtube api working. The API itself loads and the onYouTubeIframeAPIReady() function gets called, but the onReady doesn't work.
var tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
function onYouTubeIframeAPIReady() {
log('API')
log(document.getElementById('yt-pilezspnvu'));
var player = new YT.Player('yt-pilezspnvu', {
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
}
function onPlayerReady(event) {
log(event);
}
function onPlayerStateChange(event) {
log(event);
}
The code above isn't wrapped in any functions or anything. The errors in the picture below are just my adblock stuff. I've tried adding origin=http://example.com as pointed out in this thread https://stackoverflow.com/a/20505337/736967 but still not working.
I think that you need some extra bits that are missing.
Try adding in this to your code.
ytplayer = new YT.Player('ytplayer', {
height: '385',
width: '640',
videoId: '[your-videoID]',
playerVars: {'wmode': 'opaque', 'autohide': 1 , 'enablejsapi': 1 , 'origin': 'http://www.yousite.com', 'rel': 0},
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange,
}
playerVars has many parts and you can use or not. But you definatly need to have something load into the player. VideoID or PlayList. Else nothing will happen.
I had a similar issue with an overlay image being clicked and the onReady event never seemed to fire even though all the other events did; obvious through console logs in each.
The issue was apparently a race event. Clicking the overlay image should remove the image and start the YouTube video playing. I also have several videos on the same page. The click event to play the video should be placed inside the onReady event instead of the click event firing the play event. I have trimmed this down as much as I could as our DOM has several elements inside the video container, including the overlay image, captions, etc.
Here is my solution:
var player = new Array;
window.onYouTubeIframeAPIReady = function() {
// Loop through the existing video iframes designated with the inital string 'vidframe_' in the id
jQuery('iframe[id^="vidframe_"]').each(function (i, val) {
var $this = $(this); // iframe container
var $vidWrapper = $this.closest('.video-wrapper');
// Make sure it is a YouTube video. This data attribute is set when the iframe is created by my code
if( $vidWrapper.data("video-source") == "youtube" )
{
// Initialize each YouTube video present
player[i] = new YT.Player($this.attr('id'), {
events: {
onReady: function(event) {
// Set the click event listener here
$vidWrapper.on("click", function() {
// Target the overlay image
// 'this' is now the .video-wrapper image element
var imgOver = $(this).find('figure');
// Remove image overlay
if( imgOver )
imgOver.fadeOut();
// Target the right player
player[i].playVideo();
});
}
}
});
}
});
};
I hope this helps anyone else who may be searching for an errorless onReady event not seemingly working. This css-tricks article is what really gave me the best clue.
Basically, i alrdy managed to create a website to search for videos using the youtube video api.
I would like now to add a youtubevideo player (iframe?), and possibly be able to queue videos (url) from a text input.
My next question, i found on the internet the required code for using an iframe,
but the script is all in html page, and i was wondering if it was possible to place the code in my JS page.. i tried it, but doesn't work... I thought that placing the syntaxes between & in my js file would do it...
Can someone explain how i can make it work while placing the code in my js file?
I know it's prboably a stupid question...
Thanks a lot!
<html>
<body>
<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 tried it the following way;
in html:
<script src="js/app.js"></script>
in js;
// 2. This code loads the IFrame Player API code asynchronously.
var tag = document.createElement('script');
..... etc
}
http://jsfiddle.net/y2Y5k/
I'm trying to make a simple youtube player with the javascript API and I'm doing something wrong.
In that fiddle, Im just using the exact code snippets from the documentation provided by google.
I loaded the api as a source.
I placed a div with the id 'player'.
What did I do wrong here?
<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>
It is because Fiddle. All JavaScript you put in there is encapsulated in other functions (probably window.onload or something).
Therefore Youtube can't get to your onYouTubeIframeAPIReady function.
Just try this outside Fiddle. It will work.
On the top left side, from Frameworks & Extensions, select No wrap - in instead of onLoad.
That will give you the result as expected.
I'm using jQuery to try to show a hidden YouTube player, and then start playing the video once its containing div is shown.
The problem is that this only works on Chrome, not in IE or FireFox. In IE, I see the error: JSON is undefined.
What can I do to make this work right in all three browsers?
Here's the code I have so far:
var tag = document.createElement('script');
tag.src = "http://www.youtube.com/player_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
var player;
function onYouTubePlayerAPIReady() {
player = new YT.Player('player', {
height: '390',
width: '640',
videoId: 'r3lPq7qY3TU',
events: {
'onReady': onPlayerReady
}
});
$('div#target_wrap img, div#main_vid').click(function(event){
event.preventDefault();
var p = $('#player');
p.slideToggle(function(){
if (p.is(":visible")) {
player.playVideo();
} else {
player.stopVideo();
}
});
});
}
My guess is that it's lucky accident that it works on Chrome in the first place. For example a common problem is not waiting for the call to "onYouTubePlayerReady". At least for debugging purposes you'll also want to do something like:
$('div#target_wrap img, div#main_vid').click(function(event){
event.preventDefault();
$('#player').slideToggle(function(){
if (player.getPlayerState) {
alert("player state: " + player.getPlayerState());
if ($('#player').is(":visible")) {
player.playVideo();
} else {
player.stopVideo();
}
} else {
alert("Player not ready yet.");
}
});
});
If you can share more of your code we might have a better answer.
Don't really know anything about playing YouTube videos via JS, but some versions of IE doesn't include an implementation of the JSON object which serializes/deserializes to/from JSON. You can find an implementation here.
You may wish to include this via conditional comments or use a script loader so it's loaded only when needed.
if you can't achieve crossbrowser compatibility i give you a suggestion:
onClick call the same video again but with the parameter &autoplay=1