Jordan Lewallen Jordan Lewallen - 5 months ago 29
HTML Question

Reset Custom Progress Bar When New Song Is Loaded From YouTube Playlist

Quick question, I have generated a custom progress bar for an embedded YouTube video that is displayed at the bottom of the page. The only issue is that when the next song is loaded from the YouTube playlist, the bar doesn't reset and start tracking the next song. How do I fix this?

ALSO, how would I go about preventing a user from clicking on the embedded video to pause the video, but still allowing the user to click on the ads that pop up on the video if they're interested? Plug.dj somehow does this: https://plug.dj/the-chillout-room/

Here is a jsfiddle of my work so far: https://jsfiddle.net/qrsqa5zy/

HTML:

<!DOCTYPE html>
<html>
<head>
<title>Chat</title>
<link rel="stylesheet" href="main.css">
</head>

<body>

<div class="header-bar">
<div class="bar">

<img src="C:\Users\jlewa\Desktop\assets\affinity_fm_only_letters.png" class="top-logo" style="float: left;">

<ul class="standard-nav" style="float: left;">

<li>Home</li>
<li>Lyrics Hub</li>
<li>Affinity LIVE</li>
<li>Merchandise</li>

</ul>

</div>
<div class="dropshadow"></div>
</div>

<div class="container-middle-third">
<div class="youtube-video" style="float: left;">
<div class="DJ-text">Affinity FM DJ Room</div>
<div class="DJ-underline"></div>
<div id="player" style="width: 853px; height: 480px;"></div></div>
</div>

<div class="chat" style="float: left;">
<div class="Chat-text">Chat</div>
<div class="Chat-underline"></div>
<input type="text" class="chat-name" placeholder="Chat">
<div class="info-rect">Info</div>
<div class="chat-messages"></div>
<textarea placeholder="Join the conversation..."></textarea>
<div class="chat-status">Status: <span>Idle</span></div>
</div>
</div>

<div class="bottom-bar">

<div class="thumbnail" style="float: left">



</div>

<div class="title-bar" style="float: left;">

<div class="song-name">Finding Hope - Let Go (feat. Deverano)</div>
<div class="dj-playing">Affinity FM is playing</div>

<div class="progress-background">
<div id="progress-bar" class="progress-bar"></div>
</div>

</div>
<div class="subscribe" style="float: left;"></div>

</div>

<script src="http://127.0.0.1:8080/socket.io/socket.io.js"></script>

<script>
(function() {
var getNode = function(s) {
return document.querySelector(s);
},

// Get required nodes
status = getNode('.chat-status span'),
messages = getNode('.chat-messages'),
textarea = getNode('.chat textarea'),
chatName = getNode('.chat-name'),

statusDefault = status.textContent,

setStatus = function(s){
status.textContent = s;

if(s !== statusDefault){
var delay = setTimeout(function(){
setStatus(statusDefault);
clearInterval(delay);
}, 3000);
}
};

//try connection
try{
var socket = io.connect('http://127.0.0.1:8080');
} catch(e){
//Set status to warn user
}

if(socket !== undefined){

//Listen for output
socket.on('output', function(data){
if(data.length){
//Loop through results
for(var x = 0; x < data.length; x = x + 1){
var message = document.createElement('div');
message.setAttribute('class', 'chat-message');
message.textContent = ': ' + data[x].message;
var name=document.createElement('span');
name.setAttribute('class', 'userName');
name.textContent = data[x].name;

message.insertBefore(name, message.firstChild);

//Append
messages.appendChild(message);
messages.insertBefore(message, messages.firstChild);
}
}
});

//Listen for a status
socket.on('status', function(data){
setStatus((typeof data === 'object') ? data.message : data);

if(data.clear === true){
textarea.value = '';
}
});

//Listen for keydown
textarea.addEventListener('keydown', function(event){
var self = this,
name = chatName.value;

if(event.which === 13 && event.shiftKey === false){
socket.emit('input', {
name: name,
message: self.value
});
}
});
}

})();
</script>
<script>
var time_total;
var timeout_setter;
var player;
var tag = document.createElement("script");//This code loads the IFrame Player API code asynchronously

tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName("script")[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

//This function creates an <iframe> (and YouTube player) OR uses the iframe if it exists at the "player" element after the API code downloads
function onYouTubeIframeAPIReady()
{
player = new YT.Player("player",
{
height: "853",
width: "480",
/* videoId: "GGmxVDXM5X2UxaP9PvWQ4Z171DXyGcq", */
playerVars: {
listType:'playlist',
list: 'PL_GGmxVDXM5X2UxaP9PvWQ4Z171DXyGcq',
controls: '0',
html5: '1',
cc_load_policy: '0',
disablekb: '1',
iv_load_policy: '3',
modestbranding: '1',
showinfo: '0',
rel: '0',


},
events:
{
"onReady": onPlayerReady,
"onStateChange": onPlayerStateChange
}

});
}
//The API will call this function when the video player is ready
function onPlayerReady(event)
{
event.target.playVideo();
time_total = convert_to_mins_and_secs(player.getDuration(), 1);
loopy();
}

function loopy()
{
var current_time = convert_to_mins_and_secs(player.getCurrentTime(), 0);
document.getElementById("progress-bar").style.width = (player.getCurrentTime()/player.getDuration())*100+"%";
console.log( current_time + " / " + time_total);
timeout_setter = setTimeout(loopy, 1000);
}

function convert_to_mins_and_secs(seconds, minus1)
{
var mins = (seconds>=60) ?Math.round(seconds/60):0;
var secs = (seconds%60!=0) ?Math.round(seconds%60):0;
var secs = (minus1==true) ?(secs-1):secs; //Youtube always displays 1 sec less than its duration time!!! Then we have to set minus1 flag to true for converting player.getDuration()
var time = mins + ":" + ((secs<10)?"0"+secs:secs);
return time;
}

// 5. The API calls this function when the player's state changes
function onPlayerStateChange(event)
{
if (event.data == YT.PlayerState.ENDED)
{
console.log("END!");
clearTimeout(timeout_setter);
}
else
{
console.log(event.data);
}
}
</script>

</body>
</html>


CSS:

body {
background-color: #0f0f17;
margin: 0px;
width: 100%;
}

.container-middle-third{
margin-top: 20px;
margin-left: 155px;
}

body,
textarea,
input {
font: 13px "Raleway", sans-serif;
color: #ffffff;

}

.bar{
height: 80px;
width: 100%;
background-color: #15151d;
}

.top-logo{
max-height: 100%;
max-width: 100%;
margin-left: 4%
}

li{
display: inline;
padding-right: 20px;
}

.standard-nav {
list-style: none;
padding: 0;
text-transform: uppercase;
line-height: 13px;
font-weight: 700;
margin-left: 63%;
max-height: 100%;
}

.DJ-text{
font-weight: 700;
/*position:relative;*/
text-transform: uppercase;
}

.Chat-text{
font-weight: 700;
text-transform: uppercase;
}

.DJ-underline{
width: 850px;
height: 1px;
position:relative;top:10px;
background-color: #3f3f45;
margin: 0px 0px 40px;
}

.Chat-underline{
width: 100%;
position:relative;
/*left:-140px;*/
float:right;
height: 1px;
position:relative;top:10px;
background-color: #3f3f45;
margin: 0px 0px 40px;
}

.youtube-video{

}
.transparent-layer{
width: 850px;
height: 477px;
pointer-events: none;
background-color: #ffffff;
}

.ad{
width: 728px;
height: 90px;
border: 1px solid #000000;
margin-left: 11px;
margin-top: 20px;
}

.chat {
min-width: 400px;
margin: 0px 0px 0px 135px;
}
.chat-messages,
.chat-textarea,
.chat-name {
border: 1px solid #1a1a23;
background-color: #1a1a23;
}

.userName{
font-weight: 700;
color: #079ce0;
}

.chat-messages {
width:380px;
height:400px;
overflow-y:scroll;
padding:10px;
}

.chat-message {
margin-bottom:10px;
}

.info-rect{
height: 40px;
width: 180px;
padding:10px;
max-width: 100%;
margin:0;
border:0;
display: flex;
align-items: center;
justify-content: center;
font-weight: 700;
text-transform: uppercase;
background-color: #15151d
}

.chat-name{
height: 40px;
max-width: 100%;
width: 180px;
padding:10px;
border:0;
margin:0;
font-weight: 700;
text-transform: uppercase;
float:left;
text-align: center;
}

.chat textarea {
width:380px;
padding:10px;
margin:0;
border-top:0;
max-width:100%;
border-top: 1px solid #0f0f17;
border-bottom: 1px solid #1a1a23;
border-right: 1px solid #1a1a23;
border-left: 1px solid #1a1a23;
background-color: #1a1a23;

}

.chat-status {
color: #bbb;
opacity: 0;
background-color: #0f0f17;
}

.info-rect,
.chat textarea,
.chat-name {
max-width: 100%;
}


.bottom-bar{
position: fixed;
bottom: 0;
width: 100%;
}

.thumbnail{
width: 4%;
height: 80px;
opacity: 0;
background-color: #ffffff
}

.title-bar{
width:63%;
height: 80px;
background-color: #1a1a23;
}
.song-name{
font-weight: 700;
text-transform: uppercase;
margin-left: 30px;
margin-top: 25px;
}
.dj-playing{
margin-left: 30px;
}
.progress-background{
width: 63%;
height: 4px;
background-color: #313139;
position: fixed;
bottom: 0;
}
.progress-bar{
height: 4px;
transition: all 1s linear;
background-color: #fa1d57;
bottom: 0;
}
.subscribe{
width: 33%;
height: 80px;
background-color: #15151d;
}

Answer

It's easy as 123 :

When a YouTube video ends:

  • clear the timeout with clearTimeout(), in order to keep from using too much browser memory
  • put the progress-bar transition style to none

The second line is what does it. Because it resets the width to whatever width the element had prior to transitions.

You already know when the YouTube video ends, it's in the block statement :

if (event.data == YT.PlayerState.ENDED) {

That was resetting. Now with everything you "stopped", you have to start back again when a new video starts:

  • clearTimeout(timeout_setter); basically stops the loopy() function, so just call it again when a new video starts.
  • put the progress-bar transition style to what it was before.

HTML

            <!DOCTYPE html>
            <html>
              <head>
                <title>Chat</title>
                <link rel="stylesheet" href="main.css">
              </head>

              <body>

                <div class="header-bar">
                  <div class="bar">

                      <img src="C:\Users\jlewa\Desktop\assets\affinity_fm_only_letters.png" class="top-logo" style="float: left;">

                      <ul class="standard-nav" style="float: left;">

                      <li>Home</li>
                      <li>Lyrics Hub</li>
                      <li>Affinity LIVE</li>
                      <li>Merchandise</li>

                      </ul>

                  </div>
                  <div class="dropshadow"></div>
                </div>

                <div class="container-middle-third">
                  <div class="youtube-video" style="float: left;">
                    <div class="DJ-text">Affinity FM DJ Room</div>
                    <div class="DJ-underline"></div>
                    <div id="player" style="width: 853px; height: 480px;"></div></div>
                  </div>

                  <div class="chat" style="float: left;">
                    <div class="Chat-text">Chat</div>
                    <div class="Chat-underline"></div>
                    <input type="text" class="chat-name" placeholder="Chat">
                    <div class="info-rect">Info</div>
                    <div class="chat-messages"></div>
                    <textarea placeholder="Join the conversation..."></textarea>
                    <div class="chat-status">Status: <span>Idle</span></div>
                  </div>
                </div>

                <div class="bottom-bar">

                  <div class="thumbnail" style="float: left">



                  </div>

                  <div class="title-bar" style="float: left;">

                    <div class="song-name">Finding Hope - Let Go (feat. Deverano)</div>
                    <div class="dj-playing">Affinity FM is playing</div>

                    <div class="progress-background">
                      <div id="progress-bar" class="progress-bar"></div>
                    </div>

                  </div>
                  <div class="subscribe" style="float: left;"></div>

                </div>

                <script src="http://127.0.0.1:8080/socket.io/socket.io.js"></script>

                <script>
                  (function() {
                    var getNode = function(s) {
                      return document.querySelector(s);
                    },

                        // Get required nodes
                        status = getNode('.chat-status span'),
                        messages = getNode('.chat-messages'), 
                        textarea = getNode('.chat textarea'),
                        chatName = getNode('.chat-name'),

                        statusDefault = status.textContent,    

                        setStatus = function(s){
                          status.textContent = s;

                          if(s !== statusDefault){
                            var delay = setTimeout(function(){
                              setStatus(statusDefault);
                              clearInterval(delay);
                            }, 3000);
                          }
                        };

                    //try connection
                    try{
                      var socket = io.connect('http://127.0.0.1:8080');
                    } catch(e){
                      //Set status to warn user
                    }

                    if(socket !== undefined){

                      //Listen for output
                      socket.on('output', function(data){
                        if(data.length){
                          //Loop through results
                          for(var x = 0; x < data.length; x = x + 1){
                            var message = document.createElement('div');
                            message.setAttribute('class', 'chat-message');
                            message.textContent = ': ' + data[x].message;
                            var name=document.createElement('span');
                            name.setAttribute('class', 'userName');
                            name.textContent = data[x].name;

                            message.insertBefore(name, message.firstChild);

                            //Append
                            messages.appendChild(message);
                            messages.insertBefore(message, messages.firstChild);
                          }
                        }
                      });

                      //Listen for a status
                      socket.on('status', function(data){
                        setStatus((typeof data === 'object') ? data.message : data);

                        if(data.clear === true){
                          textarea.value = '';
                        }
                      });

                      //Listen for keydown
                      textarea.addEventListener('keydown', function(event){
                        var self = this,
                            name = chatName.value;

                        if(event.which === 13 && event.shiftKey === false){
                          socket.emit('input', {
                            name: name,
                            message: self.value
                          });
                        }
                      });
                    }

                  })();
                </script>
                <script>
                  var time_total;
                  var timeout_setter;
                  var player;
                  var tag = document.createElement("script");//This code loads the IFrame Player API code asynchronously

                  tag.src = "https://www.youtube.com/iframe_api";
                  var firstScriptTag = document.getElementsByTagName("script")[0];
                  firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

                  //This function creates an <iframe> (and YouTube player) OR uses the iframe if it exists at the "player" element after the API code downloads
                  function onYouTubeIframeAPIReady()
                  {
                    player = new YT.Player("player",
                                           {
                      height: "853",
                      width: "480",
                      /* videoId: "GGmxVDXM5X2UxaP9PvWQ4Z171DXyGcq", */
                      playerVars: {
                        listType:'playlist',
                        list: 'PLcV3JzAz3r32-on4upkyAv0VtvYXAl-Nz', //'PL_GGmxVDXM5X2UxaP9PvWQ4Z171DXyGcq',
                        controls: '0',
                        html5: '1',
                        cc_load_policy: '0',
                        disablekb: '1',
                        iv_load_policy: '3',
                        modestbranding: '1',
                        showinfo: '0',
                        rel: '0',


                      },
                      events:
                      {
                        "onReady": onPlayerReady,
                        "onStateChange": onPlayerStateChange
                      }

                    });
                  }
                  //The API will call this function when the video player is ready
                  function onPlayerReady(event)
                  {
                    event.target.playVideo();
                    time_total  = convert_to_mins_and_secs(player.getDuration(), 1);
                    loopy();
                  }

                  function loopy()
                  {
                    var current_time = convert_to_mins_and_secs(player.getCurrentTime(), 0);
                    document.getElementById("progress-bar").style.width = (player.getCurrentTime()/player.getDuration())*100+"%";
                    console.log( current_time + " / " + time_total);
                    timeout_setter = setTimeout(loopy, 1000);
                  }

                  function convert_to_mins_and_secs(seconds, minus1)
                  {
                    var mins    = (seconds>=60) ?Math.round(seconds/60):0;
                    var secs    = (seconds%60!=0) ?Math.round(seconds%60):0;
                    var secs    = (minus1==true) ?(secs-1):secs; //Youtube always displays 1 sec less than its duration time!!! Then we have to set minus1 flag to true for converting player.getDuration()
                    var time    = mins + ":" + ((secs<10)?"0"+secs:secs);
                    return time;
                  }

                  // 5. The API calls this function when the player's state changes
                  function onPlayerStateChange(event)
                  {
                    if (event.data == YT.PlayerState.ENDED)
                    {
                      console.log("END!");
                      clearTimeout(timeout_setter);
                      document.getElementById("progress-bar").style.cssText = "transition: none;";
                    }
                    else if (event.data == YT.PlayerState.PLAYING)
                    {
                      console.log("PLAYING");
                      loopy();
                      document.getElementById("progress-bar").style.cssText = "transition: all 1s linear 0s;";
                    }
                    else
                    {
                      console.log(event.data);
                    }
                  }
                </script>

              </body>
            </html>

CSS

        body {
          background-color: #0f0f17;
          margin: 0px;
          width: 100%;
        }

        .container-middle-third{
          margin-top: 20px;
          margin-left: 155px;
        }

        body,
        textarea,
        input {
          font: 13px "Raleway", sans-serif;
          color: #ffffff;

        }

        .bar{
          height: 80px;
          width: 100%;
          background-color: #15151d;   
        }

        .top-logo{
            max-height: 100%;
            max-width: 100%;
            margin-left: 4%
        }

        li{
            display: inline;
            padding-right: 20px;
        }

        .standard-nav {
            list-style: none;
            padding: 0;
            text-transform: uppercase;
            line-height: 13px;
            font-weight: 700;
            margin-left: 63%;
            max-height: 100%;
        } 

        .DJ-text{
          font-weight: 700;
          /*position:relative;*/
          text-transform: uppercase;
        }

        .Chat-text{
          font-weight: 700;
          text-transform: uppercase;
        }

        .DJ-underline{
          width: 850px;
          height: 1px;
          position:relative;top:10px;
          background-color: #3f3f45;
          margin: 0px 0px 40px;
        }

        .Chat-underline{
          width: 100%;
          position:relative;
          /*left:-140px;*/
          float:right;
          height: 1px;
          position:relative;top:10px;
          background-color: #3f3f45;
          margin: 0px 0px 40px;
        }

        .youtube-video{

        }
        .transparent-layer{
          width: 850px;
          height: 477px;
          pointer-events: none;
          background-color: #ffffff;
        }

        .ad{
          width: 728px;
          height: 90px;
          border: 1px solid #000000;
          margin-left: 11px;
          margin-top: 20px;
        }

        .chat {
          min-width: 400px;
          margin: 0px 0px 0px 135px;
        }
        .chat-messages,
        .chat-textarea,
        .chat-name {
          border: 1px solid #1a1a23;
          background-color: #1a1a23;
        }

        .userName{
          font-weight: 700;
          color: #079ce0;
        }

        .chat-messages {
          width:380px;
          height:400px;
          overflow-y:scroll;
          padding:10px;
        }

        .chat-message {
          margin-bottom:10px;
        }

        .info-rect{
          height: 40px;
          width: 180px;
          padding:10px;
          max-width: 100%;
          margin:0;
          border:0;
          display: flex; 
          align-items: center;
          justify-content: center;  
          font-weight: 700;
          text-transform: uppercase;
          background-color: #15151d
        }

        .chat-name{
          height: 40px;
          max-width: 100%;
          width: 180px;
          padding:10px;
          border:0;
          margin:0;
          font-weight: 700;
          text-transform: uppercase;
          float:left;
          text-align: center;
        }

        .chat textarea {
          width:380px;
          padding:10px;
          margin:0;
          border-top:0;
          max-width:100%;
          border-top: 1px solid #0f0f17;
          border-bottom: 1px solid #1a1a23;
          border-right: 1px solid #1a1a23;
          border-left: 1px solid #1a1a23;
          background-color: #1a1a23;

        }

        .chat-status {
          color: #bbb;
          opacity: 0;
          background-color: #0f0f17;
        }

        .info-rect,
        .chat textarea,
        .chat-name { 
          max-width: 100%; 
        }


        .bottom-bar{
          position: fixed;
          bottom: 0;
          width: 100%;
        }

        .thumbnail{
          width: 4%;
          height: 80px;
            opacity: 0;
          background-color: #ffffff
        }   

        .title-bar{
          width:63%;
          height: 80px;
          background-color: #1a1a23;
        }
        .song-name{
          font-weight: 700;
          text-transform: uppercase;
          margin-left: 30px;
          margin-top: 25px;
        }
        .dj-playing{
          margin-left: 30px;
        }
        .progress-background{
          width: 63%;
          height: 4px;
          background-color: #313139;
          position: fixed;
          bottom: 0;
        }
        .progress-bar{
          height: 4px;
          width: 0px;
          transition: all 1s linear;
          background-color: #fa1d57;
          bottom: 0;
        }
        .subscribe{
          width: 33%;
          height: 80px;
          background-color: #15151d;
        }

Take a quick look there :

http://lespointscom.com/a/misc/demo/2016_06_20/main.html

One last thing that I modified is the .progress-bar width style in your external stylesheet. WHy? Because it's better to initialize with a width of 0 at the beginning. This way, when you put the transition to 0, you don't get the 100% width transformation at every new video.

Look at what I mean there :

http://lespointscom.com/a/misc/demo/2016_06_20/main_alt.html