Skip to content

Video13.10.5

A basic video component that can be used in various ways and on many different types of pages.


$ npm i @if-design-system/video@13.10.5

Our visual identity Let us show you our new visual identity
Edit this section

Overview

Video can be used on product or marketing pages.

Video messages and tutorials play an integral role in how customers learn about our products. They give users an engaging way to discover features and learn how to accomplish unfamiliar workflows.

Edit this section

Variations

Type Purpose
HTML5 video For video files that is hosted locally or through a CMS
Embedded YouTube video For YouTube videoes
Edit this section

Usage

Use videos wherever some light guidance can help a user complete a task, especially when learning a new feature or trying out something for the first time. Be sure to present the video in a way that doesn’t compete with primary page content. We never want our video content to get in the way of the user’s getting stuff done. Use good judgment on when and where to include videos.

If Villaförsäkring Se hur If Villaförsäkring förändrade Anna’s vardag
Full width html5 video
If Villaförsäkring Se hur If Villaförsäkring förändrade Anna’s vardag
Full width youtube video

You can put videos in the Split component aswell.

<div class="if block">
  <section class="if split">
    <div class="if container">
      <div class="if content">
        <h1 class="if heading medium">
          Drulleförsäkringen
        </h1>
        <p class="if text body">
          Allriskförsäkring, drulleförsäkring eller otursförsäkring – oavsett vad du kallar det så är det en försäkring
          som hjälper dig när du haft lite otur och till exempel välter kaffekoppen över datorn eller tappar
          solglasögonen i havet.
        </p>
        <a href="/asdasd" class="if standalone">Läs mer om vår drulleförsäkring</a>
      </div>
      <div class="if image">
        <div class="if video">
          <video
            class="if player"
            src="https://www.dreambroker.com/channel/qtauayyk/v8ukrs97/get/fullhd.mp4"
            poster="https://v.imgi.no/cxb3sdbcsn-MOODBOARD/2042"
          >
            Sorry, your browser doesn't support embedded videos, but don't worry, you can
            <a href="https://archive.org/details/BigBuckBunny_124">download it</a>
            and watch it with your favorite video player!
          </video>
          <div class="if overlay">
            <span class="if title">If Villaförsäkring</span>
            <span class="if description">Se hur If Villaförsäkring förändrade Anna’s vardag</span>
            <button type="button" class="if play button secondary">
              Spela up filmen
            </button>
          </div>
        </div>
      </div>
    </div>
  </section>
</div>

You can also have captions/subtitles with your video.

Our visual identity Let us show you our new visual identity

You can use youtube videos and self hosted videos.

Edit this section

Overlay

The player has got an overlay before the video is activated, when it's activated and hovered, and when it is active and paused.

Edit this section

Controls

Tips
  • Some controls are optional and some are now, both are covered below
  • Controls are an integral part of usability, make sure all controls work as they should
Volume

If the video contains sound, there should be a way for users to control the volume and to mute/unmute

Subtitles

If the video contains spoken word, a narrator or any sort of dialog, the user should be able to choose from a set of subtitles

Seeker

If the video is long, and ment to be informative instead of a visual element, the user should be able to see the duration of the video and be able to traverse the video

Closed caption

For accessibility purposes, if the video is not just a visual element, closed captions should be present. These are not just subtitles, but should also describe what's going on in the video. The transcripts are generated from closed captions, so it's important to include the dialogue here as well

Quality

Generally, the quality of the video displayed should fit the bandwidth the user has. The user should also have the possibility to change the quality of the video if it's not just a visual element.

Transcript

The transcript is generated from the closed captions tracks, and should reflect the chosen language of either the html tag or the user.

The transcript component is a simple expandable panel.

Our visual identity Let us show you our new visual identity
Our visual identity Let us show you our new visual identity
Controls
  1. Play/pause
  2. Control panel
  3. Volume control
  4. Subtitles control
  5. Seeker control
  6. Closed caption control
  7. Quality control

Volume

The volume control allows the user to control the volume. The control can mute, increase or decrease the volume

If the button is clicked, the video is muted/unmuted. When hovering over the control, the volume can be either increased or decreased.

