Lazy Loading Videos und der Viewport - Vanilla JS

Lazy loading von Videos wird leider noch nicht nativ von den Browsern unterstützt und so realisiert man es halt genau so wie es früher auch für Bilder notwendig war, oder vielmehr wie es für ätere Browser noch als Fallback eingesetzt wird.

HTML:
video_lazy.htm HTML (977 bytes) 18.04.2021 12:23
<!DOCTYPE html >
<html lang="de">
  <head>
    <meta charset="utf-8" />
    <title>Video Lazy Loading Example</title>
  </head>
  <body>
    <video controls preload="none" data-loading="lazy" data-autoplay="true" data-poster="/media/video/sammy.jpg" title="Sammy geht baden" data-description="Ein Beispiel-Video zur Demonstration der Einbindung." data-uploaddate="30.12.2018" width="100%">
      <source type="video/mp4" src="/media/video/sammy-426x240.mp4" />
      <source type="video/mp4" data-src="/media/video/sammy-640x360.mp4" data-media="(min-width: 640px)" />
      <source type="video/mp4" data-src="/media/video/sammy-854x480.mp4" data-media="(min-width: 854px)" />
      <source type="video/mp4" data-src="/media/video/sammy-1280x720.mp4" data-media="(min-width: 1280px)" />
      <source type="video/mp4" data-src="/media/video/sammy-1920x1080.mp4" data-media="(min-width: 1920px)" />
    </video>
    <script src="video_lazy.js"></script>
  </body>
</html>
JavaScript:
video_lazy.js JavaScript (4,48 kByte) 08.04.2021 23:59
/** Created by: Udo Schmal | https://www.gocher.me/ */
(function() {
  'use strict';
  // lazy loading video
  function loadVideo(el) {
    if (el.dataset.poster) {
      el.setAttribute('poster', el.dataset.poster);
      el.removeAttribute('data-poster');
    }
    var childs = el.children;
    for(var i = 0; i < childs.length; i++) {
      if ((childs[i].tagName.toLowerCase() == 'source') && childs[i].dataset.src) {
        childs[i].src = childs[i].dataset.src;
        childs[i].removeAttribute('data-src');
      }
    }
    if (el.dataset.controls && el.dataset.controls == 'true') {
      el.setAttribute('controls', '');
      el.removeAttribute('data-controls');
    }
    el.setAttribute('preload', 'auto');
    el.load();
    if (el.dataset.autoplay && el.dataset.autoplay == 'true') {
      el.removeAttribute('data-autoplay');
      el.setAttribute('autoplay', 'autoplay');
      el.autoplay = true;
      el.play();
    }
  }
  // Viewport prototype to trigger event if element moves into viewport the fist time
  function Viewport(els, callback) {
    this.els = els;
    this.callback = callback;
    this.active = false;
    var self = this;
    var hidden = [];
    function alreadyObserved(el) {
      for(var i=0; i<hidden.length; i++) {
        if (el === hidden[i]) {
          return true;
        }
      }
      return false;
    }
    for (var i=0; i < els.length; i++) {
      var el = els[i];
      // parent must have position attribute for getBoundingClientRect
      let properties = window.getComputedStyle(el.parentNode);
      if (!properties.getPropertyValue('position')) {
        el.parentNode.style.position = 'relative';
        while(el && el.tagName.toLowerCase() !== 'body') {
          if (window.getComputedStyle(el).display === "none") {
            if (!alreadyObserved(el)) {
              hidden.push(el);
              var observer = new MutationObserver(function(mutations) {
                if (mutations[0].target.style.display !== 'none') {
                  self.handleEvent();
                }
              });
              observer.observe(el, { attributes: true });
              break;
            }
          }
          el = el.parentNode;
        }
      }
    }
    this.handleEvent.bind(this);
    // is already in viewport after dom ready
    function ready(f){
      /complete|loaded/i.test(document.readyState) ? f() : setTimeout(function(){ready(f);},9);
    }
    ready(function(){self.handleEvent();});
    // add event listener to scroll event to check visibility change
    document.addEventListener('scroll', this, true);
    window.addEventListener('resize', this, true);
    window.addEventListener('orientationchange', this, true);

  }

  Viewport.prototype = {
    // check if element is visible
    isVisible: function (el) {
      var style = window.getComputedStyle(el);
      return !!(el.offsetWidth || el.offsetHeight || el.getClientRects().length);
    },
    // check if element is in viewport
    isInViewport: function (el) {
      var bounding = el.getBoundingClientRect();
      return (
        bounding.bottom >= 0 &&
        bounding.right >= 0 &&
        bounding.top <= (window.innerHeight || document.documentElement.clientHeight) &&
        bounding.left <= (window.innerWidth || document.documentElement.clientWidth)
      );
    },
    // handle the visibility check and the resulting action
    handleEvent: function () {
      if (this.active === false) {
        this.active = true;
        for (var i=0; i < this.els.length; i++) {
          if (this.isInViewport(this.els[i]) && this.isVisible(this.els[i])) {
            this.callback(this.els[i]);
          }
        }
        this.active = false;
      }
    }
  };

  // activate viewport for video loading
  var videos = document.querySelectorAll("video[data-loading='lazy']");
  if (videos.length > 0) {
    if ("IntersectionObserver" in window) {
      // create intersection observer
      let lazyobserver = new IntersectionObserver(function(entries, observer) {
        for (var i=0; i < entries.length; i++) {
          if (entries[i].isIntersecting) {
            loadVideo(entries[i].target);
            lazyobserver.unobserve(entries[i].target);
          }
        }
      },
      {
        threshold: [0.1],
        // Set a minimum delay between notifications
        delay: 100
      });
      // start observing
      for (var i=0; i < videos.length; i++) {
        lazyobserver.observe(videos[i]);
      }
    } else {
      // use eventListeners
      new Viewport(videos, loadVideo);
    }
  }
})();

