From 9553c8daac73f06af704a87ace7294f751a77b8f Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Sat, 23 May 2020 11:38:25 +0100 Subject: Add lazy image loading on list items. --- web/js/loading-attribute-polyfill.js | 213 +++++++++++++++++++++++++++++++++++ 1 file changed, 213 insertions(+) create mode 100644 web/js/loading-attribute-polyfill.js (limited to 'web/js/loading-attribute-polyfill.js') diff --git a/web/js/loading-attribute-polyfill.js b/web/js/loading-attribute-polyfill.js new file mode 100644 index 000000000..f11397985 --- /dev/null +++ b/web/js/loading-attribute-polyfill.js @@ -0,0 +1,213 @@ +/* + * Loading attribute polyfill - https://github.com/mfranzke/loading-attribute-polyfill + * @license Copyright(c) 2019 by Maximilian Franzke + * Credits for the initial kickstarter / script to @Sora2455, and supported by @cbirdsong, @eklingen, @DaPo, @nextgenthemes, @diogoterremoto, @dracos, @Flimm, @TomS- and @vinyfc93 - many thanks for that ! + */ +/* + * A minimal and dependency-free vanilla JavaScript loading attribute polyfill. + * Supports standard's functionality and tests for native support upfront. + * Elsewhere the functionality gets emulated with the support of noscript wrapper tags. + * Use an IntersectionObserver polyfill in case of IE11 support necessary. + * + * MS - Removed iframe/picture/srcset parts, unneeded at present, and added external API + */ + +(function () { + 'use strict'; + + var config = { + // Start download if the item gets within 256px in the Y axis + rootMargin: '256px 0px', + threshold: 0.01 + }; + + // Device/browser capabilities object + var capabilities = { + loading: 'loading' in HTMLImageElement.prototype, + scrolling: 'onscroll' in window + }; + + // Nodelist foreach polyfill / source: https://stackoverflow.com/a/46929259 + if ( + typeof NodeList !== 'undefined' && + NodeList.prototype && + !NodeList.prototype.forEach + ) { + // Yes, there's really no need for `Object.defineProperty` here + NodeList.prototype.forEach = Array.prototype.forEach; + } + + // Define according to browsers support of the IntersectionObserver feature (missing e.g. on IE11 or Safari 11) + var intersectionObserver; + + if ('IntersectionObserver' in window) { + intersectionObserver = new IntersectionObserver(onIntersection, config); + } + + // On using a browser w/o requestAnimationFrame support (IE9, Opera Mini), just run the passed function + var rAFWrapper; + + if ('requestAnimationFrame' in window) { + rAFWrapper = window.requestAnimationFrame; + } else { + rAFWrapper = function (func) { + func(); + }; + } + + /** + * Put the source back where it belongs - now that the elements content is attached to the document, it will load now + * @param {Object} lazyItem Current item to be restored after lazy loading. + */ + function restoreSource(lazyItem) { + lazyItem.setAttribute('src', lazyItem.getAttribute('data-lazy-src')); + lazyItem.removeAttribute('data-lazy-src'); // Not using delete .dataset here for compatibility down to IE9 + } + + /** + * Handle IntersectionObservers callback + * @param {Object} entries Target elements Intersection observed changes + * @param {Object} observer IntersectionObserver instance reference + */ + function onIntersection(entries, observer) { + entries.forEach(function (entry) { + // Mitigation for EDGE lacking support of .isIntersecting until v15, compare to e.g. https://github.com/w3c/IntersectionObserver/issues/211#issuecomment-309144669 + if (entry.intersectionRatio === 0) { + return; + } + + // If the item is visible now, load it and stop watching it + var lazyItem = entry.target; + + observer.unobserve(lazyItem); + + restoreSource(lazyItem); + }); + } + + /** + * Handle printing the page + */ + function onPrinting() { + if (typeof window.matchMedia === 'undefined') { + return; + } + + var mediaQueryList = window.matchMedia('print'); + + mediaQueryList.addListener(function (mql) { + if (mql.matches) { + document + .querySelectorAll('img[loading="lazy"][data-lazy-src]') + .forEach(function (lazyItem) { + restoreSource(lazyItem); + }); + } + }); + } + + /** + * Get and prepare the HTML code depending on feature detection, + * and if not scrolling supported, because it's a Google or Bing Bot + * @param {String} lazyAreaHtml Noscript inner HTML code that src-urls need to get rewritten + */ + function getAndPrepareHTMLCode(noScriptTag) { + // The contents of a