157 votes

API iframe YouTube: comment contrôler un lecteur iframe déjà présent dans le HTML?

Je veux pouvoir contrôler les lecteurs YouTube basés sur des iframes. Ces lecteurs seront déjà présents dans le HTML, mais je veux les contrôler via l'API JavaScript.

J'ai lu la documentation pour 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 lecteur et l'assigne à 'player', puis l'insère à l'intérieur de la div #container. Ensuite, je peux opérer sur 'player' et appeler playVideo(), pauseVideo(), etc. sur celui-ci.

Mais je veux être en mesure d'opérer sur les lecteurs iframe qui sont déjà 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 puis-je assigner un objet iframe déjà présent sur la page et ensuite utiliser les fonctions de l'API sur celui-ci ?

0 votes

J'ai écrit une abstraction pour travailler avec l'API YouTube IFrame github.com/gajus/playtube

325voto

Rob W Points 125904

Liens de violon : Code source - Prévisualisation - Petite version
Mise à jour : Cette petite fonction n'exécutera le code que dans une seule direction. Si vous voulez un support complet (par exemple, les écouteurs d'événements / getters), jetez un coup d'oeil à à l'adresse Écoute de l'événement Youtube en jQuery)

À la suite d'une analyse approfondie du code, j'ai créé une fonction : function callPlayer demande un appel de fonction sur n'importe quelle vidéo YouTube encadrée. Voir le Référence à l'Api YouTube pour obtenir une liste complète des appels de fonctions possibles. Lisez les commentaires du code source pour une explication.

Le 17 mai 2012, la taille du code a été doublée afin de prendre en charge l'état prêt du joueur. Si vous avez besoin d'une fonction compacte qui ne s'occupe pas de l'état prêt du lecteur, voir http://jsfiddle.net/8R5y6/ .

