Embedded YouTube Playlist with vertical thumbnail images

html
jquery
css

#1

Hello Everyone,

I need some help adapting this YouTube Playlist code. I have searched and researched for hours in order to find the solution, but I am stumped.

Here is my page that has implemented the code: Meditation Media Page

As you can see. I have successfully embedded the player into my page, however, I cannot get the playlist thumbnails to appear vertically to the right of the video. I have gone over all of the JQuery and JavaScript and with my limited knowledge, I cannot understand what I am doing wrong.

Below is “most” of the code I believe contains the problem, however feel free to examine the page (above) as well. I am sure it is just one or two lines of code that is causing the error.

Please help!

Also, I welcome any comments in regard to code that needs to be removed or updated. I’ve made some adjustments, I just need it to function before I adjust it any further.

Thank you

<html>
<head>
 <meta charset="utf-8" />
<style>


#wrapper {
  width: 100%;
  background: #FFF;
  //color: #000;
  overflow: hidden;
  max-width: 100%;
  box-shadow: 0px 8px 13px rgba(0,0,0,0.5);
  margin-bottom: 1em;
}

#thumbs {
  overflow-y: scroll;
  overflow-x: hidden;
  width: 20%;
  background: #000;
  margin: 0;
  padding: 0;
  height: 0;
}

#thumbs li {
  list-style: none;
  margin: 0;
  position: relative;
  font-size: 0;
}

#thumbs li img{
  width: 100%;
}

#thumbs li p {
  font-family: arial;
  font-size: 10px;
  background: rgba(0, 0, 0, 0.7);
  display: none;
  position: absolute;
  width: 100%;
  height: 100%;
  color: #fff;
  padding: 5%;
  margin: 0;
}

#thumbs li:hover p {
  display: block;
  cursor: pointer;
}

#thumbs .ypt-now-playing p {
  display: block;
}

#thumbs .ypt-now-playing > span::after {
  content: "\25b6  Now playing"; 
  margin-top: -1em;
  display: block;
  width: 100%;
  padding: 5%;
  background: rgba(0, 0, 0, 0.6);
  color: #fff;
  position: absolute;
  bottom: 0;
  font-size: 10px;
}

#wrapper .video {
  position: relative;
  width: 80%;
  /*height: 0;*/
  padding-bottom: 48.4%;
  float: left;
}

#wrapper .video iframe {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  float:left;
}

@media only screen and (max-width : 400px) {
  #thumbs {
    display: none;
  }
  #wrapper .video {
    width: 100%;
    padding-bottom: 56.25%;
  }
}


</style>
  
   <title>Media Retreats </title>

  </head>
  
  <body>
  
  <div class="header">
  <h1>Writing Retreats</h1>
  <p>Personal Time With Your Creativity.</p>
  
  	<ul>
      <li><a href="index.html">Home</a></li>
      <li><a href="about.html">About</a></li>
      <li><a class="active" href="media.html">Media</a></li>
  	</ul>

    <div class="row">
    <div class="leftcollg">
        <div class="card">
        <h2>Media</h2>
        <h3> Personal Creativity Enrichment</h3>
                           
	<div id="wrapper">
     <div class="video">
      <div id="player" data-pl="PL91DBAD278E550B1E"></div>
    </div>
    <ul id="thumbs"></ul>
</div>
    </div>
  </div>
   
    </div>
 <script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.1.3.min.js"></script>
<script >// Load Youtube IFrame Player API code asynchronously. 
var tag = document.createElement('script'); //Add a script tag
tag.src = "https://www.youtube.com/iframe_api"; //Set the SRC to get the API
var firstScriptTag = document.getElementsByTagName('script')[0]; //Find the first script tag in the html
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); //Put this script tag before the first one

var player; //The Youtube API player
var player = document.getElementById('player');
var playlistID = player.getAttribute('data-pl');
var thumbs = document.getElementById('thumbs');
var nowPlaying = "ypt-now-playing"; //For marking the current thumb
var nowPlayingClass = "." + nowPlaying;
var index = 0; //Playlists begin at the first video by default

function getPlaylistData(playlistID, video_list, page_token) {//Makes a single request to Youtube Data API
  var apiKey = '[API Inserted Here]';
  var theUrl =
  'https://www.googleapis.com/youtube/v3/playlistItems?part=snippet' +
  '&maxResults=' + 50 + //Can be anything from 1-50
  '&playlistId=' + playlistID +
  '&key=' + apiKey;

  if (page_token) {theUrl += '&pageToken=' + page_token;} //If there is page token, start there
  var xmlHttp = null;
  xmlHttp = new XMLHttpRequest();
  xmlHttp.open("GET", theUrl, true);
  xmlHttp.send(null);
  xmlHttp.onload = function (e) {//when the request comes back
    buildJSON(xmlHttp.responseText, video_list, playlistID); //send the data to buildJSON
  };
}

function buildJSON(response, list, playlistID) {//Takes the text response and adds it to any existing JSON data
  var results = JSON.parse(response); //Parse it
  if (!list) {list = [];} //If there is no list to add to, make one
  list.push.apply(list, results.items); //Add JSON data to the list
  if (results.nextPageToken) {//If the results included a page token
    getPlaylistData(playlistID, list, results.nextPageToken); //Create another data API request including the current list and page token
  } else {//If there is not a next-page token
    buildHTML(list); //Send the JSON data to buildHTML
  }
}