The control has a clear icon for both states, and a title describing the control.

Our visual identity Let us show you our new visual identity
Controls
  1. Volume control
  2. Volume button to toggle on and off sound
  3. Input to change volume

Subtitles

Our visual identity Let us show you our new visual identity
Controls
  1. Subitles control
  2. Button to toggle the menu
  3. Menu selection for subtitles
  4. Toggle off subtitles

Seeker

Our visual identity Let us show you our new visual identity
Controls
  1. Seeker control
  2. Current time
  3. Seeker/Progress bar
  4. Remaining time

Closed captions

Our visual identity Let us show you our new visual identity
Controls
  1. Button
  2. Description
  3. Menu

Quality

Our visual identity Let us show you our new visual identity
Controls
  1. Button
  2. Menu
  3. Default quality is set to automatic
Edit this section

Transcript

The transcript component should be automatically generated by the enclosed caption tracks in the video element.

Transcript
  1. Expandable
  2. Content
Edit this section

States

If Villaförsäkring Se hur If Villaförsäkring förändrade Anna’s vardag
Not playing, not started
If Villaförsäkring Se hur If Villaförsäkring förändrade Anna’s vardag
Playing, hovered
If Villaförsäkring Se hur If Villaförsäkring förändrade Anna’s vardag
Not playing, paused
Edit this section

Implementation

For html5 video

<div class="if video [is-active|is-paused]">
  <video
    muted
    title="Our visual identity"
    class="if player"
    disablePictureInPicture
    poster="https://www.dreambroker.com/channel/qtauayyk/v8ukrs97/get/poster?etag=1576140475000"
    preload="none"
  >
    <track label="English" kind="subtitles" srclang="en" src="/videos/captions/vtt/if-design-en.vtt" />
    <track label="Norsk" kind="subtitles" srclang="no" src="/videos/captions/vtt/if-design-no.vtt" />
    <track label="Dansk" kind="subtitles" srclang="dk" src="/videos/captions/vtt/if-design-dk.vtt" />
    <track label="Svenska" kind="subtitles" srclang="se" src="/videos/captions/vtt/if-design-se.vtt" />
    <track label="English" kind="captions" srclang="en" src="/videos/captions/vtt/if-design-captions.en.vtt" />
    <track label="Norsk" kind="captions" srclang="no" src="/videos/captions/vtt/if-design-captions.no.vtt" />
    <source
      data-source-type="quality"
      name="Full HD"
      label="Full HD (1080p)"
      quality="1080p"
      src="https://www.dreambroker.com/channel/qtauayyk/v8ukrs97/get/fullhd.mp4"
      type="video/mp4"
    />
    <source
      data-source-type="quality"
      name="HD"
      label="HD (720p)"
      quality="720p"
      src="https://www.dreambroker.com/channel/qtauayyk/v8ukrs97/get/hd.mp4"
      type="video/mp4"
    />
    <source
      data-source-type="quality"
      name="SD"
      label="SD (480p)"
      quality="480p"
      src="https://www.dreambroker.com/channel/qtauayyk/v8ukrs97/get/normal.mp4"
      type="video/mp4"
    />
    <source
      data-source-type="quality"
      name="Mobile"
      label="Mobile (240p)"
      quality="240p"
      src="https://www.dreambroker.com/channel/qtauayyk/v8ukrs97/get/mobile.mp4"
      type="video/mp4"
    />
    Sorry, your browser doesn't support embedded videos, but don't worry, you can
    <a href="https://www.dreambroker.com/channel/qtauayyk/v8ukrs97/get/fullhd.mp4">download it</a>
    and watch it with your favorite video player!
  </video>
  <div class="if overlay">
    <span class="if title">Our visual identity</span>
    <span class="if description">
      Let us show you our new visual identity
    </span>
    <button class="if play button secondary" type="button">
      Play
    </button>
  </div>
  <div class="if controls">
    <div class="if volume-control">
      <button type="button" class="if volume button"><span class="if description">Volume</span></button>
      <input class="if volume js-volume-control" type="range" min="0" max="100" step="1" />
    </div>
    <div class="if subtitles-control">
      <button type="button" class="if subtitles button">
        <span class="if description">Subtitles</span>
      </button>
    </div>
    <div class="if seeker-control">
      <span class="if js-seeker-time-elapsed"></span>
      <progress class="if seeker js-seeker-control" value="0" min="0" max="100"></progress>
      <span class="if js-seeker-time-remaining"></span>
    </div>
    <div class="if cc-control">
      <button type="button" class="if cc button"><span class="if description">Captions</span></button>
    </div>
    <div class="if quality-control">
      <button type="button" class="if quality button">
        <span class="if js-video-quality">HD</span><span class="if description">Quality</span>
      </button>
    </div>
  </div>