/**
 * @author       Rob W <gwnRob@gmail.com>
 * @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);
    }
}

Utilisation :

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");

Questions (et réponses) possibles :

Q : Ça ne marche pas !
A : "Ne fonctionne pas" n'est pas une description claire. Obtenez-vous des messages d'erreur ? Veuillez montrer le code correspondant.

Q : playVideo ne lit pas la vidéo.
A : La lecture nécessite une interaction avec l'utilisateur, et la présence de allow="autoplay" sur l'iframe. Voir https://developers.google.com/web/updates/2017/09/autoplay-policy-changes y https://developer.mozilla.org/en-US/docs/Web/Media/Autoplay_guide

Q : J'ai intégré une vidéo YouTube en utilisant <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 : Je reçois le message d'erreur "Une chaîne de caractères 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 la fonction JSFiddle . Exemples : Voir les liens en haut de cette réponse.

Q : Comment savez-vous cela ?
A : J'ai passé un certain temps à interpréter manuellement la source de l'API. J'en ai conclu que je devais utiliser le postMessage méthode. 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é aquí .

Q : Quels sont les navigateurs pris en charge ?
A : Chaque navigateur qui supporte JSON y postMessage .

  • IE 8+ (EN ANGLAIS)
  • Firefox 3.6+ (en fait 3.5, mais document.readyState a été mis en œuvre dans la version 3.6)
  • Opera 10.50 et plus
  • Safari 4+
  • Chrome 3+.

Réponse / mise en œuvre connexe : Fondu enchaîné d'une vidéo encadrée à l'aide de jQuery
Support complet de l'API : Écoute de l'événement Youtube en jQuery)
API officielle : https://developers.google.com/youtube/iframe_api_reference

Historique des révisions

  • 17 mai 2012
    Mis en œuvre onYouTubePlayerReady : callPlayer('frame_id', function() { ... }) .
    Les fonctions sont automatiquement mises en attente lorsque le joueur n'est pas encore prêt.
  • 24 juillet 2012
    Mis à jour et testé avec succès dans les navigateurs supportés (à venir).
  • 10 octobre 2013 Lorsqu'une fonction est passée comme argument, callPlayer force une vérification de l'état de préparation. Ceci 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 se traduit par une invocation trop précoce de la fonction postMessage qui a été ignorée.
  • 12 déc. 2013, recommandé d'ajouter &origin=* dans l'URL.
  • 2 mars 2014, recommandation rétractée de supprimer &origin=* à l'URL.
  • 9 avril 2019, correction d'un bug qui entraînait une récursion infinie lorsque YouTube se charge avant que la page ne soit prête. Ajout d'une note sur la lecture automatique.

0 votes

@RobW J'ai effectivement essayé cela. Il semble que le JSON en erreur ne soit pas celui de votre script, mais à l'intérieur de l'iframe comme partie de l'API de YouTube.

0 votes

@RobW merci pour ce bel extrait de code. Avez-vous trouvé des moyens d'utiliser l'événement Message plutôt que d'utiliser l'API JS de youtube pour ajouter un écouteur d'événements ?

0 votes

@brillout.com La méthode PostMessage est basée sur l'API JS YT (?enablejsapi=1). Sans activer l'API JS, la méthode postMessage ne fera rien, consultez la réponse liée pour une implémentation facile des écouteurs d'événements. J'ai également créé, mais pas publié, un code lisible pour communiquer avec le cadre. J'ai décidé de ne pas le publier, car son effet est similaire à celui de l'API du cadre YouTube par défaut.

36voto

CletusW Points 1561

Il semble que YouTube a mis à jour leur API JS, donc c'est disponible par défaut! Vous pouvez utiliser l'identifiant d'une iframe YouTube existante...

...dans votre JS...

var player;
function onYouTubeIframeAPIReady() {
  player = new YT.Player('player', {
    events: {
      'onStateChange': onPlayerStateChange
    }
  });
}

function onPlayerStateChange() {
  //...
}

...et le constructeur utilisera votre iframe existante au lieu de la remplacer par une nouvelle. Cela signifie également que vous n'avez pas à spécifier l'identifiant de la vidéo au constructeur.

Voir Charger un lecteur vidéo

1 votes

@raven tu manques le paramètre autoplay=1 dans l'URL. Dans ton exemple d'URL, ce serait youtube.com/embed/M7lc1UVf-VE?enablejsapi=1& autoplay=1 &origin=example.com

0 votes

@alengel il ne veut pas utiliser le paramètre autoplay-url. Au lieu de cela, il essaie de démarrer la vidéo en utilisant la fonction autoplay des API js. Mais pour une raison quelconque, la fonction onYouTubeIframeAPIReady() n'est pas invoquée.

0 votes

@raven J'ai trouvé. 1) Supprimez le &origin=example.com de l'URL de l'iframe. 2) Dans la section "Cadres et Extensions" de votre jsfiddle, réglez le deuxième menu déroulant sur "Pas d'enveloppe dans - ". 3) Ajoutez l'API d'iframe Youtube en tant que ressource externe (youtube.com/iframe_api); J'ai forké votre fiddle et appliqué ces changements: jsfiddle.net/e97famd1/1

20voto

Kim T Points 434

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/

0 votes

J'ai vraiment aimé cette méthode, je l'ai juste adaptée pour fonctionner avec une directive Angular afin de ne pas avoir besoin de la boucle et de passer la fonction en fonction d'une fonction de basculement avec la portée (en gros, si la vidéo est affichée -> autoplay; sinon -> mettre en pause la vidéo). Merci!

0 votes

Vous devriez partager la directive, cela pourrait être utile!

1 votes

Voici une adaptation du code pour un Stylo : codepen.io/anon/pen/qERdza J'espère que cela vous aidera ! Cela permet également de basculer en appuyant sur la touche ESC lorsque la vidéo est en cours.

5voto

adamj Points 470

Ma propre version du code de Kim T ci-dessus qui combine avec un peu de 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 || []
        } ), '*');
    }
}

0 votes

Comment savoir que youtube est en train de jouer? Y a-t-il un rappel de l'iframe youtube, auquel l'extérieur peut se abonner?

0 votes

@Hammer Consultez la section Événements de l'API YouTube spécifiquement le OnStateChange : developers.google.com/youtube/iframe_api_reference#Events

0 votes

@admj, Pouvez-vous vérifier cela? Un comportement étrange ... stackoverflow.com/questions/38389802/…

1voto

Brady Edgar Points 148

Je rencontrais des problèmes avec les exemples ci-dessus, donc à la place, j'ai simplement inséré l'iframe au clic avec JS avec l'autoplay dans la source et cela fonctionne bien pour moi. J'avais aussi la possibilité pour Vimeo ou YouTube donc je devais pouvoir gérer cela.

Cette solution n'est pas incroyable et pourrait être nettoyée mais cela a fonctionné pour moi. Je n'aime pas non plus jQuery mais le projet l'utilisait déjà et je refacturais simplement du code existant, n'hésitez pas à le nettoyer ou à le convertir en JS natif :)

$(".btnVideoPlay").on("click", function (e) {
        var iframe = $(this).parents(".video-play").siblings(".iframe");
        iframe.show();

        if (iframe.data("player") === "youtube") {
            autoPlayVideo(iframe, iframe.data("src"), "100%", "100%");
        } else {
            autoPlayVideo(iframe, iframe.data("src"), "100%", "100%", true);
        }
    });

    function autoPlayVideo(iframe, vcode, width, height, isVimeo) {
        if (isVimeo) {
            iframe.html(
                ''
            );
        } else {
            iframe.html(
                ''
            );
        }
    }

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X