Loading the Youtube Player API inside an iframe - javascript

I have a website with several internal (i.e. without src) iframes inside, where I want to include a Youtube player (using its API). In short, I want players inside iframes, managed by the parent page.
Problem is that the code initialization, AFAIK, is not working with iframes. For example:
var player = new YT.Player('video-player1', {});
The problem, as you might have guessed, is that you can't define the document in which that ID is contained. In jQuery, I would use something like:
$("video-player1", frames["iframe1"].document)
Is there any workaround for this? The only solution I can see is, obviously, loading the YT api in every iframe and working inside of any iframe, but that would mean refactorizing a lot of logic from my application, besides the additional cost of several loads of the Youtube JS files.

I'm sorry mate. The solution to your problem is "easy" to code/implement but painful and difficult to maintain.
Youtube API does not allow to embed a YT.Player object within an Iframe (e.g. div within an iframe), because it looks for the 'player' node within the window object and not in the iframe document.
So, a quick hotfix for this would be to save a copy of the API files and modify them to add this functionality. Obviously, from that moment on, it is your responsability to serve these files and also to update them in order the files do not get deprecated.
The solution would be (I take for granted JQuery is loaded before Youtube API):
Using the base example provided at https://developers.google.com/youtube/iframe_api_reference#Getting_Started I guess you have this
<iframe id="if"></iframe> instead of <div id="player"></div> and that later on you append the player div inside that iframe.
<body>
<iframe id="if"></iframe>
<script>
$('#if').contents().find('body').append($('<div id="player"></div>'));
// ...
So, whem defining onYoutubeAPIReady(), you must add 1 parameter to the YT.Player constructor:
function onYouTubeIframeAPIReady() {
player = new YT.Player('player', { /* options */ }, $('#if') );
That is $('#if'), the iframe element where you want to embed the player.
iframe_api
In this file you just need to modify the src of the script it loads:
from
a.src = 'http://s.ytimg.com/yts/jsbin/www-widgetapi-vflOb0oo1.js
to a.src = './widget.js'; (widget.js is your copy of www-widgetapi-vflOb0oo1.js).
Finally on widget.js:
Here you must modify this 2 functions: function S(a,b) and function Y(a,b).
First function Y(a,b) to function Y(a,b,c) in order to get the iframe parameter. Then in its body you change S.call(this,a,new nb(b)); to S.call(this,a,new nb(b),c);
Second, function S(a,b) to function S(a,b, dom) and c = document to
c= dom === undefined ? document : dom.contents()[0].
Now you have a Youtube player inside your iframe and you are able to use it from the parent window.
I hope it is useful! ;)

Related

Calling a single function from the YouTube Player API?

I want to use the YouTube Player API to call a single function that will stop an embedded YT video (loaded dynamically on the page with JS and jQuery) from playing on a button click.
I'm admittedly (very) new to working with APIs for web development and despite reading through several of the other answers (such as here, here and here), I am still confused as to the necessity of the different JS components.
I have a code block that is dynamically appended multiple times to the HTML page, with the YouTube URL changing for each entry based on a separate JSON file. The added code is as follows...
<li>
<section class="youtube-video">
<iframe src="'theLoadedURL'?enablejsapi=1&rel=0" frameborder="0" allowfullscreen="1">
</iframe>
</section>
</li>
... and then I have added the <script> tag at the bottom of the <body> that loads(?) the API...
<script src="https://www.youtube.com/iframe_api"></script>
... and finally a simple button for demonstration.
<button> Stop Video </button>
I was hoping that there would be a way to then simply call the player.stopVideo(); function from the API and have it affect the already-embedded iFrame on the button click, something like...
$('button').on('click', function () {
$('.youtube-video iframe').stopVideo();
});
... but alas, it doesn't seem to be that simple!
The documentation and other answers appear to suggest defining various variables and functions for the player, or deal with loading videos manually (rather than dynamically, as in this case); but it seems unnecessarily complex for an element that is already loaded on the page - is there something that I'm missing or an obvious way of simplifying things?
Detailed answers would be much appreciated as I'm still getting to grips with JS in general.
Thanks! :)
So from playing around with New's fiddle from the comments, I believe I have found the simplest way of implementing the Youtube Player API, in my situation.
Firstly, it requires minor editing the HTML <li> code block that, in practice, would be loaded dynamically onto my page:
<li>
<section class="active">
<iframe id="youtube-video" src="https://www.youtube.com/embed/y1yFiotx3qk?
rel=0&modestbranding=1&showinfo=0&enablejsapi=1" frameborder="0"
allow="autoplay; encrypted-media" allowfullscreen></iframe>
</section>
</li>
The main changes come from the addition of a class of .active being added to the <section> tag to specify the current video targeted by the YouTube Player API. The iframe then has an id of #youtube-video. It should be noted that all ids and classes are completely arbitrary.
At the bottom of the HTML's <body> I have added the API script...
<script src="https://www.youtube.com/iframe_api"></script>
... however, found that it is important that this is placed above all other <script> tags in the same location.
Following this, I have set up my JS file (with jQuery) as follows:
$(document).ready(function () {
// Creates arbitrary global 'player' variable to be defined later
var player;
function onYouTubeIframeAPIReady() {
setTimeout(function() { //Using this as seemed to come across occasional bugs in Chrome
console.log('API Loaded!')
// Selects first <li> tag iframe contained in a section with the class of '.active'
var $selectedVideo = $('.active #youtube-video')[0];
// Uses the previous variable to define the youtube player
player = new YT.Player($selectedVideo);
}, 100);
}
// Calls the Function
onYouTubeIframeAPIReady();
});
Finally, also within the $(document).ready() function, I am able to bind the relevant YouTube Player API event handlers to the <button> HTML element:
$('button').on('click', function(){
player.stopVideo();
});
The intention behind my code is to form part of a slideshow, and thus there should only be one section that has the .active class at any one time and this can be used to select different videos on different slides.
I've put together an example JSFiddle here that contains a better UI and more in-depth comments, which may be useful to someone in future.