</div>

JavaScript implementation example

Note!

This is just a quick example on how to implement the video player. YMMV.

/* eslint no-console:0*/

function isChrome() {
  return navigator.userAgent.indexOf('Chrome') > -1;
}

if (isChrome()) {
  document.querySelector('html').classList.add('chrome');
}
function isMobileDevice() {
  return (
    navigator.userAgent.match(/Android/i) ||
    navigator.userAgent.match(/webOS/i) ||
    navigator.userAgent.match(/iPhone/i) ||
    navigator.userAgent.match(/iPad/i) ||
    navigator.userAgent.match(/iPod/i) ||
    navigator.userAgent.match(/BlackBerry/i) ||
    navigator.userAgent.match(/Windows Phone/i)
  );
}

function checkBandwidth() {
  if (!navigator.connection) {
    if (isMobileDevice()) return '3g';
    return '4g';
  }
  return navigator.connection.effectiveType;
}

function getQualityByBandwidth() {
  var bandwidth = checkBandwidth();
  if (bandwidth == '4g') {
    return '1080p';
  } else if (bandwidth == '3g') {
    return '480p';
  } else {
    return '240p';
  }
}

function str_pad_left(string, pad, length) {
  return (new Array(length + 1).join(pad) + string).slice(-length);
}

const createMediaTimestamp = time => {
  var minutes = Math.floor(time / 60);
  var seconds = time - minutes * 60;

  var finalTime = str_pad_left(minutes, '0', 2) + ':' + str_pad_left(seconds, '0', 2);

  return finalTime;
};

