API iframe YouTube: как мне управлять плеером iframe, который уже находится в HTML?
Я хочу иметь возможность контролировать плееры YouTube на основе iframe. Эти игроки уже будут в HTML, но я хочу контролировать их через JavaScript API.
Я читал документацию по API iframe, в которой объясняется, как добавить новое видео на страницу с помощью API, а затем управлять им с помощью функций проигрывателя YouTube:
var player;
function onYouTubePlayerAPIReady() {
player = new YT.Player('container', {
height: '390',
width: '640',
videoId: 'u1zgFlCw8Aw',
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
}
Этот код создает новый объект player и назначает его "player", а затем вставляет его в div #container. Тогда я могу оперировать "игроком" и звонить playVideo()
, pauseVideo()
и т. д. на нем.
Но я хочу иметь возможность работать с игроками iframe, которые уже есть на странице.
Я мог бы сделать это очень легко с помощью старого метода встраивания, что-то вроде:
player = getElementById('whateverID');
player.playVideo();
Но это не работает с новыми фреймами. Как я могу назначить объект iframe уже на странице и затем использовать функции API на нем?
7 ответов
Скриптовые ссылки: Исходный код - Предварительный просмотр - Маленькая версия
Обновление: эта маленькая функция будет выполнять код только в одном направлении. Если вам нужна полная поддержка (например, прослушиватели / получатели событий), взгляните на Listening for Youtube Event в jQuery
В результате глубокого анализа кода я создал функцию: function callPlayer
запрашивает функциональный вызов для любого видео на YouTube. См. Ссылку на API YouTube, чтобы получить полный список возможных вызовов функций. Прочитайте комментарии в исходном коде для объяснения.
17 мая 2012 года размер кода был удвоен, чтобы позаботиться о состоянии готовности игрока. Если вам нужна компактная функция, которая не связана с состоянием готовности проигрывателя, см. http://jsfiddle.net/8R5y6/.
/**
* @author Rob W <gwnRob@gmail.com>
* @website https://stackru.com/a/7513356/938089
* @version 20131010
* @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 / 0 = 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 (!domReady ||
iframe && (!iframe.contentWindow || queue && !queue.ready) ||
(!queue || !queue.ready) && 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);
}
}
Использование:
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: это не работает!
A: "Не работает" не является четким описанием. Вы получаете какие-либо сообщения об ошибках? Пожалуйста, покажите соответствующий код.
Q: Я добавил видео на YouTube, используя <iframe src="http://www.youtube.com/embed/As2rZGPGKDY" />
но функция не выполняет никакой функции!
A: Вы должны добавить ?enablejsapi=1
в конце вашего URL: /embed/vid_id?enablejsapi=1
,
Q: Я получаю сообщение об ошибке "Указана неверная или недопустимая строка". Зачем?
A: API не работает должным образом на локальном хосте (file://
). Разместите свою (тестовую) страницу в Интернете или используйте JSFiddle. Примеры: см. Ссылки в верхней части этого ответа.
Q: Как вы узнали это?
A: Я потратил некоторое время на ручную интерпретацию источника API. Я пришел к выводу, что я должен был использовать postMessage
метод. Чтобы узнать, какие аргументы передать, я создал расширение Chrome, которое перехватывает сообщения. Исходный код расширения можно скачать здесь.
Q: Какие браузеры поддерживаются?
A: Каждый браузер, который поддерживает JSON и postMessage
,
- IE 8+
- Firefox 3.6+ (на самом деле 3.5, но
document.readyState
был реализован в 3.6) - Опера 10.50+
- Safari 4+
- Chrome 3+
Связанный ответ / реализация: добавление видео в рамку с помощью jQuery
Полная поддержка API: прослушивание событий Youtube в jQuery
Официальный API: https://developers.google.com/youtube/iframe_api_reference
Лист регистраций изменений
- 17 мая 2012
РеализованonYouTubePlayerReady
:callPlayer('frame_id', function() { ... })
,
Функции автоматически ставятся в очередь, когда игрок еще не готов. - 24 июля 2012
Обновлен и успешно протестирован в поддерживаемых браузерах (смотри вперед). - 10 октября 2013 г. Когда функция передается в качестве аргумента,
callPlayer
заставляет проверить готовность. Это необходимо, потому что когдаcallPlayer
вызывается сразу после вставки iframe, когда документ готов, он не может точно знать, что iframe полностью готов. В Internet Explorer и Firefox этот сценарий привел к слишком раннему вызовуpostMessage
, который был проигнорирован. - 12 декабря 2013, рекомендуется добавить
&origin=*
в URL. - 2 марта 2014 года, отозвана рекомендация удалить
&origin=*
на URL.
Похоже, YouTube обновил свой JS API, поэтому он доступен по умолчанию! Вы можете использовать существующий идентификатор iframe на YouTube...
<iframe id="player" src="http://www.youtube.com/embed/M7lc1UVf-VE?enablejsapi=1&origin=http://example.com" frameborder="0"></iframe>
... в вашем JS...
var player;
function onYouTubeIframeAPIReady() {
player = new YT.Player('player', {
events: {
'onStateChange': onPlayerStateChange
}
});
}
function onPlayerStateChange() {
//...
}
... и конструктор будет использовать ваш существующий iframe вместо его замены новым. Это также означает, что вам не нужно указывать videoId для конструктора.
Смотрите Загрузка видеоплеера
Вы можете сделать это с гораздо меньшим количеством кода:
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 || []
}), '*');
}
}
}
Рабочий пример: http://jsfiddle.net/kmturley/g6P5H/296/
Моя собственная версия приведенного выше кода Ким Т, которая сочетается с некоторыми jQuery и позволяет нацеливать определенные фреймы.
$(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 || []
} ), '*');
}
}
Одно быстрое решение, если запросы не являются проблемой , и вы хотите, чтобы такое поведение было похоже на показ / скрытие видео, - это удалить / добавить iframe или очистить и заполнить
src
.
const stopPlayerHack = (iframe) => {
let src = iframe.getAttribute('src');
iframe.setAttribute('src', '');
iframe.setAttribute('src', src);
}
Если iframe будет удален, воспроизведение прекратится, и он будет загружен сразу после этого. В моем случае я улучшил код, чтобы онлайн снова установить src в открытом лайтбоксе, поэтому загрузка будет происходить только в том случае, если пользователь потребует просмотреть видео.
У меня были проблемы с приведенными выше примерами, поэтому вместо этого я просто вставил iframe при нажатии с помощью JS с автовоспроизведением в источнике, и он отлично работает для меня. У меня также была возможность использовать Vimeo или YouTube, поэтому мне нужно было справиться с этим.
Это решение не удивительно, и его можно было бы очистить, но это сработало для меня. Мне также не нравится jQuery, но проект уже использовал его, и я просто реорганизовывал существующий код, не стесняйтесь очищать или конвертировать в ванильный JS :)
<!-- HTML -->
<div class="iframe" data-player="viemo" data-src="$PageComponentVideo.VideoId"></div>
<!-- jQuery -->
$(".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(
'<iframe width="' +
width +
'" height="' +
height +
'" src="https://player.vimeo.com/video/' +
vcode +
'?color=ff9933&portrait=0&autoplay=1" frameborder="0" allowfullscreen wmode="Opaque"></iframe>'
);
} else {
iframe.html(
'<iframe width="' +
width +
'" height="' +
height +
'" src="https://www.youtube.com/embed/' +
vcode +
'?autoplay=1&loop=1&rel=0&wmode=transparent" frameborder="0" allowfullscreen wmode="Opaque"></iframe>'
);
}
}
Спасибо, Роб В., за ответ.
Я использовал это в приложении Cordova, чтобы избежать загрузки API и чтобы я мог легко управлять динамически загружаемыми фреймами.
Я всегда хотел иметь возможность извлекать информацию из iframe, такую как состояние (getPlayerState) и время (getCurrentTime).
Роб В. помог осветить, как работает API с помощью postMessage, но, конечно, он отправляет информацию только в одном направлении, с нашей веб-страницы в iframe. Доступ к геттерам требует от нас прослушивания сообщений, отправленных нам из iframe.
Мне потребовалось некоторое время, чтобы выяснить, как настроить ответ Роба В., чтобы активировать и прослушивать сообщения, возвращаемые iframe. Я в основном просматривал исходный код в iframe YouTube, пока не нашел код, отвечающий за отправку и получение сообщений.
Ключевым моментом было изменение "события" на "прослушивание", что в основном давало доступ ко всем методам, которые были разработаны для возврата значений.
Ниже представлено мое решение, обратите внимание, что я переключился на "прослушивание" только тогда, когда запрашиваются геттеры, вы можете настроить условие, чтобы включить дополнительные методы.
Обратите внимание, что вы можете просмотреть все сообщения, отправленные из iframe, добавив console.log (e) в window.onmessage. Вы заметите, что после активации прослушивания вы будете получать постоянные обновления, которые включают текущее время видео. Вызов геттеров, таких как getPlayerState, активирует эти постоянные обновления, но отправит сообщение, содержащее состояние видео, только при изменении состояния.
function callPlayer(iframe, func, args) {
iframe=document.getElementById(iframe);
var event = "command";
if(func.indexOf('get')>-1){
event = "listening";
}
if ( iframe&&iframe.src.indexOf('youtube.com/embed') !== -1) {
iframe.contentWindow.postMessage( JSON.stringify({
'event': event,
'func': func,
'args': args || []
}), '*');
}
}
window.onmessage = function(e){
var data = JSON.parse(e.data);
data = data.info;
if(data.currentTime){
console.log("The current time is "+data.currentTime);
}
if(data.playerState){
console.log("The player state is "+data.playerState);
}
}