Привязка видео YouTube к элементу Div из отдельного файла JS

У меня есть проблема при встраивании видео YouTube в одностраничное мобильное приложение PhoneJS. В PhoneJS сценарии JS определены в другом файле. Итак, я определил HTML-div следующим образом:

<div id="player"></div>

Теперь в файле JS я сделал это:

function getVideo() {        
    var tag = document.createElement('script');
    tag.src = "https://www.youtube.com/iframe_api";
    var playerDiv = document.getElementById('player');
    var firstScriptTag = document.getElementsByTagName('script')[0];
    firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

    var player;
    function onYouTubeIframeAPIReady() {
        player = new YT.Player(playerDiv, {
            height: '250',
            width: '444',
            videoId: sIFYPQjYhv8               
        });
    }        
}

Когда я запускаю и просматриваю отладчик, выполняется звонок на Youtube, и ответ получен, но он не отображается в представлении.

Хорошо, так как я использую привязку KnockoutJS, я изменил div в представлении html следующим образом:

<iframe id="player" type="text/html" width="444" height="250" frameborder="0" data-bind="attr: { src: src }"></iframe>

А затем передайте идентификатор видео src следующим образом:

src: ko.observable('http://www.youtube.com/embed/' + sIFYPQjYhv8 + '?autoplay=1')

В этом случае, однако, в отладчике вызов даже не делается на Youtube. Ничего просто не происходит. На самом деле я предпочитаю использовать вызов API вместо второго подхода.

Любые предложения о том, как заставить первый подход работать? Я имею в виду использование вызова API?

РЕДАКТИРОВАТЬ Просто хочу отметить, что когда я добавляю код ниже в представление, видео транслируется в порядке.

<h1>Video</h1>
        <div id="player"></div>
        <script>
            var tag = document.createElement('script');
            tag.src = "https://www.youtube.com/iframe_api";
            var playerDiv = document.getElementById('player');
            var firstScriptTag = document.getElementsByTagName('script')[0];
            firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

            var player;
            function onYouTubeIframeAPIReady() {
                player = new YT.Player(playerDiv, {
                    height: '250',
                    width: '444',
                    videoId: 'sIFYPQjYhv8'
                });
            }
        </script>

1 ответ

Решение

Я думаю, что самый простой способ сделать это - использовать собственный обработчик привязки с флагом, установленным из обратного вызова onYouTubeIFrameAPIReady

Образец jsFiddle

ko.bindingHandlers['player'] = {
    init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
        // Check if global script and function is declared.
        if ( !document.getElementById('playerScript') ) {
            // Create script
            var tag = document.createElement('script');
            tag.src = "https://www.youtube.com/iframe_api";
            var playerDiv = document.getElementById('player');
            var firstScriptTag = document.getElementsByTagName('script')[0];
            firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

            // Create global function that their API calls back
            window.playerReady = ko.observable(false);
            window.onYouTubeIframeAPIReady = function() {
                window.playerReady(true);
            };
        }
    },
    update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {

        var value = valueAccessor(),
            id = value.id(),
            height = ko.unwrap(value.height) || '250',
            width = ko.unwrap(value.width) || '444'
        ;

        if ( !value.id()) {
            return;
        }

        if ( !window.playerReady() ) {
            // YT hasn't invoked global callback.  Subscribe to update
            var subscription;
            subscription = window.playerReady.subscribe( function(newValue) {
                 if ( newValue ) {
                     subscription.dispose();
                     // Just get this binding to fire again
                     value.id.notifySubscribers(value.id());
                 }
            });
        } else {

            var player = new YT.Player( element, {
                height: height,
                width: width,
                videoId: id 
            });
        }
    },
}

Теперь измените ваш игрок div на

<div data-bind="player: { id: id, height: height, width: width }"></div>

Наконец, связать

var vm = { 
    id: 'sIFYPQjYhv8',
    height: '250',
    width: '444'
};
ko.applyBindings( vm )

РЕДАКТИРОВАТЬ

Чтобы убрать зависимость от окна, поместите тег script, который добавляет новый элемент script, обратно, настройте, как показано ниже, измените их обратный вызов и используйте setTimeout вместо наблюдаемого "playerReady"

HTML-скрипт

var tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
tag.setAttribute('id', 'playerScript');
tag.setAttribute('data-ready', 'false');
...

function onYouTubeIframeAPIReady = function() {
    document.getElementById('playerScript').setAttribute('data-ready', 'true');
};

Связывание игрока

    update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {

        var value = valueAccessor(),
            id = value.id(),
            height = ko.unwrap(value.height) || '250',
            width = ko.unwrap(value.width) || '444',
            playerScript = document.getElementById('playerScript')
        ;

        if ( !value.id()) {
            return;
        }

        if ( !playerScript || playerScript.getAttribute('data-ready') !== 'true' ) ) {
            // YT hasn't invoked global callback.  
            setTimeout( function() {
                 value.id.notifySubscribers(value.id());
            }, 50);
        } else {

            var player = new YT.Player( element, {
                height: height,
                width: width,
                videoId: id 
            });
        }
    }
Другие вопросы по тегам