加载多个 YouTube 视频 iFrame API

Loading Multiple YouTube Videos iFrame API

我遇到了以下代码的问题,但我不确定问题出在哪里。

在我的主页上,在 javascript 中,我定义了一个数组来保存我创建的 YouTubePlayer 个对象的列表。我也在此处加载 YouTube API。

我也有(有时是几个)YouTube 用户控件,其中包含 bootstrap 模态中的 YouTube div。它还包含一个 JavaScript 用于将新的 YouTubePlayer 对象推送到母版页 js 中的数组。最后,在用户控件上,我定义了在 bootstrap 模式的 'shown' 和 'hide' 事件上自动启动和停止视频的方法。

最后,为了(希望)解决正在加载的文档和正在加载的 YouTube API 之间的竞争条件,我设置了两个 bool 变量(一个用于文档,一个用于 API),并在调用 initVideos 函数之前检查两者是否为真,该函数遍历 YouTubePlayer 对象数组并初始化它们,在 window 中设置 YT.Player 对象。我认为部分问题是我无法静态设置 window.player1 等,因为我永远不知道将加载多少 YouTube 用户控件。

问题是每当 bootstrap 模式事件触发时,我从 window 检索到的 YT.Player 对象不包含 [=20] 的方法=] 和 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

所以问题在于视频仅在 YouTube API 加载完视频元素后才获取 playVideo() 方法。此视频元素是异步加载的,因此代码执行将继续执行 $(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>

这给了我你所描述的同样的错误。所以为了检验我的理论(并确保没有其他脚本错误)我这样做了:

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();
    }

所以这不是针对您的问题的复制粘贴解决方案,但应该能让您深入了解为什么代码不起作用。可能有更优雅的方法来实现您想要做的事情,但我们现在只让修复保持简单。

让我知道它是否有效,或者如果您有任何其他问题。