admin管理员组

文章数量:1287583

I'm having an issue with the following code, but I'm not sure where the problem is.

On my master page, in javascript, I have an array defined to hold a list of YouTubePlayer objects that I create. I also load the YouTube API here.

I also have (sometime several) YouTube user control(s) that contains the YouTube div in a bootstrap modal. It also contains a JavaScript for pushing a new YouTubePlayer object to the array in the master page js. Lastly, on the user control, I define methods for auto-starting and stopping the video on the 'shown' and 'hide' events of the bootstrap modal.

Finally, to (hopefully) solve the race condition between the document being loaded and the YouTube API being loaded, I set two bool variables (one for document, one for API), and check both for true before calling an initVideos function which iterates through the array of YouTubePlayer objects and initializes them, setting the YT.Player object in the window. Part of the issue, I think, is that I can't statically set window.player1 etc., because I never know how many YouTube user controls will be loaded.

The problem is whenever the bootstrap modal events fire, the YT.Player object I retrieve from the window doesn't contain the methods for playVideo() and pauseVideo().

On my master page:

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

And on the user control:

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

This may be a lack of js expertise, but I'm absolutely stuck. Any help would be greatly appreciated. Also, for reference, the exception I get is

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

I'm having an issue with the following code, but I'm not sure where the problem is.

On my master page, in javascript, I have an array defined to hold a list of YouTubePlayer objects that I create. I also load the YouTube API here.

I also have (sometime several) YouTube user control(s) that contains the YouTube div in a bootstrap modal. It also contains a JavaScript for pushing a new YouTubePlayer object to the array in the master page js. Lastly, on the user control, I define methods for auto-starting and stopping the video on the 'shown' and 'hide' events of the bootstrap modal.

Finally, to (hopefully) solve the race condition between the document being loaded and the YouTube API being loaded, I set two bool variables (one for document, one for API), and check both for true before calling an initVideos function which iterates through the array of YouTubePlayer objects and initializes them, setting the YT.Player object in the window. Part of the issue, I think, is that I can't statically set window.player1 etc., because I never know how many YouTube user controls will be loaded.

The problem is whenever the bootstrap modal events fire, the YT.Player object I retrieve from the window doesn't contain the methods for playVideo() and pauseVideo().

On my master page:

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

And on the user control:

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

This may be a lack of js expertise, but I'm absolutely stuck. Any help would be greatly appreciated. Also, for reference, the exception I get is

Uncaught TypeError: window.ctl100_phContent_ctl100player.playVideo is not a function
Share Improve this question edited Apr 28, 2015 at 12:10 Praxis Ashelin 5,2272 gold badges21 silver badges46 bronze badges asked Apr 17, 2015 at 21:05 JackJack 9502 gold badges17 silver badges36 bronze badges 3
  • Is it intentional that you are firing initVideos() twice in onYouTubeIframeAPIReady() function? – Praxis Ashelin Commented Apr 28, 2015 at 12:14
  • possible duplicate of .playVideo() - not a function? or playVideo doesn't work – Praxis Ashelin Commented Apr 28, 2015 at 12:28
  • Could you paste some of the generated HTML please ? That'll make it more easy for me to reproduce the issue. – Jochen van Wylick Commented Apr 29, 2015 at 16:24
Add a ment  | 

1 Answer 1

Reset to default 9 +25

So the problem is that the video only gets that playVideo() method when the YouTube API has finished loading the video element. This video element is loaded async, so the code execution will continue with the $(document).ready(function() { jQuery code where it will try to attach the playVideo() functions which at that point in time - do not yet exist.

I've reproduced this error in that HTML/JS page:

<!doctype html>
<html class="no-js" lang="">
<head>
    <meta charset="utf-8">
    <meta http-equiv="x-ua-patible" 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./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>

This gives me the same error you describe. So in order to test my theory ( and make sure there were no other script errors ) I did this:

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

That did work. So indeed this seems to the problem. However - we don't know for sure that 1000ms is enough to guarantee that the player is initialized. So what you could do - if you want to not refactor too much - is start listening for the onready events of the player:

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

Which assumes you used codegeneration to create functions like:

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

So this is not a copy-paste solution for you problem but should give you insight into why the code is not working. There are probably more elegant ways to achieve what you're trying to do, but let's just keep the fix simple for now.

Let me know if it worked or if you have any other questions.

本文标签: javascriptLoading Multiple YouTube Videos iFrame APIStack Overflow