I have working code to update a video id. However, when I moved all my JS code from the script tag to a JS file, the code breaks. The code does not load, so iframe api does not load and the on click events do not execute.
<Script>
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', {
height: '390',
width: '640',
videoId: '{{no-video-id}}',
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
}
// 4. The API will call this function when the video player is ready.
function onPlayerReady(event) {
$('.open-popup').click(function() {
event.target.playVideo();
});
$('.close-popup').click(function(e) {
player.stopVideo();
});
}
// 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 === 0) {
$('.close.close-popup').click();
}
}
function stopVideo() {
player.stopVideo();
}
$(function () {
$('#my_modal').on('show.bs.modal', function(e) {
var bookId = $(e.relatedTarget).data('book-id');
$(e.currentTarget).find('input[name="bookId"]').val(bookId);
var x = new String(bookId);
player.loadVideoById(x);
});
$('#video-player-1').click(function(e) {
var bookId = $(e.relatedTarget).data('book-id');
var x = new String(bookId);
player.loadVideoById(x);
alert("HEELO");
});
$( "#video-player-2" ).click(function() {
alert( "Handler for .click() called." );
});
});
</script>
This is the JS fiddle showing the code moved into a separate file.
https://jsfiddle.net/k7FC2/7313/
When I click the tag, I get "TypeError: undefined is not an object (evaluating 'player.loadVideoById')" in the console
UPDATE Solution: Add <script src="youtube.com/iframe_api"> in html https://jsfiddle.net/k7FC2/7325/
Please call onYouTubeIframeAPIReady method after loading complete, html takes time to load. and your player is undefined. check here
$(function () {
// call onYoutubeIframeAPIReady method when you have access to all html
onYouTubeIframeAPIReady(); // add this line
$('#my_modal').on('show.bs.modal', function(e) {
var bookId = $(e.relatedTarget).data('book-id');
$(e.currentTarget).find('input[name="bookId"]').val(bookId);
var x = new String(bookId);
player.loadVideoById(x);
});
$('#video-player-1').click(function(e) {
var bookId = $(e.relatedTarget).data('book-id');
var x = new String(bookId);
player.loadVideoById(x);
alert("HEELO");
});
$( "#video-player-2" ).click(function() {
alert( "Handler for .click() called." );
});
});
and add script tag
<script src="https://www.youtube.com/iframe_api"></script>
Related
When $('#myModal').on('show.bs.modal' gets called. I get this:
TypeError: player.loadVideoById is not a function. (In 'player.loadVideoById(x)', 'player.loadVideoById' is undefined)
https://jsfiddle.net/rdbj3ty1/5/
In JS Fiddle the code works, but when I add it to my code base, it stops working. I have a lot of other js code and CSS code that it must be interfering with. Is there a way to prevent the interference? Maybe wrap my js code with a block or add more specific references? Is namespacing the issue? If so, how do I do that?
I don't really know what to try. I have spent 2 days on this and getting nowhere.
JS
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', {
height: '390',
width: '640',
videoId: 'novideoid',
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
}
// 4. The API will call this function when the video player is ready.
function onPlayerReady(event) {
$('.open-popup').click(function() {
event.target.playVideo();
});
$('.close-popup').click(function(e) {
player.stopVideo();
});
}
// 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 === 0) {
$('.close.close-popup').click();
}
}
function stopVideo() {
player.stopVideo();
}
$(function () {
onYouTubeIframeAPIReady();
$('#myModal').on('show.bs.modal', function(e) {
var videoId = $(e.relatedTarget).data('video-id');
var x = new String(videoId);
player.loadVideoById(x);
});
});
I already know how to play/pause a YouTube video dynamically as you can see from my weave.
However I'm having difficulty getting it to work dynamically with FancyBox using onBlur (when the window is not focused video should pause).
Here's my Pen: http://codepen.io/anon/pen/wJXoJy?editors=0010
// Show Lightbox Video Onload
$.fancybox.open({
youtube : {
controls : 0,
showinfo : 0
},
src : '//www.youtube.com/embed/-AszZcClVXA?enablejsapi=1', // Source of the content
type : 'iframe', // Content type: image|inline|ajax|iframe|html (optional)
});
// Inject YouTube API script
var tag = document.createElement('script');
tag.src = "//www.youtube.com/player_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
var player;
function onYouTubePlayerAPIReady(e) {
// create the global player from the specific iframe (#video)
player = new YT.Player($(".fancybox-iframe").attr("id"), {
events: {
// call this function when player is ready to use
'onReady': onPlayerReady
}
});
}
function onPlayerReady() {
window.addEventListener("blur", function() {
player.pauseVideo();
});
}
<link rel="stylesheet" href="//www.fancyapps.com/fancybox/3/fancybox/jquery.fancybox.min.css?v=1490320405" />
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="//www.fancyapps.com/fancybox/3/fancybox/jquery.fancybox.min.js?v=1490320405"></script>
Here is a demo - http://codepen.io/fancyapps/pen/jBKowp?editors=1010
Basically, idea is that you initialize fancyBox after YouTube API is available and then you can use YouTube API methods inside fancyBox callback.
// 1. 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);
// 2. This function initializes fancyBox
// after the API code downloads.
var player;
function onYouTubeIframeAPIReady() {
$('[data-fancybox="group"]').fancybox({
onComplete : function( instance, current ) {
if ( current.type === 'iframe' ) {
var id = current.$content.find('iframe').attr('id');
player = new YT.Player( id, {
events : {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
}
}
});
}
// 3. The API will call this function when the video player is ready.
function onPlayerReady(event) {
event.target.playVideo();
}
// 4. Fires when the player's state changes.
function onPlayerStateChange(event) {
// Go to the next video after the current one is finished playing
if (event.data === 0) {
$.fancybox.getInstance('next');
}
}
// Page Visibility API to pause video when window is not active
$(document).on("visibilitychange", function() {
if ( player ) {
if ( document.hidden ) {
player.pauseVideo();
} else {
player.playVideo();
}
}
});
You're only pausing the video on blur. Why not use mouseover event to play and mouseout event to pause the video?
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.
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!)
i need to show a popup when youtube video finish it works fine for the alert() but when i tried to make magnific popup instead if the alert it dosen't work here is my code
<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: '600',
width: '600',
videoId: 'id',
playerVars: { 'autoplay':"0", 'controls':"1" },
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
}
var playerReady = false;
function onPlayerReady(event) {
playerReady = true;
}
function onPlayerStateChange(event) {
if (event.data == YT.PlayerState.ENDED ) {
$.magnificPopup.open({
items: {
src: 'mylink to the pic'
},
type: 'image'
// You may add options here, they're exactly the same as for $.fn.magnificPopup call
// Note that some settings that rely on click event (like disableOn or midClick) will not work here
}, 0); }
}
</script>