function buildHTML(data) {//Turns JSON data into HTML elements
  var list_data = ''; //A string container
  for (i = 0; i < data.length; i++) {if (window.CP.shouldStopExecution(0)) break; //Do this to each item in the JSON list
    var item = data[i].snippet; //Each Youtube playlist item snippet
    if (!item.thumbnails) {continue;} //private videos do no reveal thumbs, so skip them
    list_data += '<li data-ypt-index="' + i + '"><p>' + item.title + '</p><span><img alt="' + item.title + '" src="' + item.thumbnails.medium.url + '"/></span></li>'; //create an element and add it to the list
  }window.CP.exitedLoop(0);
  thumbs.innerHTML = list_data; //After the for loop, insert that list of links into the html
}

function yptThumbHeight() {
  thumbs.style.height = document.getElementById('player').clientHeight + 'px'; //change the height of the thumb list
  //breaks if player.clientHeight + 'px';
}

function onPlayerReady(event) {//Once the player is ready...
  yptThumbHeight(); //Set the thumb containter height
}

getPlaylistData(playlistID);

//Once the Youtube Iframe API is ready...
window.onYouTubeIframeAPIReady = function () {// Creates an <iframe> (and YouTube player) after the API code downloads. must be globally available
  player = new YT.Player('player', {
    height: '360',
    width: '640',
    playerVars:
    {
      listType: 'playlist',
      list: playlistID },

    events: {
      'onReady': onPlayerReady,
      'onStateChange': onPlayerStateChange } });



  // When the player does something...
  function onPlayerStateChange(event) {

    //Let's check on what video is playing
    var currentIndex = player.getPlaylistIndex();
    var the_thumbs = thumbs.getElementsByTagName('li');
    var currentThumb = the_thumbs[currentIndex];

    if (event.data == YT.PlayerState.PLAYING) {//A video is playing

      for (var i = 0; i < the_thumbs.length; i++) {if (window.CP.shouldStopExecution(1)) break; //Loop through the thumbs
        the_thumbs[i].className = ""; //Remove nowplaying from each thumb
      }window.CP.exitedLoop(1);

      currentThumb.className = nowPlaying; //this will also erase any other class belonging to the li
      //need to do a match looking for now playing
    }

    //if a video has finished, and the current index is the last video, and that thumb already has the nowplaying class
    if (event.data == YT.PlayerState.ENDED && currentIndex == the_thumbs.length - 1 && the_thumbs[currentIndex].className == nowPlaying) {
      jQuery.event.trigger('playlistEnd'); //Trigger a global event
    }
  } //function onPlayerStateChange(event)

  //When the user changes the window size...
  window.addEventListener('resize', function (event) {
    yptThumbHeight(); //change the height of the thumblist
  });

  //When the user clicks an element with a playlist index...
  jQuery(document).on('click', '[data-ypt-index]:not(".ypt-now-playing")', function (e) {//click on a thumb that is not currently playing
    index = Number(jQuery(this).attr('data-ypt-index')); //Get the index of the clicked item
    if (navigator.userAgent.match(/(iPad|iPhone|iPod)/g)) {//if IOS
      player.cuePlaylist({ //cue is required for IOS 7
        listType: 'playlist',
        list: playlistID,
        index: index,
        suggestedQuality: 'hd720' //quality is required for cue to work, for now
        // https://code.google.com/p/gdata-issues/issues/detail?id=5411
      }); //player.cuePlaylist
    } else {//yay it's not IOS!
      player.playVideoAt(index); //Play the new video, does not work for IOS 7
    }
    jQuery(nowPlayingClass).removeClass(nowPlaying); //Remove "now playing" from the thumb that is no longer playing
    //When the new video starts playing, its thumb will get the now playing class
  }); 
};

</script>
</body></html>```

#2

Hi @MWms, if you open the console of your browser dev tools you see an

Uncaught TypeError: Cannot read property ‘shouldStopExecution’ of undefined

and if you remove the line causing that problem

Uncaught TypeError: Cannot read property ‘exitedLoop’ of undefined

If you remove that line too it works… I’m not sure what window.CP is supposed to be, but apparently you forgot to include that library.

PS: A quick search suggests that this is codepen specific API – so you copy/pasting code from a pen using that API won’t work on your page.

PPS: Infinite loop detection inside a regular for loop seems rather unnecessary anyway, so I guess you can indeed safely remove those lines.


#3

First let me say thank you for your response.
Second, I am happy to report that it works…finally.

I only received one error (I have no idea what it means).


 www-widgetapi.js:100 Failed to execute 'postMessage' on 'DOMWindow': The target origin provided ('https://www.youtube.com') does not match the recipient window's origin ('null').

Second, yes copying and pasting is NEVER a good idea! My original quest was to find some online “instructions” on how to construct this YouTube player with scrolling playlist without a plugin. When that failed, I attempted to write/adapt code…using what I know and understand (which is limited at best).

So, I have to ask: Do I really need all of this code to accomplish this task? (It seems a bit much).