Je souhaite pouvoir contrôler les lecteurs YouTube basés sur l'iframe. Ce lecteur sera déjà dans le code HTML, mais je souhaite le contrôler via l’API JavaScript.
J'ai lu documentation relative à l'API iframe qui explique comment ajouter une nouvelle vidéo à la page avec l'API, puis la contrôler avec les fonctions du lecteur YouTube:
var player;
function onYouTubePlayerAPIReady() {
player = new YT.Player('container', {
height: '390',
width: '640',
videoId: 'u1zgFlCw8Aw',
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
}
Ce code crée un nouvel objet joueur et l'assigne à "joueur", puis l'insère dans le conteneur #container. Ensuite, je peux utiliser le lecteur et appeler playVideo()
, pauseVideo()
, etc.
Mais je veux pouvoir utiliser les lecteurs iframe déjà présents sur la page.
Je pouvais le faire très facilement avec l'ancienne méthode d'intégration, avec quelque chose comme:
player = getElementById('whateverID');
player.playVideo();
Mais cela ne fonctionne pas avec les nouveaux iframes. Comment attribuer un objet iframe déjà sur la page, puis utiliser les fonctions de l'API dessus?
Liens de violon: Code source - Aperç - Petite version
Mise à jour: Cette petite fonction n'exécutera le code que dans un seul sens. Si vous souhaitez une assistance complète (par exemple, écouteurs/getters d'événements), consultez sous Événement d'écoute Youtube dans jQuery
À la suite d’une analyse approfondie du code, j’ai créé une fonction: function callPlayer
demande un appel de fonction pour n’importe quelle vidéo YouTube encadrée. Consultez le Référence YouTube Api pour obtenir la liste complète des appels de fonction possibles. Lisez les commentaires sur le code source pour une explication.
Le 17 mai 2012, la taille du code a été doublée afin de prendre en compte l'état de préparation du joueur. Si vous avez besoin d’une fonction compacte qui ne traite pas de l’état de disponibilité du lecteur, voir http://jsfiddle.net/8R5y6/ .
/**
* @author Rob W <[email protected]>
* @website https://stackoverflow.com/a/7513356/938089
* @version 20190409
* @description Executes function on a framed YouTube video (see website link)
* For a full list of possible functions, see:
* https://developers.google.com/youtube/js_api_reference
* @param String frame_id The id of (the div containing) the frame
* @param String func Desired function to call, eg. "playVideo"
* (Function) Function to call when the player is ready.
* @param Array args (optional) List of arguments to pass to function func*/
function callPlayer(frame_id, func, args) {
if (window.jQuery && frame_id instanceof jQuery) frame_id = frame_id.get(0).id;
var iframe = document.getElementById(frame_id);
if (iframe && iframe.tagName.toUpperCase() != 'IFRAME') {
iframe = iframe.getElementsByTagName('iframe')[0];
}
// When the player is not ready yet, add the event to a queue
// Each frame_id is associated with an own queue.
// Each queue has three possible states:
// undefined = uninitialised / array = queue / .ready=true = ready
if (!callPlayer.queue) callPlayer.queue = {};
var queue = callPlayer.queue[frame_id],
domReady = document.readyState == 'complete';
if (domReady && !iframe) {
// DOM is ready and iframe does not exist. Log a message
window.console && console.log('callPlayer: Frame not found; id=' + frame_id);
if (queue) clearInterval(queue.poller);
} else if (func === 'listening') {
// Sending the "listener" message to the frame, to request status updates
if (iframe && iframe.contentWindow) {
func = '{"event":"listening","id":' + JSON.stringify(''+frame_id) + '}';
iframe.contentWindow.postMessage(func, '*');
}
} else if ((!queue || !queue.ready) && (
!domReady ||
iframe && !iframe.contentWindow ||
typeof func === 'function')) {
if (!queue) queue = callPlayer.queue[frame_id] = [];
queue.Push([func, args]);
if (!('poller' in queue)) {
// keep polling until the document and frame is ready
queue.poller = setInterval(function() {
callPlayer(frame_id, 'listening');
}, 250);
// Add a global "message" event listener, to catch status updates:
messageEvent(1, function runOnceReady(e) {
if (!iframe) {
iframe = document.getElementById(frame_id);
if (!iframe) return;
if (iframe.tagName.toUpperCase() != 'IFRAME') {
iframe = iframe.getElementsByTagName('iframe')[0];
if (!iframe) return;
}
}
if (e.source === iframe.contentWindow) {
// Assume that the player is ready if we receive a
// message from the iframe
clearInterval(queue.poller);
queue.ready = true;
messageEvent(0, runOnceReady);
// .. and release the queue:
while (tmp = queue.shift()) {
callPlayer(frame_id, tmp[0], tmp[1]);
}
}
}, false);
}
} else if (iframe && iframe.contentWindow) {
// When a function is supplied, just call it (like "onYouTubePlayerReady")
if (func.call) return func();
// Frame exists, send message
iframe.contentWindow.postMessage(JSON.stringify({
"event": "command",
"func": func,
"args": args || [],
"id": frame_id
}), "*");
}
/* IE8 does not support addEventListener... */
function messageEvent(add, listener) {
var w3 = add ? window.addEventListener : window.removeEventListener;
w3 ?
w3('message', listener, !1)
:
(add ? window.attachEvent : window.detachEvent)('onmessage', listener);
}
}
Usage:
callPlayer("whateverID", function() {
// This function runs once the player is ready ("onYouTubePlayerReady")
callPlayer("whateverID", "playVideo");
});
// When the player is not ready yet, the function will be queued.
// When the iframe cannot be found, a message is logged in the console.
callPlayer("whateverID", "playVideo");
Q: Cela ne fonctionne pas!
A: "Ne fonctionne pas" n'est pas une description claire. Avez-vous des messages d'erreur? S'il vous plaît montrer le code approprié.
Q: playVideo
ne lit pas la vidéo.
A: la lecture nécessite une interaction de l'utilisateur, ainsi que la présence de allow="autoplay"
sur l'iframe. Voir https://developers.google.com/web/updates/2017/09/autoplay-policy-changes et https://developer.mozilla.org/en-US/ docs/Web/Media/Autoplay_guide
Q: J'ai intégré une vidéo YouTube à l'aide de <iframe src="http://www.youtube.com/embed/As2rZGPGKDY" />
mais la fonction n'exécute aucune fonction!
A: Vous devez ajouter ?enablejsapi=1
à la fin de votre URL: /embed/vid_id?enablejsapi=1
.
Q: le message d'erreur suivant s'affiche "Une chaîne non valide ou illégale a été spécifiée". Pourquoi?
A: l'API ne fonctionne pas correctement sur un hôte local (file://
). Hébergez votre page (de test) en ligne ou utilisez JSFiddle . Exemples: Voir les liens en haut de cette réponse.
Q: Comment avez-vous su cela?
A: J'ai passé un certain temps à interpréter manuellement le code source de l'API. J'ai conclu que je devais utiliser la méthode postMessage
. Pour savoir quels arguments passer, j'ai créé une extension Chrome qui intercepte les messages. Le code source de l'extension peut être téléchargé ici .
Q: Quels navigateurs sont supportés?
A: Tous les navigateurs prenant en charge JSON et postMessage
.
document.readyState
a été implémenté dans 3.6)Réponse/implémentation associée: Ouverture en fondu d'une vidéo avec cadre à l'aide de jQuery
Prise en charge complète de l'API: Écoute de l'événement Youtube dans jQuery
API officielle: https://developers.google.com/youtube/iframe_api_reference
onYouTubePlayerReady
: callPlayer('frame_id', function() { ... })
.callPlayer
force une vérification de l'état de préparation. Cela est nécessaire car, lorsque callPlayer
est appelé juste après l'insertion de l'iframe alors que le document est prêt, il ne peut pas savoir avec certitude que l'iframe est entièrement prêt. Dans Internet Explorer et Firefox, ce scénario aboutissait à un appel trop précoce de postMessage
, qui était ignoré.&Origin=*
dans l'URL.&Origin=*
à l'URL.On dirait que YouTube a mis à jour leur API JS, donc celle-ci est disponible par défaut! Vous pouvez utiliser un identifiant iframe YouTube existant ...
<iframe id="player" src="http://www.youtube.com/embed/M7lc1UVf-VE?enablejsapi=1&Origin=http://example.com" frameborder="0"></iframe>
... dans votre JS ...
var player;
function onYouTubeIframeAPIReady() {
player = new YT.Player('player', {
events: {
'onStateChange': onPlayerStateChange
}
});
}
function onPlayerStateChange() {
//...
}
... et le constructeur utilisera votre iframe existant au lieu de le remplacer par un nouveau. Cela signifie également que vous n'avez pas à spécifier le videoId au constructeur.
Vous pouvez le faire avec beaucoup moins de code:
function callPlayer(func, args) {
var i = 0,
iframes = document.getElementsByTagName('iframe'),
src = '';
for (i = 0; i < iframes.length; i += 1) {
src = iframes[i].getAttribute('src');
if (src && src.indexOf('youtube.com/embed') !== -1) {
iframes[i].contentWindow.postMessage(JSON.stringify({
'event': 'command',
'func': func,
'args': args || []
}), '*');
}
}
}
Exemple de travail: http://jsfiddle.net/kmturley/g6P5H/296/
Ma propre version du code de Kim T ci-dessus, qui se combine avec du jQuery et permet de cibler des iframes spécifiques.
$(function() {
callPlayer($('#iframe')[0], 'unMute');
});
function callPlayer(iframe, func, args) {
if ( iframe.src.indexOf('youtube.com/embed') !== -1) {
iframe.contentWindow.postMessage( JSON.stringify({
'event': 'command',
'func': func,
'args': args || []
} ), '*');
}
}