const initVideo = (video, videoIndex) => {
  let pauseTime = 0;
  const videoContainer = video.parentElement;
  const videoOverlay = videoContainer.querySelector('.if.overlay');
  const playButton = videoContainer.querySelector('.if.play.button');
  const controls = videoContainer.querySelector('.if.controls');
  const captionsControl = videoContainer.querySelector('.if.cc-control');
  const captionsButton = videoContainer.querySelector('.if.cc.button');
  const qualityControl = videoContainer.querySelector('.if.quality-control');
  const qualityButton = videoContainer.querySelector('.if.quality.button');
  const subtitlesControl = videoContainer.querySelector('.if.subtitles-control');
  const subtitlesButton = videoContainer.querySelector('.if.subtitles.button');
  const seekerRange = videoContainer.querySelector('.if.js-seeker-control');
  const volumeControl = videoContainer.querySelector('.if.volume-control');
  const volumeButton = videoContainer.querySelector('.if.volume.button');
  const volumeRange = videoContainer.querySelector('.if.js-volume-control');
  const seekerElapsed = videoContainer.querySelector('.if.js-seeker-time-elapsed');
  const seekerRemaining = videoContainer.querySelector('.if.js-seeker-time-remaining');

  const titleElement = videoOverlay.querySelector('.if.title');
  const title = `video-${videoIndex}-title`;
  titleElement.setAttribute('id', title);

  video.setAttribute('aria-labelledby', title);

  const createTranscript = caption => {
    const transcriptExpandableElement = videoContainer.parentElement.querySelector('.if.panel');
    if (!transcriptExpandableElement) return;

    const transcriptExpandableTitleElement = transcriptExpandableElement.querySelector('.if.title');
    const transcriptionExpandableContentElement = transcriptExpandableElement.querySelector('.if.title + .if.content');

    const transcriptionExpandableContentId = `video-${videoIndex}-transcription-content-title`;
    const transcriptExpandableTitleId = `video-${videoIndex}-transcription-expandable-title`;

    transcriptExpandableTitleElement.setAttribute('aria-controls', transcriptionExpandableContentId);
    transcriptExpandableTitleElement.setAttribute('tabindex', 0);
    transcriptExpandableTitleElement.setAttribute('aria-expanded', 'false');
    transcriptExpandableTitleElement.setAttribute('id', transcriptExpandableTitleId);

    transcriptionExpandableContentElement.setAttribute('aria-live', 'off');
    transcriptionExpandableContentElement.setAttribute('aria-atomic', true);
    transcriptionExpandableContentElement.setAttribute('role', 'region');
    transcriptionExpandableContentElement.setAttribute('aria-relevant', 'all');
    transcriptionExpandableContentElement.setAttribute('tabindex', 0);

    Array.prototype.slice.call(caption.cues).forEach(cue => {
      var pF = document.createDocumentFragment();
      var cueLine = document.createElement('span');
      var text = document.createTextNode(cue.text);
      cueLine.appendChild(text);
      cueLine.classList.add('if');
      cueLine.classList.add('transcript-line');
      pF.appendChild(cueLine);
      transcriptionExpandableContentElement.appendChild(pF);
    });

    video.setAttribute('aria-describedby', transcriptionExpandableContentId);
  };

  if (volumeControl) {
    volumeControl.addEventListener('mouseenter', e => {
      e.target.classList.add('is-active');
    });

    if (video.muted || video.muted == 'true') {
      volumeButton.classList.add('is-muted');
    } else {
      volumeButton.classList.remove('is-muted');
    }

    volumeButton.addEventListener('click', () => {
      if (video.muted || video.muted == 'true') {
        video.muted = false;
        volumeButton.classList.remove('is-muted');
      } else {
        video.muted = true;
        volumeButton.classList.add('is-muted');
      }
    });

    volumeRange.addEventListener('change', e => {
      video.volume = e.target.value / 100;
      if (e.target.value == 0) {
        video.muted = true;
        volumeButton.classList.add('is-muted');
      } else {
        video.muted = false;
        volumeButton.classList.remove('is-muted');
      }
    });

    volumeRange.addEventListener('input', e => {
      video.volume = e.target.value / 100;
      if (isChrome()) {
        volumeRange.style.backgroundImage =
          'linear-gradient(to right, #faf9f7 0%, #faf9f7 ' +
          e.target.value +
          '%, #6e625e ' +
          e.target.value +
          '%, #6e625e 100%)';
      }
    });
  }

  if (videoOverlay && volumeControl) {
    videoOverlay.addEventListener('mouseleave', () => {
      volumeControl.classList.remove('is-active');
    });
  }

  for (var i = 0; i < video.textTracks.length; i++) {
    video.textTracks[i].mode = 'hidden';
  }

  var qualitySources = video.querySelectorAll('source[data-source-type]');

  if (!qualitySources || qualitySources.length == 0) {
    if (qualityControl) {
      qualityControl.classList.add('hidden');
    }
  } else {
    if (!qualityControl) return;
    var qualityMenuButtons = [];
    var createQualityMenuItem = function(id, name, quality, label) {
      var listItem = document.createElement('li');
      listItem.classList.add('if');
      if (label === 'separator') {
        listItem.classList.add('separator');
      }
      if (label !== 'separator') {
        var button = listItem.appendChild(document.createElement('button'));
        button.setAttribute('id', `if-video-${videoIndex}-${id}`);
        button.className = 'if';
        if (quality.length > 0) button.setAttribute('quality', quality);

        button.setAttribute('data-state', 'inactive');
        if (id === 'quality-automatic') {
          button.setAttribute('data-state', 'active');
          button.classList.add('is-active');
          var autoQuality = getQualityByBandwidth();
          button.setAttribute('quality', autoQuality);
          button.value = autoQuality;
          button.appendChild(document.createTextNode(`${name} (${autoQuality})`));
          const source = video.querySelector(`source[quality="${autoQuality}"]`);

          if (source) {
            source.remove(); //Remove the source from select
            video.prepend(source); //Prepend source on top of options
            video.load();
            controls.querySelector('.js-video-quality').textContent = source.getAttribute('name');
          }
        } else {
          button.value = quality;
          button.appendChild(document.createTextNode(label));
        }

        button.addEventListener('click', function() {
          var autoQuality = getQualityByBandwidth();
          var source;
          // Set all buttons to inactive
          qualityMenuButtons.map(button => {
            button.setAttribute('data-state', 'inactive');
            button.classList.remove('is-active');
          });
          if (id === 'quality-automatic') {
            source = video.querySelector(`source[quality="${autoQuality}"]`);
            button.textContent = `${name} (${autoQuality})`;
            controls.querySelector('.js-video-quality').textContent = source.getAttribute('name');
          } else {
            source = video.querySelector(`source[quality="${quality}"]`);
            controls.querySelector('.js-video-quality').textContent = name;
          }

          pauseTime = video.currentTime; //Get Current Time of Video
          source.remove(); //Remove the source from select
          video.prepend(source); //Prepend source on top of options
          video.load(); //Reload Video
          video.play(); //Resume video
          video.currentTime = pauseTime; //Continue from video's stop

          button.setAttribute('data-state', 'active');
          button.classList.add('is-active');

          qualityMenuHolder.classList.toggle('is-open');
          qualityMenu.classList.toggle('is-open');
        });
        qualityMenuButtons.push(button);
      }
      return listItem;
    };
    var qualityMenu;
    var qualityMenuHolder;
    if (qualitySources.length && qualitySources.length > 1) {
      var df = document.createDocumentFragment();
      qualityMenuHolder = df.appendChild(document.createElement('div'));
      qualityMenuHolder.classList.add('if');
      qualityMenuHolder.classList.add('dropdown-menu');
      qualityMenuHolder.classList.add('bottom');
      qualityMenuHolder.classList.add('left');
      qualityMenu = qualityMenuHolder.appendChild(document.createElement('ul'));
      qualityMenu.className = 'if';

      qualitySources.forEach(source => {
        qualityMenu.appendChild(
          createQualityMenuItem(
            'quality-' + source.getAttribute('quality'),
            source.getAttribute('name'),
            source.getAttribute('quality'),
            source.getAttribute('label')
          )
        );
      });
      qualityMenu.appendChild(createQualityMenuItem('quality-separator', '', '', 'separator'));
      qualityMenu.appendChild(createQualityMenuItem('quality-automatic', 'Automatic', '', 'Automatic'));
      qualityControl.appendChild(qualityMenuHolder);

      qualityButton.addEventListener('click', () => {
        if (qualityMenuHolder) {
          qualityMenuHolder.classList.toggle('is-open');
          qualityMenu.classList.toggle('is-open');
        }
      });
    } else {
      controls.querySelector('.js-video-quality').textContent = qualitySources[0].getAttribute('name');
    }
  }

  const subtitlesTracks = Array.prototype.slice.call(video.textTracks).filter(track => track.kind == 'subtitles');
  const captionsTracks = Array.prototype.slice.call(video.textTracks).filter(track => track.kind == 'captions');

  if (captionsTracks && captionsTracks.length !== 0) {
    setTimeout(function() {
      createTranscript(captionsTracks[0]);
    }, 1000);
  }

  if (!subtitlesTracks || subtitlesTracks.length == 0) {
    if (subtitlesControl) {
      subtitlesControl.classList.add('hidden');
    }
  } else {
    if (!subtitlesControl) return;
    var subtitleMenuButtons = [];
    var createMenuItem = function(id, lang, label) {
      var listItem = document.createElement('li');
      listItem.classList.add('if');
      if (label === 'separator') {
        listItem.classList.add('separator');
      }
      if (label !== 'separator') {
        var button = listItem.appendChild(document.createElement('button'));
        button.setAttribute('id', `if-video-${videoIndex}-${id}`);
        button.className = 'if';
        if (lang.length > 0) button.setAttribute('lang', lang);
        button.value = label;
        button.setAttribute('data-state', 'inactive');
        button.appendChild(document.createTextNode(label));
        button.addEventListener('click', function() {
          // Set all buttons to inactive
          subtitleMenuButtons.map(button => {
            button.setAttribute('data-state', 'inactive');
            button.classList.remove('is-active');
          });
          // Find the language to activate
          var lang = this.getAttribute('lang');

          for (var i = 0; i < subtitlesTracks.length; i++) {
            // For the 'subtitles-off' button, the first condition will never match so all will subtitles be turned off
            if (subtitlesTracks[i].language == lang) {
              subtitlesTracks[i].mode = 'showing';
              this.setAttribute('data-state', 'active');
              button.classList.add('is-active');
            } else {
              subtitlesTracks[i].mode = 'hidden';
              button.classList.remove('is-active');
            }
          }
          subtitlesMenuHolder.classList.toggle('is-open');
          subtitlesMenu.classList.toggle('is-open');
        });
        subtitleMenuButtons.push(button);
      }
      return listItem;
    };
    var subtitlesMenu;
    var subtitlesMenuHolder;
    if (subtitlesTracks) {
      var subtitlesMenuHolderDF = document.createDocumentFragment();
      subtitlesMenuHolder = subtitlesMenuHolderDF.appendChild(document.createElement('div'));
      subtitlesMenuHolder.classList.add('if');
      subtitlesMenuHolder.classList.add('dropdown-menu');
      subtitlesMenuHolder.classList.add('bottom');
      subtitlesMenuHolder.classList.add('right');
      subtitlesMenu = subtitlesMenuHolder.appendChild(document.createElement('ul'));
      subtitlesMenu.className = 'if';
      subtitlesMenu.appendChild(createMenuItem('subtitles-off', '', 'Off'));
      subtitlesMenu.appendChild(createMenuItem('subtitles-separator', '', 'separator'));
      for (var o = 0; o < subtitlesTracks.length; o++) {
        subtitlesMenu.appendChild(
          createMenuItem(
            'subtitles-' + subtitlesTracks[o].language,
            subtitlesTracks[o].language,
            subtitlesTracks[o].label
          )
        );
      }
      subtitlesControl.appendChild(subtitlesMenuHolder);
    }

    subtitlesButton.addEventListener('click', () => {
      if (subtitlesMenuHolder) {
        subtitlesMenuHolder.classList.toggle('is-open');
        subtitlesMenu.classList.toggle('is-open');
      }
    });
  }

  if (!captionsTracks || captionsTracks.length == 0) {
    if (captionsControl) {
      captionsControl.classList.add('hidden');
    }
  } else {
    if (!captionsControl) return;
    var captionMenuButtons = [];
    var createCaptionsMenuItem = function(id, lang, label) {
      var listItem = document.createElement('li');
      listItem.classList.add('if');
      if (label === 'separator') {
        listItem.classList.add('separator');
      }
      if (label !== 'separator') {
        var button = listItem.appendChild(document.createElement('button'));
        button.setAttribute('id', `if-video-${videoIndex}-${id}`);
        button.className = 'if';
        if (lang.length > 0) button.setAttribute('lang', lang);
        button.value = label;
        button.setAttribute('data-state', 'inactive');
        button.appendChild(document.createTextNode(label));
        button.addEventListener('click', function() {
          // Set all buttons to inactive
          captionMenuButtons.map(button => {
            button.setAttribute('data-state', 'inactive');
            button.classList.remove('is-active');
          });
          // Find the language to activate
          var lang = this.getAttribute('lang');
          for (var i = 0; i < captionsTracks.length; i++) {
            // For the 'captions-off' button, the first condition will never match so all will captions be turned off
            if (captionsTracks[i].language == lang) {
              captionsTracks[i].mode = 'showing';
              this.setAttribute('data-state', 'active');
              button.classList.add('is-active');
            } else {
              captionsTracks[i].mode = 'hidden';
              button.classList.remove('is-active');
            }
          }
          captionsMenuHolder.classList.toggle('is-open');
          captionsMenu.classList.toggle('is-open');
        });
        captionMenuButtons.push(button);
      }
      return listItem;
    };
    var captionsMenu;
    var captionsMenuHolder;
    if (captionsTracks && captionsControl) {
      var captionsMenuHolderDF = document.createDocumentFragment();
      captionsMenuHolder = captionsMenuHolderDF.appendChild(document.createElement('div'));
      captionsMenuHolder.classList.add('if');
      captionsMenuHolder.classList.add('dropdown-menu');
      captionsMenuHolder.classList.add('bottom');
      captionsMenuHolder.classList.add('right');
      captionsMenu = captionsMenuHolder.appendChild(document.createElement('ul'));
      captionsMenu.className = 'if';
      captionsMenu.appendChild(createCaptionsMenuItem('captions-off', '', 'Off'));
      captionsMenu.appendChild(createCaptionsMenuItem('captions-separator', '', 'separator'));
      for (var u = 0; u < captionsTracks.length; u++) {
        captionsMenu.appendChild(
          createCaptionsMenuItem(
            'captions-' + captionsTracks[u].language,
            captionsTracks[u].language,
            captionsTracks[u].label
          )
        );
      }
      captionsControl.appendChild(captionsMenuHolder);
    }

    captionsButton.addEventListener('click', () => {
      if (captionsMenuHolder) {
        captionsMenuHolder.classList.toggle('is-open');
        captionsMenu.classList.toggle('is-open');
      }
    });
  }

  if (seekerRange) {
    video.addEventListener('timeupdate', () => {
      if (video.currentTime != 0 && !isNaN(video.duration)) {
        seekerRange.value = (video.currentTime / video.duration) * seekerRange.max;
        seekerElapsed.textContent = createMediaTimestamp(parseInt(video.currentTime));
        seekerRemaining.textContent = `-${createMediaTimestamp(parseInt(video.duration - video.currentTime))}`;
      }
    });

    seekerRange.addEventListener('click', function(e) {
      const clickedValue = (e.offsetX * this.max) / this.offsetWidth;

      video.currentTime = (video.duration * clickedValue) / seekerRange.max;
      seekerElapsed.textContent = createMediaTimestamp(parseInt(video.currentTime));
      seekerRemaining.textContent = `-${createMediaTimestamp(parseInt(video.duration - video.currentTime))}`;
    });
  }

  const videoWorks = !!document.createElement('video').canPlayType;
  if (videoWorks) {
    video.controls = false;
  }

  // togglePlay toggles the playback state of the video.
  // If the video playback is paused or ended, the video is played
  // otherwise, the video is paused
  function ifVideoTogglePlay() {
    if (video.paused || video.ended) {
      video.play();
      videoContainer.classList.add('is-active');
      videoContainer.classList.remove('is-paused');
    } else {
      video.pause();
      videoContainer.classList.remove('is-active');
      videoContainer.classList.add('is-paused');
    }
  }

  // updatePlayButton updates the playback icon and tooltip
  // depending on the playback state
  function onPause() {
    pauseTime = video.currentTime;
    if (seekerRange) {
      if (video.currentTime != 0 && !isNaN(video.duration)) {
        seekerRange.value = (video.currentTime / video.duration) * seekerRange.max;
        seekerElapsed.textContent = createMediaTimestamp(parseInt(video.currentTime));
        seekerRemaining.textContent = `-${createMediaTimestamp(parseInt(video.duration - video.currentTime))}`;
      }
    }
    video.load();
  }
  function onPlay() {
    video.currentTime = pauseTime;
    if (seekerRange) {
      if (video.currentTime != 0 && !isNaN(video.duration)) {
        seekerRange.value = (video.currentTime / video.duration) * seekerRange.max;
        seekerElapsed.textContent = createMediaTimestamp(parseInt(video.currentTime));
        seekerRemaining.textContent = `-${createMediaTimestamp(parseInt(video.duration - video.currentTime))}`;
      }
    }
    video.play();
  }

  function toggleFullScreen() {
    if (document.fullscreenElement) {
      document.exitFullscreen().catch(err => console.error(err));
    } else {
      video.requestFullscreen();
    }
  }
  let timer = 0;
  // Add eventlisteners here
  playButton.addEventListener('click', ifVideoTogglePlay);
  video.addEventListener('play', onPlay);
  video.addEventListener('pause', onPause);
  video.addEventListener('click', () => {
    if (event.detail === 1) {
      timer = setTimeout(() => {
        ifVideoTogglePlay();
      }, 200);
    }
  });
  video.addEventListener('dblclick', () => {
    clearTimeout(timer);
    toggleFullScreen();
  });
  // video.addEventListener('mouseenter', showControls);
  // video.addEventListener('mouseleave', hideControls);
};

