Подтвердить что ты не робот

Загрузка нескольких видеороликов YouTube iFrame API

У меня проблема со следующим кодом, но я не уверен, где проблема.

На моей главной странице, в javascript, у меня есть массив, определяемый для хранения списка объектов YouTubePlayer, которые я создаю. Я также загружаю API YouTube.

У меня также есть (иногда несколько) пользовательский элемент YouTube, содержащий div YouTube в модуле bootstrap. Он также содержит JavaScript для нажатия нового объекта YouTubePlayer на массив на главной странице js. Наконец, в пользовательском элементе управления я определяю методы автоматического запуска и остановки видео в "показанных" и "скрытых" событиях модальности bootstrap.

Наконец, чтобы (надеюсь) решить условие гонки между загружаемым документом и загружаемым API YouTube, я установил две переменные bool (один для документа, один для API) и проверил оба значения true перед вызовом функции initVideos который выполняет итерацию через массив объектов YouTubePlayer и инициализирует их, устанавливая объект YT.Player в окне. Я думаю, что часть проблемы состоит в том, что я не могу статически устанавливать window.player1 и т.д., Потому что я никогда не знаю, сколько пользовательских элементов YouTube будет загружено.

Проблема заключается в том, что каждый раз, когда запускаются модальные события bootstrap, объект YT.Player, который я извлекаю из окна, не содержит методов для playVideo() и pauseVideo().

На моей главной странице:

$(document).ready(function () {
    window.docReady = true;

    if (window.apiReady)
        initVideos();
});

function onYouTubeIframeAPIReady() {
    window.apiReady = true;

    if (window.docReady)
        initVideos();

    initVideos();
}

function initVideos() {

    if (typeof ytPlayerList === 'undefined')
        return;

    for (var i = 0; i < ytPlayerList.length; i++) {
        var player = ytPlayerList[i];
        var pl = new YT.Player(player.DivId, {
            playerVars: {
                'autoplay': '0',
                'controls': '1',
                'autohide': '2',
                'modestbranding': '1',
                'playsinline': '1',
                'rel': '0',
                'wmode': 'opaque'
            },
            videoId: player.VideoId,
            events: {
                'onStateChange': player.StateChangeHandler
            }
        });

        window[player.Id] = pl;
    }
}

И в элементе управления пользователя:

window.ytPlayerList.push({
    Id: "<%=ClientID%>player",
    DivId: "<%=ClientID%>player",
    VideoId: "<%=VideoId%>",
    StateChangeHandler: hide<%=ClientID%>Player
});

function hide<%=ClientID %>Player(state) {
    if (state.data == '0') {
        hide<%=ClientID %>Video();
    }
}

function show<%=ClientID %>Video() {
    $('#<%=ClientID %>video-modal').modal('show');
}

function hide<%=ClientID %>Video() {
    $('#<%=ClientID %>video-modal').modal('hide');
}

$(document).ready(function() {
    $("#<%=ClientID%>Video").click(function() {
        show<%=ClientID%>Video();
    });

    $("#<%=ClientID %>video-modal").on('shown', function () {
        window["<%=ClientID%>player"].playVideo();
    });

    $("#<%=ClientID %>video-modal").on('hide', function () {
        window["<%=ClientID%>player"].pauseVideo();
    });
});

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

Uncaught TypeError: window.ctl100_phContent_ctl100player.playVideo is not a function
4b9b3361

Ответ 1

Таким образом, проблема заключается в том, что видео только получает этот метод playVideo(), когда YouTube API завершил загрузку видеоэлемента. Этот видеоэлемент загружается async, поэтому выполнение кода будет продолжено с кодом $(document).ready(function() { jQuery, где он попытается присоединить функции playVideo(), которые в этот момент времени еще не существуют.

Я воспроизвел эту ошибку на этой странице HTML/JS:

<!doctype html>
<html class="no-js" lang="">
<head>
    <meta charset="utf-8">
    <meta http-equiv="x-ua-compatible" content="ie=edge">
    <title></title>
</head>
<body>
    <div id="ctl100_phContent_ctl100player" style="border:1px solid red;"></div>
    <div id="ctl100_phContent_ctl101player" style="border:1px solid red;"></div>
    <div id="ctl100_phContent_ctl102player" style="border:1px solid red;"></div>

    <script src="https://www.youtube.com/iframe_api"></script>
    <script>window.jQuery || document.write('<script src="js/vendor/jquery-1.6.2.min.js"><\/script>')</script>
    <script>
        window.ytPlayerList = [];
        window.players = [];

        window.ytPlayerList.push({ Id: 'ctl100_phContent_ctl100player', DivId: 'ctl100_phContent_ctl100player', VideoId: 'IaHxPi9dM7o', StateChangeHandler: 'hide_player_1' });
        window.ytPlayerList.push({ Id: 'ctl100_phContent_ctl101player', DivId: 'ctl100_phContent_ctl101player', VideoId: 'IaHxPi9dM7o', StateChangeHandler: 'hide_player_2' });
        window.ytPlayerList.push({ Id: 'ctl100_phContent_ctl102player', DivId: 'ctl100_phContent_ctl102player', VideoId: 'IaHxPi9dM7o', StateChangeHandler: 'hide_player_3' });

        function onYouTubeIframeAPIReady() {
            initVideos();
        }

        function initVideos() {
            for (var i = 0; i < ytPlayerList.length; i++) {
                var player = ytPlayerList[i];
                var pl = new YT.Player(player.DivId, {
                    height: '390',
                    width: '640',
                    videoId: player.VideoId,
                });
                window[player.Id] = pl;
            }
        }

        window.ctl100_phContent_ctl100player.playVideo();
    </script>
</body>
</html>

Это дает мне ту же ошибку, которую вы описываете. Поэтому, чтобы проверить мою теорию (и убедиться, что не было других ошибок script), я сделал это:

setTimeout(function() {
        window.ctl100_phContent_ctl100player.playVideo() 
}, 1000);

Это сработало. Так и в самом деле это проблема. Однако - мы не знаем точно, что 1000 мс достаточно, чтобы гарантировать, что игрок будет инициализирован. Итак, что вы могли бы сделать - если вы хотите не рефакторировать слишком много - начинает прослушивать события onready игрока:

    function initVideos() {
        for (var i = 0; i < ytPlayerList.length; i++) {
            var player = ytPlayerList[i];
            var pl = new YT.Player(player.DivId, {
                height: '390',
                width: '640',
                videoId: player.VideoId,
                events: {
                     'onReady': window[player.Id + '_ready']
                }
            });
            window[player.Id] = pl;
        }
    }

Предполагается, что вы использовали когенерацию для создания таких функций, как:

    function ctl100_phContent_ctl100player_ready() {
        // Hook up the hide() / onShow() and other behaviors here
        // ... to prove that playVideo() is a function here, I'm calling it
        window.ctl100_phContent_ctl100player.playVideo();
    }
    function ctl100_phContent_ctl101player_ready() {
        window.ctl100_phContent_ctl101player.playVideo();
    }
    function ctl100_phContent_ctl102player_ready() {
        window.ctl100_phContent_ctl102player.playVideo();
    }

Итак, это не решение для копирования-вставки для вас, но должно дать вам представление о том, почему код не работает. Есть, вероятно, более элегантные способы достижения того, что вы пытаетесь сделать, но давайте просто сохранить исправление на данный момент.

Сообщите мне, если это сработало, или если у вас есть другие вопросы.