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,42 kByte) 21.08.2023 07:33
<!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="/code/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="/code/video-js/video.js"></script>
<!-- Unless using the CDN hosted version, update the URL to the Flash SWF -->
    <script>
      videojs.options.flash.swf = "/code/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="/media/video/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>

Kontakt

Udo Schmal
Udo Schmal

Udo Schmal
Softwareentwickler
Ellerndiek 26
24837 Schleswig
Schleswig-Holstein
Germany




+49 4621 9785538
+49 1575 0663676
+49 4621 9785539
SMS
WhatsApp

Google Maps Profile
Instagram Profile
vCard 2.1, vCard 3.0, vCard 4.0

Service Infos

CMS Info

Product Name:
UDOs Webserver
Version:
0.5.1.81
Description:
All in one Webserver
Copyright:
Udo Schmal
Compilation:
Mon, 15. Apr 2024 18:45:24

Development Info

Compiler:
Free Pascal FPC 3.3.1
compiled for:
OS:Linux, CPU:x86_64

System Info

OS:
Ubuntu 22.04.4 LTS (Jammy Jellyfish)

Hardware Info

Model:
Hewlett-Packard HP Pavilion dm4 Notebook PC
CPU Name:
Intel(R) Core(TM) i5-2430M CPU @ 2.40GHz
CPU Type:
x86_64, 1 physical CPU(s), 2 Core(s), 4 logical CPU(s),  MHz