Cannot read property 'apply' of undefined - Youtube Api - javascript

I created the following code to make videos play on hover:
var player = [];
var el_number;
function onYouTubePlayerAPIReady() {
var players = $(".video");
for (var i = 0; i < players.length; i++) {
el_number = $(players[i]).parent().parent().index();
player[i] = new YT.Player(players[i], {
events: {
'onReady': onPlayerReady(i, el_number)
}
});
}
}
function onPlayerReady(number, elnumber) {
$(".article:eq("+elnumber+")").on({
'mouseover': function() {
player[number].playVideo();
},
'mouseout': function() {
player[number].pauseVideo();
}
});
}
<script src="https://www.youtube.com/iframe_api"></script>
<!-- Videos look like this: -->
<iframe class='video' src='https://www.youtube.com/embed/(video_id)?controls=0&showinfo=0&disablekb=1&fs=0&iv_load_policy=3&&enablejsapi=1' frameborder='0' allowfullscreen></iframe>
It works perfectly fine, but after some time of loading scripts it shows me this error in the console:
Uncaught TypeError: Cannot read property 'apply' of undefined www-widgetapi.js:65
at K.g.I (www-widgetapi.js:65)
at W.g.l (www-widgetapi.js:114)
at W.g.J (www-widgetapi.js:127)
at S.g (www-widgetapi.js:143)
at k (www-widgetapi.js:95)
I ckecked Opera and Edge there is no error. After disabling every extention there is still that problem in Chrome. What is the root of the problem here?

