When I create an instance of YT.Player(HTMLIFrameElement, { options })
I get back an object that has:
But not playVideo
, pauseVideo
etc as described in the documentation.
I got a demo here: http://siesta-annotations.surge.sh/Siesta_webviewer_test/?page=3
I am creating the iframes via the DOM in trait.playable.youtube.js
and adding the iframe to a documentFragment
that eventually will be added to a div:
const element = document.createElement('iframe')
element.src = `https://www.youtube.com/embed/${id}?rel=0;&autoplay=${this.options.autostart ? 1 : 0};&enablejsapi=1;&origin=${location.hostname};`
element.style = this.inlineStyle
I then create an instance of YT.Player:
// nasty initialization because we're outside webpack and this is a demo
if (global.YT.loaded) {
this.player = new global.YT.Player(element, {
events: {
'onStateChange': this.stateHandler,
'onReady': onPlayerReady
}
})
} else {
const oldHandler = global.onYouTubeIframeAPIReady
global.onYouTubeIframeAPIReady = () => {
if (oldHandler) oldHandler()
this.player = new global.YT.Player(element, {
events: {
'onStateChange': this.stateHandler,
'onReady': onPlayerReady
}
})
}
}
The instance of YT.Player looks something like this:
It looks like the minification process went very wrong. What am I doing wrong - how should I initialise YT.Player for my use-case?
Strangely enough you do not get access to the YTPlayer API until 'onReady'
is called in the constructor player options object. https://developers.google.com/youtube/iframe_api_reference#Loading_a_Video_Player
function onYouTubeIframeAPIReady() {
new YT.Player('player', {
height: '390',
width: '640',
videoId: 'M7lc1UVf-VE',
events: {
'onReady': function (event) {
onPlayerReady(event.target)
},
'onStateChange': onPlayerStateChange
}
});
}
function onPlayerReady(player) {
player.playVideo();
}
This of course only works for one player, so you need to roll out your own YouTube player manager if you want to handle multiple players. (I know the API is insane)
A quick solution could be the following if you have iframes with youtube videos (untested):
const YTManager = {
videos: [],
youtubeApiReady = false,
initialize () {
const matchId = /[^/]+$/
this.youtubeApiReady = true
document.querySelectorAll('iframe[src*="youtube"]')
.forEach(iframe => this.videos.push({
dom: iframe,
player: null,
videoId: iframe.src.exec(matchId) || iframe.src.exec(matchId)[0]
}))
},
createPlayers () {
if (this.youtubeApiReady === false) return
this.videos.forEach(v => {
new YT.Player(v.dom.id, {
videoId: v.videoId,
events: {
'onReady': function (event) {
v.player = event.target
}
}
})
})
},
find (id) {
return this.videos.find(v => v.id === id)
},
play (id) {
const video = this.find(id)
if (this.youtubeApiReady && video && video.player) {
video.playVideo()
}
},
pause (id) {
const video = this.find(id)
if (this.youtubeApiReady && video && video.player) {
video.pauseVideo()
}
},
}
window.onYouTubeIframeAPIReady = function onYouTubeIframeAPIReady() {
// YouTube client side script has been loaded
YTManager.youtubeApiReady = true
YTManager.createPlayers()
}
document.addEventListener("DOMContentLoaded", function() {
// all iframes has been parsed by the browser
YTManager.initialize()
});
Of course you can not call YTManager.play
or YTManager.pause
without having the ID and wait for the YouTube script to load and then the video initialization to finish. All in all, a real mess. I'm sure you can come up with a better manager. I have written one at work that is better but also works with completely different objects and requirements, so it's not a good fit for general purpose YouTube video manager. But the one above is the gist of my current player.
I recommend using iframes from the beginning - if anything goes wrong in your script, the videos will still be playable, you will just not have any control of them.
Iframed youtube videos looks like this:
<iframe
id="uniqueDOMElementID1"
width="560"
height="315"
src="https://www.youtube.com/embed/bHQqvYy5KYo"
frameborder="0"
allow="autoplay; encrypted-media"
allowfullscreen></iframe>