user11730319
user11730319

Reputation:

HTML5 video media fragments with Javascript

No one on here ever comments on my questions but maybe this is an easy one? I'm new to coding and I am trying to find a way to navigate a video, as in play the first 10 seconds then stop, play seconds 30-40 then stop, play second 60-30 then stop, etc and to have buttons for it. I have found a tutorial but the buttons don't respond. I've cleaned up the code a bit (don't think I removed anything necessary) and pasted it below. Tutorial link: https://www.sitepoint.com/html5-video-fragments-captions-dynamic-thumbnails/

HTML:

<head>

    <meta charset="UTF-8">
    <title>Untitled Document</title>

    <link href="style.css" rel="stylesheet" type="text/css">
    <script type="text/javascript" src="script.js"></script>

</head>

<body>

<section class="wrapper">

  <div>
    <video id="frag1" controls preload="metadata">

      <source src="assets/disney.mp4"  type='video/mp4;codecs="avc1.42E01E, mp4a.40.2"' data-original="assets/disney.mp4">

      <source src="assets/disney.mp4" type='video/webm;codecs="vp8, vorbis"' data-original="assets/disney.mp4">

    </video>
    <nav>
      <button data-start="0">Section One</button>
      <button data-start="6">Section Two</button>
      <button data-start="17">Section Three</button>
    </nav>
  </div>

</section>


</body>

</html>

CSS:

@charset "UTF-8";

#main { width: 85%; margin: 1em auto; }

section>div { margin: 0 1.5% 1.5% 0; padding: 1.5%; float: left; background-color: #efefef; }


video { width: 100%; min-height: 430px; }
button { cursor: pointer; }
nav>button { margin: 0.27em; }
nav>button:first-child { margin-left: 0; }
.wrapper { width: 520px; margin: auto; overflow: hidden; text-align: center; }

.p {
  text-align: center;
  padding-top: 120px;
}

Resources 1×0.5×0.25× Rerun

JAVASCRIPT:

function mediaFragOne() 
{
var video, sources, nav, buttons;

video = document.querySelector('video#frag1');
sources = video.getElementsByTagName('source');
nav = document.querySelector('video#frag1+nav');
buttons = nav.getElementsByTagName('button');

for(var i = buttons.length - 1; i >= 0; i--) 
{
    buttons[i].addEventListener('click', function() {
            for (var i = sources.length - 1; i >= 0; i--) {
                sources[i].setAttribute(
                    'src', (sources[i].getAttribute('data-original')
                    .concat('#t=' + this.getAttribute('data-start'))));
                    video.load();
                    video.play();
            };
        });
    }
}
mediaFragOne();

Upvotes: 1

Views: 2849

Answers (3)

zer00ne
zer00ne

Reputation: 43880

Use Event Delegation when you have multiple tags to click.

  1. Find an ancestor tag (ex. <nav>) that all of the target tags (ex. <a>) have in common. Register the ancestor tag to the event. Now whenever the ancestor tag or its descendant tags triggers the registered event, the callback function bound to the ancestor tag will fire. So essentially that's one event listener for an unlimited number of target tags.

    document.querySelector(ancestor).addEventListener('click', callback);
    
  2. Always have callback function pass the Event Object.

    function callback(event) {...
    
  3. Determine the tag that is the origin of event (button clicked, video time updated, etc...) by referencing Event.target.

    const clicked = event.target
    
  4. In the callback function it should only apply to the target tags and exclude all other tags.

    if (clicked.matches('a')) {...
    ...}
    return false;
    

Details commented in demo

<!DOCTYPE html>
<html>

<head>
  <meta charset='utf-8'>
  <style>
     :root {
      font: 400 2.5vw/1.2 Consolas
    }
    
    .vid {
      display: block;
      margin: 0 auto;
      width: 80vw;
    }
    
    .btns {
      display: flex;
      justify-content: center;
      width: 80vw;
      margin: 0 auto;
    }
    
    a {
      display: block;
      margin: 5px 8px;
      border: 1px solid #000;
      border-radius: 8px;
      text-decoration: none;
      text-align: center;
      padding: 3px 5px;
      width: 15vw;
    }
  </style>
</head>

<body>
  <video class='vid' src='https://storage04.dropshots.com/photos6000/photos/1381926/20170326/005610.mp4' controls></video>
  <nav class='btns'>
    <a href='#/' data-start='00' data-end='10'>00 - 10</a>
    <a href='#/' data-start='10' data-end='20'>10 - 20</a>
    <a href='#/' data-start='20' data-end='30'>20 - 30</a>
    <a href='#/' data-start='30' data-end='40'>30 - 40</a>
  </nav>
  <script>
    // Declare end
    let end;
    // Reference the parent tag of all buttons
    const btns = document.querySelector('.btns');
    // Reference the video
    const vid = document.querySelector('.vid');

    // Register the click event to nav.btns
    btns.addEventListener('click', frag);

    // Register the timeupdate event to video.vid
    vid.addEventListener('timeupdate', stop);

    // Pass Event Object
    function frag(event) {
      // Event.target always points to the clicked button, time-updated video, etc.
      const clicked = event.target;

      // if clicked tag is an <a>...
      if (clicked.matches('a')) {
        // Get the value of its data-start and convert it to a real number
        let start = Number(clicked.dataset.start);
        // Set end to the value of it data-end and convert it to a real number
        end = Number(clicked.dataset.end);
        // Set video current time to value of start
        vid.currentTime = start;
        // Play video
        vid.play();
      }
      // End function
      return false;
    }

    // Pass Event Object
    function stop(event) {
      // Reference the video
      const vid = event.target;

      // if it is a <video>...
      if (vid.matches('video')) {
        // and if that video time is currently at or past the value of end...
        if (vid.currentTime >= end) {
          // Pause video
          vid.pause();
        }
      }
      // End function
      return false;
    }
  </script>
</body>

</html>

Upvotes: 1

Brad
Brad

Reputation: 163334

This is easier than you're making it to be. Just set the currentTime property.

Untested, but this should get you started:

document.querySelector('nav').addEventListener('click', (e) => {
  if (!e.target.matches('[data-start]')) {
    return;
  }
  document.querySelector('video').currentTime = Number.parseFloat(e.target.dataset.start);
});

Upvotes: 0

A. Meshu
A. Meshu

Reputation: 4148

Just to elaborate @Brand answer, you can do it like that:

var vid = document.getElementById("frag1"); // set video to variable
var buttons = document.getElementsByTagName("button"); // element collection
for (var i = 0; i < buttons.length; i++) { // run for loop
  buttons[i].addEventListener("click", function() { // add event listenr
    var dataStart = this.getAttribute('data-start'); // extract start time
    var dataEnd = this.getAttribute('data-end'); // extract end time
    var dataDuration = Number(dataEnd - dataStart + '000'); // make the duration will assist on the setTimeOut
    vid.currentTime = dataStart; // this is the magic
    vid.play();
    window.setTimeout(function(){ vid.pause(); }, dataDuration);// play according to duration   
  });
}
<section class="wrapper">
  <div>
    <video id="frag1" controls preload="metadata" width="400px">
      <source src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4">
      <source src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.ogg">
    </video>
    <nav>
      <button data-start="0" data-end="10">Section One</button>
      <button data-start="30" data-end="40">Section Two</button>
      <button data-start="30" data-end="60">Section Three</button>
    </nav>
  </div>
</section>

You can find a lot more information about this here and here.

Hope that helps.

Upvotes: 0

Related Questions