let videoObserver = new IntersectionObserver((entries, videoObserver) => {
  entries.forEach((entry, entryIndex) => {
    if (entry.intersectionRatio > 0) {
      const video = entry.target;
      videoObserver.unobserve(entry.target);

      initVideo(video, entryIndex);
    }
  });
});

document.querySelectorAll('video.if.player').forEach(video => {
  videoObserver.observe(video);
});

For youtube video

<div class="if video [is-active|is-paused]">
  <div class="if yt-player" id="yt-player-2"></div>
  <div class="if overlay">
    <span class="if title">If Villaförsäkring</span>
    <span class="if description">Se hur If Villaförsäkring förändrade Anna’s vardag</span>
    <button type="button" class="if play button secondary">
      Spela up filmen
    </button>
  </div>
</div>

JavaScript implementation example

var tag = document.createElement('script');

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

let youtubeVideos = document.querySelectorAll('.if.yt-player');

let players = [];

window.onPlayerReady = function(event, player_id) {
  console.info('YT: PLAYER READY');
  const video = event.target;
  console.log(video);
  const videoContainer = document.getElementById(player_id).parentElement;
  const videoOverlay = videoContainer.querySelector('.overlay');
  const playButton = videoContainer.querySelector('.button');
  function togglePlay() {
    if (videoContainer.classList.contains('is-active')) {
      video.pauseVideo();
    } else {
      video.playVideo();
    }

    console.info('YT: TOGGLING PLAY');
  }
  playButton.addEventListener('click', togglePlay);
};