Video.js - HTML5 Video Player

Hier nun eine simple Implementierung des HTML5 Video-Tags mit Flash Fallback. Mehr Infos unter https://videojs.com/.

Damit das Video auch auf dem iPad läuft müss auf die richtigen Einstellungen beim Encoden geachtet werden:

  • H.264 Baseline Profile Level 3.0
  • Auflösung unter 640 x 480 und Frameraten bis 30 Frames/Sec
  • keine B Frames
  • Bitraten bis 900kb
video.htm HTML (1,44 kByte) 21.02.2021 00:35
<!DOCTYPE html >
<html>
  <head>
    <title>Video.js | HTML5 Video Player</title>
    <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: " />
<!-- Chang URLs to wherever Video.js files will be hosted -->
    <link href="/scripts/video-js/video-js.css" rel="stylesheet" type="text/css" />
<!-- video.js must be in the html document head for older IEs to work. -->
    <script src="/scripts/video-js/video.js"></script>
<!-- Unless using the CDN hosted version, update the URL to the Flash SWF -->
    <script>
      videojs.options.flash.swf = "/scripts/video-js/video-js.swf";
    </script>
  </head>
  <body>
    <video id="example_video_1" class="video-js vjs-default-skin vjs-big-play-centered" controls preload="auto" width="640" height="360" poster="/media/video/sammy.png" data-setup="{}">
      <source src="/media/video/sammy.mp4" type="video/mp4" />
      <source src="/media/video/sammy.webm" type="video/webm" />
      <source src="/media/video/sammy.ogv" type="video/ogg" />
      <track kind="captions" src="/scripts/video-js/sammy.de.vtt" srclang="de" label="Deutsch" />
      <p class="vjs-no-js">To view this video please enable JavaScript, and consider upgrading to a web browser that <a href="http://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a></p>
    </video>
  </body>
</html>

Author: , published: , last modified:

Kontakt

Udo Schmal

Udo Schmal
Softwareentwickler
Olvengraben 41
47608 Geldern
Nordrhein-Westfalen
Germany





+49 2831 9776557
+49 1575 0663676
+49 2831 1328709
SMS
WhatsApp

Instagram Profile
vCard 3.0

Service Infos

CMS Info Product Name:
UDOs Webserver
Version:
0.5.0.68
Description:
All in one Webserver
Copyright:
Udo Schmal
Compilation:
Tue, 4. May 2021 23:15:05
Development Info Compiler:
Free Pascal FPC 3.3.1
compiled for:
OS:Linux, CPU:x86_64
System Info OS:
Ubuntu 20.04.2 LTS (Focal Fossa)
Hardware Info Model:
Hewlett-Packard HP Pavilion dv7 Notebook PC
CPU Name:
Intel(R) Core(TM) i5-2450M CPU @ 2.50GHz
CPU Type:
x86_64, 1 physical CPU(s), 2 Core(s), 4 logical CPU(s), 1607.994 MHz