Oh yes, that code has a lot of JSLint problems that need to be fixed up.
All of the issues that I found just seem related to formatting. It’s all fixed up now. https://jsfiddle.net/h7jgfsv8/1/
Oh yes, that code has a lot of JSLint problems that need to be fixed up.
All of the issues that I found just seem related to formatting. It’s all fixed up now. https://jsfiddle.net/h7jgfsv8/1/
Just this one is left I think.
player.getCurrentTime is not a function
That’s happening because your player is undefined.
How is it fixed?
In the code at https://jsfiddle.net/h7jgfsv8/1/ the videoPlayer has a reference to the player that is kept at the top of the function.
const videoPlayer = (function makeVideoPlayer() {
...
let player = null;
...
player = new YT.Player(video, playerOptions);
There is also a getPlayer() method that lets code outside of videoPlayer gain access to the player.
function getPlayer() {
return player;
}
...
return {
...
getPlayer,
...
};
That is how setInterval gains access to the player
setInterval(function () {
const player = videoPlayer.getPlayer();
...
This error message can’t be removed?
player.getCurrentTime is not a function"
Oh yes, it takes some time for the player to be available.
We could pass that setInterval code as a callback function to the videoPlayer code, so that only after the player is ready does the setInterval get started.
function monitorPlayback() {
setInterval(function checkTime() {
const player = videoPlayer.getPlayer();
const currentTime = player.getCurrentTime();
if (currentTime > 10) {
player.nextVideo();
}
}, 250);
}
videoPlayer.init([
"0dgNc5S8cLI",
"mnfmQe8Mv1g",
"CHahce95B1g",
"2VwsvrPFr9w"
], monitorPlayback);
We can then put that callback function (monitorPlayback at this stage), aside in the config, so that it can be retrieved later on.
function init(videos, callback) {
config.playlist = videos.join();
config.callback = callback;
Then later on when the player is ready, we can call that callback function, and even pass the player to it.
function onPlayerReady(event) {
player = event.target;
player.setVolume(100); // percent
shufflePlaylist(player);
config.callback(player);
}
We can then replace that getPlayer() command with using player as a function parameter instead.
function monitorPlayback(player) {
setInterval(function checkTime() {
const currentTime = player.getCurrentTime();
if (currentTime > 10) {
player.nextVideo();
}
}, 250);
}
We then don’t need that getPlayer function, so it can be removed again.
// function getPlayer() {
// return player;
// }
...
return {
addPlayer,
// getPlayer,
init,
I got this error:
Error: player.playVideo is not a function"
I can’t say it happens often.
I don’t seem to get that error. You will need to supply detailed instructions on what I must do so that I can see that same error too.
I agree.
Let’s fix that issue on here first:
https://jsitor.com/SEWXsfQOf7
I’ve noticed this quite frequently but never thought of it as anything.
Uncaught TypeError: player.playVideo is not a function at line 72 col 12
Keep tapping run then play until you see the error.
That happens because it takes time for the youtube player to load and be ready.
Do you agree that we should disable clicking the play button, until the player is loaded and ready?
It was never added to a single player before, you can try adding it to it.
There’s a lot to be done here because we are returning some fundamentals back to the code, such as init methods.
To help ensure that timing works right, we’ll use a videoPlayer init method to load the scripts and setup the onYouTubeIframeAPIReady handler.
function loadIframeScript() {
const tag = document.createElement("script");
tag.src = "https://www.youtube.com/iframe_api";
const firstScriptTag = document.getElementsByTagName("script")[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
}
function onYouTubeIframeAPIReady() {
const cover = document.querySelector(".jacket");
const wrapper = cover.parentElement;
const frameContainer = wrapper.querySelector(".video");
videoPlayer.addPlayer(frameContainer);
}
...
function init() {
loadIframeScript();
window.onYouTubeIframeAPIReady = onYouTubeIframeAPIReady;
}
return {
addPlayer,
init,
play
};
...
videoPlayer.init();
The manageCover code at the top needs an init method, so that it can be delayed until after the player is ready.
const manageCover = (function makeManageCover() {
const events = {};
...
function coverClickHandler(evt) {
const cover = evt.currentTarget;
...
cover.dispatchEvent(events.afterClickCover);
}
function init(callback) {
const cover = document.querySelector(".jacket");
cover.addEventListener("click", coverClickHandler);
events.afterClickCover = new Event("afterClickCover");
cover.addEventListener("afterClickCover", callback);
}
return {
init
};
}());
In fact, that init method can accept a callback function, so that we can run that callback function when initialising the code.
(function manageCover() {
...
function coverClickHandler(evt) {
...
callback(cover);
}
...
function init(callback) {
const cover = document.querySelector(".jacket");
cover.addEventListener("click", coverClickHandler);
}
...
videoPlayer.init();
manageCover.init(function playVideo() {
videoPlayer.play();
});
And, the videoPlayer function can take an eventHandlers object, so that we can delay that manageCover code until after the player is ready.
const videoPlayer = (function makeVideoPlayer() {
const events = {};
const eventHandlers = {};
...
function addEvents(handlers) {
eventHandlers.afterPlayerReady = handlers.afterPlayerReady;
events.afterPlayerReady = new Event("afterPlayerReady");
}
...
function init(initEventHandlers) {
addEvents(initEventHandlers);
Then we just need to add that afterPlayerReady event to an element, such as the iframe, and dispatch the event when after the onPlayerReady function.
function onPlayerReady(event) {
...
const iframe = player.h;
iframe.dispatchEvent("afterPlayerReady");
}
...
player = new YT.Player(video, config);
const iframe = player.h;
const eventHandler = eventHandlers.afterPlayerReady;
iframe.addEventListener("afterPlayerReady", eventHandler);
The code at https://jsitor.com/SEWXsfQOf7 has been updated, and clicking on the cover now doesn’t trigger it until after the player is ready to go.
Thank you for that simple example where the code is easily movable.
I agree with you now, Math.random()
should not be used where there is a much better alternative Playlist shuffle.
Playlist shuffle does the same thing, and uses way less code, and it is able to use setInterval which I did not know could be done.
Math here, where it picks a single video and plays only one, that’s really the only thing it is useful for.
const videoID = ["0dgNc5S8cLI", "2VwsvrPFr9w"];
const index = Math.floor(Math.random() * videoID.length);
function addPlayer(videos) {
const config = {
height: 360,
host: "https://www.youtube-nocookie.com",
videoId: videoID[index],
The array that plays only one video, it wouldn’t be able to work in this, would it?
function onYouTubeIframeAPIReady() {
...
const videos = [
"0dgNc5S8cLI",
"mnfmQe8Mv1g",
"CHahce95B1g",
"2VwsvrPFr9w"
];
videoPlayer.addPlayer(frameContainer, videos.join());
}
You have a habit of removing code that isn’t used in a certain circumstance, but is still used in the wider picture by other things you are are actively working on. That kind of chaos is not conducive to much progress.
What would be helpful here is if the addPlayer function was smart enough to handle both a single video id, as well as an array of them.
Carrying on with the code from https://jsitor.com/SEWXsfQOf7 we do not use join() when passing the list to addPlayer. Using join() there was a bad choice. It should be done inside of addPlayer so that it’s only the videos (or single video) being given to addPlayer.
We can do that by checking if the videoIds are an array or not. If they aren’t an array then we want the value to be given to videoId. If they are an array though then we want them joined together and given to playlist.
function addPlayer(video, videoIds) {
const videoId = !Array.isArray(videoIds) && videoIds;
const playlist = Array.isArray(videoIds) && videoIds.join();
const playerOptions = {
...
videoId,
...
};
playerOptions.playerVars = {
...
playlist: playlist || undefined,
...
};
That’s how easy it is to ensure that addPlayer can handle both a single video, or a list of videos.
I’ve put both a single video and multiple videos in the example code:
const video = "mnfmQe8Mv1g";
const videos = [
"0dgNc5S8cLI",
"mnfmQe8Mv1g",
"-Xgi_way56U",
"CHahce95B1g"
];
videoPlayer.addPlayer(frameContainer, videos);
That way you can use it as is shown above for multiple videos, or as follows for a single video.
videoPlayer.addPlayer(frameContainer, video);
The code has been updated at https://jsitor.com/SEWXsfQOf7