window.onYouTubeIframeAPIReady = function() {
  console.info('YT: onYouTubeIframeAPIReady');
  players = [];
  // 5. The API calls this function when the player's state changes.
  //    The function indicates that when playing a video (state=1),
  //    the player should play for six seconds and then stop.
  var done = false;
  function onPlayerStateChange(event, player_id) {
    if (event.data == YT.PlayerState.PLAYING) {
      playVideo(event, player_id);
      console.info('YT: PLAYING');
    }
    if (event.data == YT.PlayerState.PAUSED) {
      pauseVideo(event, player_id);
      console.info('YT: PAUSED');
    }
    if (event.data == YT.PlayerState.ENDED && !done) {
      stopVideo(event, player_id);
      console.info('YT: ENDED');
      done = true;
    }
  }
  function playVideo(event, player_id) {
    const videoContainer = document.getElementById(player_id).parentElement;
    videoContainer.classList.add('is-active');
    videoContainer.classList.remove('is-paused');
    console.info('YT: PLAY VIDEO CALLED');
  }
  function pauseVideo(event, player_id) {
    const videoContainer = document.getElementById(player_id).parentElement;
    videoContainer.classList.remove('is-active');
    videoContainer.classList.add('is-paused');
    console.info('YT: PAUSE VIDEO CALLED');
  }
  function stopVideo(event, player_id) {
    const video = event.target;
    const videoContainer = document.getElementById(player_id).parentElement;
    video.stopVideo();
    videoContainer.classList.remove('is-active');
    videoContainer.classList.remove('is-paused');
    console.info('YT: STOP VIDEO CALLED');
  }

  youtubeVideos.forEach(player => {
    players.push(
      new YT.Player(player.id, {
        videoId: '-5V0RWaERSU',
        playerVars: {
          controls: 0,
          showinfo: 0,
          ecver: 2,
          rel: 0,
          iv_load_policy: 3,
          autoplay: 0,
          loop: 0
        },
        events: {
          onReady: e => {
            window.onPlayerReady(e, player.id);
          },
          onStateChange: e => {
            onPlayerStateChange(e, player.id);
          }
        }
      })
    );
  });
};
Edit this section

Anatomy

If Villaförsäkring Se hur If Villaförsäkring förändrade Anna’s vardag
Video
  1. Title
  2. Description
  3. Play button
Edit this section

Contact us