Can't attach mousedown event to embed element

I seem to have hit a wall with this crap and can't make sense of it. I have a page with a relatively complex SVG (it has 6 other smalled SVGs embedded into it) in it embeded inside embed tag. When I try to attach a mousedown event to it, it simply doesn't work. Here is the markup:
<body>
<embed type="image/svg+xml" src="images/cog.svg" id="cog">
</embed>
</body>
then in javascript I do
$(function(){
var cog = document.getElementById("cog");
document.addEventListener('mousedown', function(e){
console.log(e);
});
});
It doesn't work. Moreover when i attach events to document, they do not fire when I click on that embed object either!
I searched this thing for 3 hours and everywhere I look it appears as if the embed should support mousedown even without any problem. What is the problem here?
Edit:
If it's any help here is how the full markup looks like (I use Foundation framework):
If CORS aren't an issue, you can try :
document.querySelector('embed').addEventListener('load', function(){
this.getSVGDocument().addEventListener('mousedown', function(){
alert('hello')});
});
According to the specs:
3 . If the previous step determined that the content's type is
image/svg+xml, then run the following substeps:
If the embed element is not associated with a nested browsing context, associate the element with a newly created nested browsing context,[...]
Navigate the nested browsing context to the fetched resource, with replacement enabled, and with the embed element's node document's
browsing context as the source browsing context. (The src attribute of
the embed element doesn't get updated if the browsing context gets
further navigated to other locations.)
The embed element now represents its associated nested browsing context.
But I can't find anything about actually how it should handle events.
Nevertheless, it appears that when its src is set and returns something, the events are captured by the new context (whether reached or created).
See #Marius comment for a demo when no src is set, it fires the event.
I think the embed tag is sandboxed and can't be accessed from external sources. Just use the img tag.
$('#logo').click(function() {
alert('410');
});
img{
width: 200px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h3>Click the SVG</h3>
<img id='logo' src="http://dev.w3.org/SVG/tools/svgweb/samples/svg-files/410.svg">

Javascript to image fallback

I am working on a web widget that can be embedded on 3rd party websites.
Since a lot of content management systems do not allow users to post/execute scripts, I want my widget to show an image instead of JS-generated content if such situation occurs.
<script type="text/javascript">
(function(){var s = document.createElement('script');s.src = '//example.com/file.js';s.async = "async";document.body.appendChild(s);}());
</script>
<img src="//example.com/image.svg?param1=value1" src="" id="my_fallback">
For now I am using the code above. Is there any way to show the image only if the script did not load? The goal is to reduce transfer usage and provide better user experience.
The first line of my widget script is removing #my_fallback, but it is not fast enough - sometimes I can see the image for a second before the actual widget content replaces it.
The only thing I came up with is to delay creation of the image by including something like sleep() in the beginning of my image generator.
EDIT
No, <noscript> won't work here. I do not want to fallback if user has disabled javascript. I want to fallback when a script has not loaded - for any reason, especially if some security mechanism cut off the <script> section.
Use html tag Noscript
<noscript>Your browser does not support JavaScript! or a image here</noscript>
Remember
In HTML 4.01, the tag can only be used inside the element.
In HTML5, the tag can be used both inside and .
Edit : -
add one html tag
<span class="noscript">script is loading.....or put image</span>
inside your script tag
now in your scripts which has to be load add one code like
add this line at the end
$('.noscript').hide();
This is the other way which you can handle the same!
One quick fix is to create a global variable from that script, visible to the window object.Also the image must be hidden. Then, on a main.js script check for that variable. If it exists then run your widget code from there. If it doesnt exist then fadeIn the fallback image.
Heres a demo
The default img is an image 272x178 size and the widget image is an image 300x400 size.
To simulate the action when the script is unavailable, just name the variable myWidgetIsEnabled with a different name so the condition fails.
Here is some code:
// Code goes here
var widget = (function(){
window.myWidgetIsEnabled = true;
return {
init: function(){
var s = document.createElement('script');s.src = 'file.js';s.async = "async";
document.body.appendChild(s);}
}
}());
$(document).ready(function(){
if(window.myWidgetIsEnabled){
widget.init();
}else{
console.log('not enabled, the default behavior');
$('.fallback').fadeIn();
}
})

Vimeo JavaScript API add event listener to all iframes on the page

Been messing around with Froogaloop, Vimeo's JavaScript API, and am trying to add a class to the Vimeo iFrame that is currently playing. Seems simple enough, using the API's events, but I can't seem to wrap my head around it. Here's what I've got so far:
The code below is a simplified version of their example. While it's not causing any errors in my console, I'm not getting any of the logs (and therefore not getting the classes). Am I missing something?
Thanks for your help!
var iframe = $('article.video iframe')[0],
player = $f(iframe);
player.addEvent('ready', function() {
player.addEvent('play', on);
player.addEvent('pause', off);
player.addEvent('finish', off);
});
function on(id) {
console.log('playing');
player.addClass('playing');
}
function off(id) {
console.log('not playing');
player.removeClass('playing');
}
UPDATE
The issue definitely has to do with the variables. Player tells Froogaloop which iframe to work with, iframe identifies which html object that is. So I suppose the issue is how I can identify all the iframes on the page and then feed Froogaloop the appropriate iframe when one is activated.
Yes, as I see, in the on() and off() functions Froogaloop is giving you the ID of the iframe by parameter. So, in that case you should add and remove class like this:
$('#'+id).addClass('playing');
$('#'+id).removeClass('playing');
And in your HTML you should provide an id="videoX" to every iframe tag, plus to add &player_id=videoX at the end of the url address of the src property from the iframe.

Stop a Vimeo Video with Jquery

I need to stop a Vimeo video embedded with new oembed api (universal player) but when I try to add an event I get this error:
Uncaught TypeError: Object #<an HTMLIFrameElement> has no method 'addEvent'
But I don't why I get this error, I added jquery and the frogaloop api, also I added ids to the iframes, but it still doesn't work: :(
The full code is here:
http://tv.bisaccia.info
Eli, please edit your post. As Joe said, you are partially misinformed. While postMessage is needed for cross-domain communication, it is implemented through a DOM method added by a call to "Froogaloop.init();"
is_embed_iframe = _this.iframe_pattern.test(cur_frame.getAttribute('src'));
if (is_embed_iframe) {
cur_frame.api = _that.api;
cur_frame.get = _that.get;
cur_frame.addEvent = _that.addEvent;
}
Note: you will need to grab froogaloop.js (or the min variant) from the Vimeo site.
Be sure the iFrame "src" is set prior to calling init(), otherwise froogaloop will do nothing.
As per Mike's suggestion, invoking:
Froogaloop.init();
Does make the control API work. In my case:
<iframe id="player_1" src="http://player.vimeo.com/video/26859570?js_api=1&js_swf_id=player_1&title=0&byline=0&portrait=0" width="620" height="354" frameborder="0"></iframe>
<script>
$(document).ready(function() {
Froogaloop.init();
$("#player_1").moogaloop({
load: function(element) {
$("#segment1").click(function() { element.moogaloop('seekTo', "7"); });
}
});
});
</script>
Weird... Moogaloop's author demo page does work without the init() call. Anyway, worked for me.
Thanks for your time!
This is not the correct answer, but may work for your situation as it did for mine. I simply wanted to stop my Vimeo from playing when I closed its containing DOM element. I was collapsing its container and that hid it visually but the audio continued to play and use browser resources unnecessarily.
What I do now is simply store the iframe in a variable, remove it from the DOM, then replace it immediately. I have NOT tested across browsers, only the latest version of Chrome and Safari Mobile.
var container = $("#VimeoContainer");
var iframe = container.find("iframe");
iframe.remove();
container.append(iframe);
Again, Froogaloop is really the way to go, however I've had issues with it in the past so for this situation I was looking for something simple. Obviously you could do this without JQuery with the same results.
You can't.
There's no DOM addEvent method.
You don't have cross-domain access to Vimeo, so you are not permitted to have JavaScript interface with the iframe's document or abstract view.
If you wanted to interface with Vimeo via JavaScript, you would have to get them to implement a postMessage API that also accepts your domain.

Categories