In one of my modals I want to display a youtube video. Which one (which ID) depends on which button is used to open the model.
<button class='yt-play' data-yt='xxxxxx'>Play video</button>
In my javascript file I'm using the YT player-api to generate an iframe; i followed the Getting started on google developers.
So In the modal I added an <div id='player'></div> and This is my included javascript:
var tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
var player;
function onYouTubeIframeAPIReady() {
player = new YT.Player('player', {
videoId: '5ulO97zuVF0', //- just a temporary id
});
}
// on document ready do some jQuery things,
// like adding an event handler to the button.
$(document).ready(function (){
$('.yt-play').click(function(ev){
ev.preventDefault();
var videoid = $(this).data('yt');
player.loadVideoById(videoid);
$('#yt-player').modal('show');
player.playVideo();
});
});
The click-handler on yt-play should load the video by means of player.loadVideoById() as stated here in the documentation.
But somehow I get an javascript error: TypeError: player.loadVideoById is not a function
If I dump the player-object in the console I'm getting a nice player object; which holds amongst many others the loadVideoById function. At least it looks like it:
What's the reason the new video is not loaded?
It's possibly because the "loadVideoById" is not yet available.
You must construct your YT.Player Object with an events object, and include an "onReady" Event callback.
Then in your "onReady" callback function you bind your Button click event.
function onPlayerReady() {
$('.yt-play').click(function(ev){
ev.preventDefault();
var videoid = $(this).data('yt');
// player.loadVideoByID is now available as a function
player.loadVideoById(videoid);
$('#yt-player').modal('show');
player.playVideo();
});
}
player = new YT.Player('player', {
videoId: '5ulO97zuVF0', //- just a temporary id,
events:{
“onReady”: onPlayerReady
}
});
See the events section in the docs for more:
https://developers.google.com/youtube/iframe_api_reference#Events
add div element in html document
initial YT to global object in onYouTubeIframeAPIReady function
call YT object in your function
The code:
<script>
// load API
var tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
// define player
var player;
function onYouTubeIframeAPIReady() {
player = new YT.Player('player', {
height: '360',
width: '640'
});
}
$(function () {
// load video and add event listeners
function loadVideo(id, start, end) {
// check if player is defined
if ((typeof player !== "undefined")) {
// listener for player state change
player.addEventListener('onStateChange', function (event) {
if (event.data == YT.PlayerState.ENDED) {
// do something
}
});
// listener if player is ready (including methods, like loadVideoById
player.addEventListener('onReady', function(event){
event.target.loadVideoById({
videoId: id,
startSeconds: start,
endSeconds: end
});
// pause player (my specific needs)
event.target.pauseVideo();
});
}
// if player is not defined, wait and try again
else {
setTimeout(loadVideo, 100, id, start, end);
}
}
// somewhere in the code
loadVideo('xxxxxxxx', 0, 3);
player.playVideo();
});
</script>
The other answers don't clarify the issue.
The YouTube api is confusing for sure! This is because the YT.Player constructer returns a dom reference to the iframe of the youtube player not to the YT player object.
In order to get a reference to the YT player we need to listen to the onReady event.
var ytPlayer;
var videoIframe = new YT.Player('player', {
videoId: 'xxxxxxx', // youtube video id
playerVars: {
'autoplay': 0,
'rel': 0,
'showinfo': 0
},
events: {
'onStateChange': onPlayerStateChange,
'onReady': function (ev) {
ytPlayer = ev.target;
}
}
});
Only 6 years late to the party, I have the solution.
The issue is that the YouTube API doesn't like the DOM shuffling Foundation does when it opens a modal.
The only way to achieve this is to create the YouTube player after opening the modal:
function play_video(ytid) {
modal = new Foundation.Reveal($('#yt_modal'),{});
modal.open();
ytplayer = new YT.Player('ytplayer', {
videoId: ytid,
height: '390',
width: '640',
playerVars: {
'playsinline': 1
},
events: {
'onReady': whatever()
}
});
}
This assumes
<div id="ytplayer"></div>
is inside your Foundation modal.
Oh and you'll need to remove the iframe each time else YouTube API just looks for the old iframe (which isn't where it expects, cos the modal is closed and the DOM has changed):
$(document).on('closed.zf.reveal', function(e) {
switch($(e.target).prop('id')) {
case 'yt_modal':
ytplayer.destroy();
break;
}
});
And regarding #Tosh's answer about the API returning a reference to the iframe not the player is not true as far as I can determine by comparing it to what's returned by onReady(ev.target) - they appear identical (so don't go down that blind alley like I did!)
Related
I am trying to create a button that allows the user to click it and stop the embedded youtube video. However, whenever I try and call the player object itself to use the function player.playVideo() I get an error saying the function is not defined.
Player is globally defined and set when the Youtube API loads (just like the tutorial on their website). Function calls to playVideo work just fine when events trigger their usage, but using it outside of those simply do not work.
'''javascript
// 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);
var player;
function onYouTubeIframeAPIReady() {
isReady=true;
player = new YT.Player('player', {
height: '390',
width: '640',
videoId: 'M7lc1UVf-VE',
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
}
function onPlayerReady(event) {
playVideo();
}
var done = false;
function onPlayerStateChange(event) {
if (event.data == YT.PlayerState.PLAYING && !done) {
setTimeout(stopVideo, 6000);
done = true;
}
}
function stopVideo() {
player.pauseVideo();
}
function playVideo() {
player.playVideo();
}
//The function that is run when the button is pressed, only in this case
// stopVideo not work!
function togglePlay() {
if(isPlaying) {
stopVideo();
} else {
playVideo();
}
}
'''
Expected: Youtube video plays
Actual: Error player.playVideo is not a function
I solved the issue. Something weird happens with even setting up buttons with onclick functions that use the player before the player is finished loading. To solve this, I moved all button declarations and functions inside the onPlayerReady(event) function and used event.target.play or event.target.pause and that worked perfectly. My advice is just to contain any declarations of buttons that use the youtube player functions inside of the onPlayerReady.
I'm adding a custom player to my website and I have two issues.
My playlist on YouTube has 391 songs and the API player only loads 200.
I have problems shuffling the list using the API commands.
Here's the code that I'm using to call to the player:
<div class="videoplayer" id="ytplayer"></div>
<script>
var getRandom = function(min, max) {
return Math.random() * (max - min) + min;
};
// 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() {
player = new YT.Player('ytplayer', {
height: '50',
width: '400',
events: {
'onReady': onPlayerReady
},
playerVars:{
list:'PLH30k6CwlcgK8C-LET6_ZrAWwYGLqT8R-',
index:parseInt(0),
suggestedQuality:'small',
controls:0,
autoplay:1
}
});
}
function onPlayerReady(event) {
event.target.setShuffle();
event.target.setLoop();
}
</script>
I tried to workaround the shuffle thing using:
function onPlayerReady(event) {
var num = parseInt(getRandom(1,391));
event.target.playVideoAt(num);
}
But it doesn't work like I want, so I want retrieve the ID's of all the videos in the playlist into an array, mix them and pass all the array to load each video. I was trying to use some examples from previous questions but those examples are not working anymore, if some of you guys has examples with the v3 of the YouTube API or actually working examples I will appreciate it.
Regards.
First, you have to use iframe API
because, javascript API is outdated and deprecated
Look for youtube API documentation (list of deprecated api's on left side menu)
https://developers.google.com/youtube/iframe_api_reference?hl=fr
replacing
tag.src = "https://www.youtube.com/player_api";
// by
tag.src = "https://www.youtube.com/iframe_api";
and
function onYouTubePlayerAPIReady()
// by
function onYouTubeIframeAPIReady()
0 To fix shuffling, try to randomize at each end state
by using onPlayerStateChange event listener, and YT.PlayerState.ENDED
state.
1 You need to CUE the playlist to get whole video's ids
before the randomization.
and get them with proper method : getPlaylist()
var player;
var videoList, videoCount;
function onYouTubeIframeAPIReady()
{
player = new YT.Player('ytplayer',
{
height: '50',
width: '400',
events:
{
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
},
playerVars:
{
controls:0,
autoplay:0
}
});
}
function onPlayerReady(event)
{
// cue the playlist, to get the video's ids
event.target.cuePlaylist
({
listType: 'playlist',
list: 'PLH30k6CwlcgK8C-LET6_ZrAWwYGLqT8R-',
suggestedQuality:'small',
autoplay: 1,
index:0,
});
event.target.setLoop();
}
function onPlayerStateChange(event)
{
if(event.data == YT.PlayerState.CUED)
{
videoList = event.target.getPlaylist();
// to prevent adding new video and for the randomize
videoCount = videoList.length;
// get the ids before randomize playlist, send it
sendIds(videoList);
// starting the player (like autoplay)
event.target.playVideo();
}
// randomize at each video ending
if(event.data == YT.PlayerState.ENDED)
{
var num = getRandom(1,videoCount);
event.target.playVideoAt(num);
}
}
function sendIds(ids)
{
console.log(ids);
// make what you want to do with them... ajax call to your php
}
I am trying to implement the example youtube api html page described here: https://developers.google.com/youtube/iframe_api_reference in a meteor application.
I have read that you can use Template.<template name>.rendered to implement traditional javascript functionality within a meteor application.
So I attempted to implement that youtube example in meteor by putting it into a rendered function.
However No video will display.
I worry I am not understanding meteors capabilities. Is something like this even possible in meteor?
Code:
home.html:
enter code here
<template name="home">
<h1> Home</h1>
This is the home page
<!-- 1. The <iframe> (and video player) will replace this <div> tag. -->
<div id="player"></div>
<script>
</script>
</template>
home.js:
Template.home.rendered = function() {
// 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();
}
}
P.s. I am aware of adrianliaw:youtube-iframe-api and do not want to use that. I would like a better understanding of how to implement this on my own. Hopefully doing so will further my knowledge of javascript and meteor.
I think the problem here is that after https://www.youtube.com/iframe_api loads, it will try to call the onYouTubeIframeAPIReady function but it can't find it. The code works if you change the functions to anonymous functions stored in variables that are available across the entire application.
home.html:
<head>
<title>test</title>
</head>
<body>
{{> home}}
</body>
<template name="home">
<h1> Home</h1>
This is the home page
<div id="player"></div>
</template>
home.js:
Template.home.rendered = function() {
/* 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;
onYouTubeIframeAPIReady = function() {
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. */
onPlayerReady = function(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;
onPlayerStateChange = function(event) {
if (event.data == YT.PlayerState.PLAYING && !done) {
setTimeout(stopVideo, 6000);
done = true;
}
};
stopVideo = function() {
player.stopVideo();
};
};
Notice the function declaration changes:
function onYouTubeIframeAPIReady() has been changed to
onYouTubeIframeAPIReady = function()
function onPlayerReady(event) has been changed to onPlayerReady
= function(event)
function onPlayerStateChange(event) has been changed to
onPlayerStateChange = function(event)
function stopVideo() has been changed to stopVideo = function()
These global variables are now callable by the injected YouTube script.
I was trying the following code from the samples given on developers.google.com/*
<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: 'yZxrao3zou4',
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>
The code works perfectly when I have nothing else on my web page, but when I try to merge it with my project it doesn't seem to work.
I am guessing the problem is with the following lines:
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
Can someone tell me what does the above two lines do, especially the [0] part?
My code is pretty much the same except that instead of the script tag I have the code inside a function, which takes in a argument for the videoId.
EDIT:
My code is as follows:
<script>
// I have a input area, where the user can enter the movie name. When the user submits the movie name, I capture the val and pass it to the youtube().
function youtube(movie_name) {
var videoId;
$.ajax({
url:"https://www.googleapis.com/youtube/v3/search?part=snippet&q="+movie_name+"&type=video&key=my_key",
success: function (response) {
videoId = response.items[0].id.videoId;
findMovieById(videoId);
}
});
}
function findMovieById(videoID) {
$("#player").css('display', 'inline-block');
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: ""+videoID,
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
}
// 4. The API will call this function when the video player is ready.
function onPlayerReady(event) {
alert('Player Ready');
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>
The idea behind the sample code is that it is much more consistent when you load the YouTube iFrame library code after the rest of the page has loaded; hence the sample code demonstrates that you put an inline at the bottom of the page, and within that, you traverse the DOM, find a place where you can insert another tag, and do so dynamically (the [0] just says 'the first entry in the array of all elements of name in the document).
The logic here is that, when the iFrame library loads, it will call the function onYouTubeIframeAPIReady. But since the library loads asynchronously, it's best to load it this way to ensure that anything else that your API hooks might depend on (the element in the DOM you're binding to, for example) already exists.
Also note that, because the loaded library will always call the onYouTubeIframeAPIReady function, it MUST be defined outside any other function. Otherwise it isn't callable. That could be why nesting it inside your code somewhere isn't working.
Feel free to post some of your merged code for more detailed help.
I'm trying to load YouTube's iframe API. So far, all I'm trying to do is make and load the player. It seems to load the API, but then not recognize "YT.Player()" as a constructor. The exact error I'm getting at that line, in the chrome js console, is:
Uncaught TypeError: undefined is not a function
So... What in the world am I doing wrong? I've thrown console.log statements all over this thing, and tried rewriting it in a few ways. I've tried copying the api into a local file. I've tried loading it with regular script tags. I've tried loading it with the wacky DOM Modification they used in the api reference at https://developers.google.com/youtube/iframe_api_reference. I'm pretty sure the code below should work:
function youtubeAPIReady(script, textStatus, jqXHR)
{
player = new YT.Player('player', {
height: '390',
width: '640',
videoId: 'CxTtN0dCDaY'
});
}
function readyFunction()
{
$.getScript("https://www.youtube.com/iframe_api", youtubeAPIReady);
}
jQuery(document).ready(readyFunction);
Any help?
You can borrow the technique used in YouTube Direct Lite to defer loading the JavaScript until it's explicitly needed:
var player = {
playVideo: function(container, videoId) {
if (typeof(YT) == 'undefined' || typeof(YT.Player) == 'undefined') {
window.onYouTubeIframeAPIReady = function() {
player.loadPlayer(container, videoId);
};
$.getScript('//www.youtube.com/iframe_api');
} else {
player.loadPlayer(container, videoId);
}
},
loadPlayer: function(container, videoId) {
new YT.Player(container, {
videoId: videoId,
width: 356,
height: 200,
playerVars: {
autoplay: 1,
controls: 0,
modestbranding: 1,
rel: 0,
showInfo: 0
}
});
}
};
poor man's solution, but ...
function readyYoutube(){
if((typeof YT !== "undefined") && YT && YT.Player){
player = new YT.Player('player', {
...
});
}else{
setTimeout(readyYoutube, 100);
}
}
Quote from http://api.jquery.com/jQuery.getScript/
The callback is fired once the script has been loaded but not
necessarily executed.
The API probably hasn't run by the time you call YT.Player()
I can't speak for the jQuery solution, but try using the stock standard javascript. In any case you won't have to wait for the document to be loaded (this code should sit outside $(document).ready())
// Load the YouTube API 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);
// Create the player object when API is ready
var player;
window.onYouTubeIframeAPIReady = function () {
player = new YT.Player('player', {
height: '390',
width: '640',
videoId: 'CxYyN0dCDaY'
});
};
I've solved this issue by combining approaches of Simon and user151496.
The code:
<script>
// load API
var tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
// define player
var player;
function onYouTubeIframeAPIReady() {
player = new YT.Player('player', {
height: '360',
width: '640'
});
}
$(function () {
// load video and add event listeners
function loadVideo(id, start, end) {
// check if player is defined
if ((typeof player !== "undefined")) {
// listener for player state change
player.addEventListener('onStateChange', function (event) {
if (event.data == YT.PlayerState.ENDED) {
// do something
}
});
// listener if player is ready (including methods, like loadVideoById
player.addEventListener('onReady', function(event){
event.target.loadVideoById({
videoId: id,
startSeconds: start,
endSeconds: end
});
// pause player (my specific needs)
event.target.pauseVideo();
});
}
// if player is not defined, wait and try again
else {
setTimeout(loadVideo, 100, id, start, end);
}
}
// somewhere in the code
loadVideo('xxxxxxxx', 0, 3);
player.playVideo();
});
</script>
Remove the add block from your browser and try again. Its worked for me.