I think it's an issue with your callback definition :
onPlayerReady(i, el_number)
onPlayerReady takes the event data as parameter. To get the player, you can call event.target and store some data inside this object
For instance :
var player = [];
function onYouTubePlayerAPIReady() {
var players = $(".video");
for (var i = 0; i < players.length; i++) {
el_number = $(players[i]).parent().parent().index();
player[i] = new YT.Player(players[i], {
events: {
'onReady': onPlayerReady
}
});
player[i].el_number = el_number;
}
}
function onPlayerReady(event) {
console.log("event.target.el_number : " + event.target.el_number);
$(".article:eq(" + event.target.el_number + ")").on({
'mouseover': function() {
event.target.playVideo();
},
'mouseout': function() {
event.target.pauseVideo();
}
});
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://www.youtube.com/iframe_api"></script>
<!-- Videos look like this: -->
<iframe class='video' src='https://www.youtube.com/embed/B_hLfhccYf0?controls=0&showinfo=0&disablekb=1&fs=0&iv_load_policy=3&enablejsapi=1' frameborder='0' allowfullscreen></iframe>
You can find a jsfiddle here

Related

Moving through Vimeo videos with Javascript

I would like to play through Vimeo videos in sequence with JavaScript with continuous playback and looping through items. The following code moves to the second video at the end of the first but the same functions are not called at the end of the second video. Only the end of the first video shows Called! in the Javascript console, not the end of the second.
The Vimeo Player SDK has player.getVideoId() but not player.setVideoId() so I used a hack to load the next video: setting the source of the iframe and starting the Vimeo player anew. I wonder if this is the problem and the listener for the end of the video is attached to the discarded player.
Another issue with this code is that it exits fullscreen to load the next video.
I attempted this CodePen that sets the Vimeo Player on any element instead of an iframe and was unable to change the played video.
What is a cleaner or effective way of changing videos with the Vimeo Player SDK?
The HTML is:
<div class="vimeo">
<div class="resp-iframe-container container-centered">
<iframe class="resp-iframe" scrolling="no" frameborder="no" src="https://player.vimeo.com/video/238301525" allowfullscreen="" data-ready="true"></iframe></div></div>
And the javascript is:
var l = ["238301525", "496371201", "238301525", "496371201", "..."];
window.current = 0;
var increment = function() {
window.current += 1;
if (window.current == l.length) {
window.current = 0;
}
};
var decrement = function() {
window.current -= 1;
if (window.current < 0) {
window.current = l.length - 1;
}
};
prevA.addEventListener("click", function() {
decrement();
loadPlayer();
});
nextA.addEventListener("click", function() {
increment();
console.log("here!");
loadPlayer();
console.log("there!");
});
var loadPlayer = function() {
console.log("Called!");
var iframe = document.querySelector('iframe');
iframe.src = "https://player.vimeo.com/video/" + l[window.current];
var player = new Vimeo.Player(iframe);
player.autoplay = true;
var treatEvent = function(data, eventLabel) {
// Custom code...
if ("end" == eventLabel) {
console.log("incrementing automatically");
increment();
loadPlayer();
}
};
var onPlay = function(data) {
treatEvent(data, "play");
}
var onPause = function(data) {
treatEvent(data, "pause");
}
var onSeeked = function(data) {
treatEvent(data, "seeked");
}
var onEnd = function(data) {
treatEvent(data, "end");
}
player.on('play', onPlay);
player.on('pause', onPause);
player.on('seeked', onSeeked);
player.on('ended', onEnd);
setTimeout(function() {
//console.log("Trying to play...");
player.play().then(function() {
// the video was played
console.log("success: video played");
}).catch(function(error) {
console.log("Error! " + error.name);
});
}, 1000);
}
loadPlayer();
It seems that the Vimeo Player keeps track of whether it was initialized, adding a tag data-vimeo-initialized="true" in the HTML element.
One solution is to use the Vimeo Player SDK function .loadVideo():
var song_iframe = document.querySelector("iframe#song_video");
var song_player = new Vimeo.Player(song_iframe);
var on_song_end = function(data) {
// Logic to get the new id.
song_player.loadVideo(newId).then(function() {
song_player.play();
});
};
song_player.on("ended", on_song_end);

Index of object and play next with youtube api

I am using the youtube api to search for youtube videos. The videos will then be displayed on #searchBar with the video id ex. NJNlqeMM8Ns as data-video. I get the video id by pressing on a img:
<img data-video = "{{videoid}}" src = "bilder/play.png" alt = "play" class = "knapp" width = "40" height = "40">
Which in my poorly understanding of javascript becomes (this).
When I search for videos I will get more than one result which means that I will get more than one img tag.
In this case I want to play the next song when the first one is finished. I tried to get the index when I pressed on my img tag:
$(".knapp").click(function(){
var index = $(".knapp").index(this);
alert(index);
});
However, when I alerted the index after the video was finshed I always got the value 0 back.
So I thought I could do something like this:
function onPlayerStateChange(event) {
if (event.data == YT.PlayerState.ENDED){
playNext();
}
}
$('#searchBar').on('click', '[data-video]', function(){
player.current_video = $(this).attr('data-video');
playVideo();
});
function playVideo(){
var video_id = player.current_video;
player.loadVideoById(video_id, 0, "large");
}
function playNext(){
var player.current_videon = **$(this + 1).attr('data-video');**
var next_id = player.current_videon;
player.loadVideoById(next_id, 0, "large");
}
But I'm not sure how to make it work, as you can see in the bold section, can I solve my problem like this or do I need another approach?
Edit:
With some research I found out that I need to set the value of the current video being played and also efter the video was done playing I add this number by 1.
However even if it did make the next video play, I was unable to chose which song I wanted anymore...
function onPlayerStateChange(event) {
if (event.data == YT.PlayerState.ENDED){
player.current_video++;
playVideo();
}
}
var player = document.querySelector('iframe');
function onYouTubeIframeAPIReady() {
player = new YT.Player('player', {
height: '390',
width: '640',
videoId: '40mSZPyqpag',
playerVars: {rel: 0},
events: {
'onStateChange': onPlayerStateChange
}
});
player.current_video = 0;
}
$('#searchBar').on('click', '[data-video]', function(){
player.current_video = $(this).index();
alert(player.current_video);
playVideo();
});
function playVideo(){
var video_id = $('[data-video]').eq(player.current_video).attr('data-video');
player.loadVideoById(video_id, 0, "large");
}
Here is a working PEN (click to RUN)
The solution is based on a global currentSongIndex index, without facilitating next()
var currentSongIndex = null;
$(".knapp").click(function () {
var index = $(this).attr('data-video');
playVideo(index);
});
function playVideo(index) {
console.log("Playing song INDEX: " + index);
currentSongIndex = index;
playNext();
}
function playNext() {
currentSongIndex++;
let nextSongIndex = $('img[data-video="' + currentSongIndex + '"]').attr('data-video');
console.log("Next song INDEX: " + nextSongIndex);
if(typeof nextSongIndex != "undefined") {
playVideo(nextSongIndex);
} else {
console.log('end of list... you can play from index 1 or whatever....');
}
}
I got a suggestion to get player.current_video by the closest li index, so I made an update to my data-video img tag.
<li class = liItem>
<img data-video = "{{videoid}}" src = "bilder/play.png" alt = "play" class = "knapp" width = "40" height = "40">
</li>
Then I changed the index on my click function in my edited example:
$('#searchBar').on('click', '[data-video]', function(){
player.current_video = $(this).closest('li').index();
playVideo();
});
With this new fuction I was able to chose and play the next song!
Shoutout to #cale_b for providing me with the .closest('li') suggestion.

How to loop a video 3 times only?

I want to loop a video three times only. Rendering it in a for loop doesn't seem to work properly.
I am wondering how to do this with an HTML video.
I have this HTML video.
<div id="video" class="Adform-video"></div>
And this JS
(function() {
Floating.setup({
clicktag: dhtml.getVar('clickTAG', 'http://example.com'),
target: dhtml.getVar('landingPageTarget', '_blank'),
video: {
sources: dhtml.getVar('videoSources'),
poster: dhtml.getAsset(3),
clicktag: dhtml.getVar('clickTAG')
}
});
Floating.init();
})();
var Floating = (function() {
var videoPlayer;
var banner = dhtml.byId('banner'),
closeButton = dhtml.byId('closeButton'),
video = dhtml.byId('video'),
clickArea = dhtml.byId('click-area'),
lib = Adform.RMB.lib;
function setup(settings) {
for (var prop in settings) {
if (_settings[prop] instanceof Object) {
for (var prop2 in settings[prop]) {
_settings[prop][prop2] = settings[prop][prop2];
}
} else {
_settings[prop] = settings[prop];
}
}
}
var _settings = {
clicktag: null,
target: null,
video: null
};
function init() {
createVideoPlayer();
}
closeButton.onclick = function (event) {
dhtml.external.close && dhtml.external.close();
};
clickArea.onclick = function() {
stopVideo();
window.open(_settings.clicktag, _settings.target);
};
function createVideoPlayer() {
var videoSettings = _settings.video;
videoPlayer = Adform.Component.VideoPlayer.create({
sources: videoSettings.sources,
clicktag: videoSettings.clicktag,
loop: videoSettings.loop,
muted: videoSettings.muted,
poster: videoSettings.poster,
theme: 'v2'
});
if (videoPlayer) {
videoPlayer.removeClass('adform-video-container');
videoPlayer.addClass('video-container');
videoPlayer.appendTo(video);
}
function landPoster() {
if(!lib.isWinPhone) {
videoPlayer.video.stop();
}
}
videoPlayer.poster.node().onclick = landPoster;
if (lib.isAndroid && lib.isFF) {
lib.addEvent(video, 'click', function(){}, false);
}
}
function stopVideo() {
if (videoPlayer.video.state === 'playing') videoPlayer.video.pause();
}
return {
setup: setup,
init: init
};
})();
The video will be used as an ad, and therefore I will only loop through it trice.
I have looked at these posts but they didn't seem to work:
Loop HTML5 video
Prop video loop
How can I do that?
Check out the standard HTML5 video element's onended event handler. Set up a simple JS event function with a integer counter and use the pause feature of video element when counter reaches 3. This link should help!
https://www.w3schools.com/TAGS/av_event_ended.asp
Also, I'm curious to know why exactly you want a video to loop only thrice...
Anyway, if the functionality is somewhat similar to a small animation(of a small video) which should be played 3 times, consider making a GIF animation with three hard-coded repetitions!
PROBLEM SOLVED
<!DOCTYPE html>
<html>
<body>
<video id="myVideo" width="320" height="176" autoplay controls>
<source src="mov_bbb.mp4" type="video/mp4">
<source src="mov_bbb.ogg" type="video/ogg">
Your browser does not support the audio element.
</video>
<script>
var aud = document.getElementById("myVideo");
var a=0;
aud.onended = function() {
a=a+1;
if(a!=3)
{
aud.play();
}
};
</script>
</body>
</html>

Start youtube video on hover/mouseover

I'm trying to get youtube videos to start on hover. It will pause (not stop) when the user hovers over another video...
I am stuck on the hover command. Can someone help me work it out please?
The page has 16 videos, this is the working code from the jsfiddle that contains 3 videos as an example.
http://jsfiddle.net/sebwhite/gpJN4/
VIDEO:
<iframe id="player" width="385" height="230" src="http://www.youtube.com/embed/erDxb4IkgjM?rel=0&wmode=Opaque&enablejsapi=1;showinfo=0;controls=0" frameborder="0" allowfullscreen></iframe>
JAVASCRIPT:
function onYouTubeIframeAPIReady() {
player = new YT.Player('player', {
events: {
'onStateChange': onPlayerStateChange
}
});
onYouTubeIframeAPIReady();
function onPlayerStateChange(event) {
if (event.data == YT.PlayerState.PLAYING) {
player1.pauseVideo();
player2.pauseVideo();
}
UPDATED FIDDLE
Try this:
var $$ = function(tagname) { return document.getElementsByTagName(tagname); }
function onYouTubeIframeAPIReady() {
var videos = $$('iframe'), // the iframes elements
players = [], // an array where we stock each videos youtube instances class
playingID = null; // stock the current playing video
for (var i = 0; i < videos.length; i++) // for each iframes
{
var currentIframeID = videos[i].id; // we get the iframe ID
players[currentIframeID] = new YT.Player(currentIframeID); // we stock in the array the instance
// note, the key of each array element will be the iframe ID
videos[i].onmouseover = function(e) { // assigning a callback for this event
if (playingID !== currentHoveredElement.id) {
players[playingID].stopVideo();
}
var currentHoveredElement = e.target;
if (playingID) // if a video is currently played
{
players[playingID].pauseVideo();
}
players[currentHoveredElement.id].playVideo();
playingID = currentHoveredElement.id;
};
}
}
onYouTubeIframeAPIReady();
Fiddle:
http://jsfiddle.net/gpJN4/3/

Using an array in Jquery

I'm not so experienced with Javascript and I have been struggling with this one pretty much all day.
I'm using Jquery to create and array of the ids of embedded youtube videos:
$(function() {
$('li').on("click",function(){
alert($(this).attr('data-pile'));
var pilename = $(this).attr('data-pile');
var videoIDs = [];
$("li[data-pile='"+pilename+"']").each(function(index){
videoIDs.push($(this).attr('id'));
});
$.each(videoIDs,function(){
});
});
});
And I need to use the array in this JS:
<script src="//www.youtube.com/iframe_api"></script>
<script>
/**
* Put your video IDs in this array
*/
var videoIDs = [
//my array of Ids here
];
var player, currentVideoId = 0;
function onYouTubeIframeAPIReady() {
player = new YT.Player('player', {
height: '350',
width: '425',
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
}
function onPlayerReady(event) {
event.target.loadVideoById(videoIDs[currentVideoId]);
}
function onPlayerStateChange(event) {
if (event.data == YT.PlayerState.ENDED) {
currentVideoId++;
if (currentVideoId < videoIDs.length) {
player.loadVideoById(videoIDs[currentVideoId]);
}
}
}
</script>
In each div where embedded videos are I'm applying an id with same id as video.
How should I make the two scripts work?
I'll really appreciate if someone can point me in the right direction.
You're declaring your videoIDs array twice, once in your click events and again in your second
script.
The one inside your click events is local to that function whereas the other one is global. Javascript has function scope, so that click event one gets discarded once that function ends.
If you remove the one inside your click events, I believe it should work. You should also remove the $.each... as I don't think it's going to help (you're trying to make a playlist, right?).
It should be noted that it's considered bad practice to pollute the global namespace by using global variables. If this is all the code you have on your page, it's probably not an issue.
Try doing it this way: add a custom listener after "click" event. Didn't check your array forming section, tested with a custom array, hope you won't have issues with it.
<script>
var tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
function onYouTubeIframeAPIReady() {
player = new YT.Player('player', {
height: '350',
width: '425',
});
}
$(function(){
$(document.body).on("click",".play", function(){
player.stopVideo();
var pilename = $(this).attr('data-pile');
var videoIDs = [];
$("li[data-pile='"+pilename+"']").each(function(index){
videoIDs.push($(this).attr('id'));
});
if(videoIDs.length > 0){
currentVideoId = 0;
player.loadVideoById(videoIDs[0]);
function onPlayerStateChange(event) {
if (event.data == YT.PlayerState.ENDED) {
currentVideoId++;
if (currentVideoId < videoIDs.length) {
player.loadVideoById(videoIDs[currentVideoId]);
}
}
}
player.addEventListener("onStateChange", onPlayerStateChange)
player.playVideo();
}
});
});
</script>

Categories

Resources