From 93b1abc7364eacb028c30f145603d0a155f2fcbd Mon Sep 17 00:00:00 2001 From: Max Rothman Date: Tue, 19 Mar 2019 22:28:30 -0400 Subject: [PATCH] Add data-preload attribute for iframes Allows lazy-loaded (i.e. data-src) iframes to be preloaded when they come within the viewDistance, rather than once they're visible. --- README.md | 21 ++++++++++++++++++++ js/reveal.js | 54 +++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 68 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 4c720e2..20b07f9 100644 --- a/README.md +++ b/README.md @@ -316,6 +316,13 @@ Reveal.initialize({ // - false: No media will autoplay, regardless of individual setting autoPlayMedia: null, + // Global override for preloading lazy-loaded iframes + // - null: Iframes with data-src AND data-preload will be loaded when within + // the viewDistance, iframes with only data-src will be loaded when visible + // - true: All iframes with data-src will be loaded when within the viewDistance + // - false: All iframes with data-src will be loaded only when visible + preloadIframes: null, + // Number of milliseconds between automatically proceeding to the // next slide, disabled when set to 0, this value can be overwritten // by using a data-autoslide attribute on your slides @@ -555,10 +562,24 @@ To enable lazy loading all you need to do is change your `src` attributes to `da ``` #### Lazy Loading Iframes + Note that lazy loaded iframes ignore the `viewDistance` configuration and will only load when their containing slide becomes visible. Iframes are also unloaded as soon as the slide is hidden. When we lazy load a video or audio element, reveal.js won't start playing that content until the slide becomes visible. However there is no way to control this for an iframe since that could contain any kind of content. That means if we loaded an iframe before the slide is visible on screen it could begin playing media and sound in the background. +You can override this behavior with the `data-preload` attribute. The iframe below will be loaded +according to the `viewDistance`. + +```html +
+ +
+``` + +You can also change the default globally with the `preloadIframes` configuration option. If set to +`true` ALL iframes with a `data-src` attribute will be preloaded when within the `viewDistance` +regardless of individual `data-preload` attributes. If set to `false`, all iframes will only be +loaded when they become visible. ### API diff --git a/js/reveal.js b/js/reveal.js index 387077a..662679b 100644 --- a/js/reveal.js +++ b/js/reveal.js @@ -3671,6 +3671,26 @@ } + /** + * Should the given element be preloaded? + * Decides based on local element attributes and global config. + * + * @param {HTMLElement} element + */ + function shouldPreload(element) { + + // Prefer an explicit global preload setting + var preload = config.preloadIframes; + + // If no global setting is available, fall back on the element's + // own preload setting + if( typeof preload !== 'boolean' ) { + preload = element.hasAttribute( 'data-preload' ); + } + + return preload; + } + /** * Called when the given slide is within the configured view * distance. Shows the slide element and loads any content @@ -3686,10 +3706,20 @@ slide.style.display = config.display; // Media elements with data-src attributes - toArray( slide.querySelectorAll( 'img[data-src], video[data-src], audio[data-src]' ) ).forEach( function( element ) { - element.setAttribute( 'src', element.getAttribute( 'data-src' ) ); - element.setAttribute( 'data-lazy-loaded', '' ); - element.removeAttribute( 'data-src' ); + toArray( slide.querySelectorAll( 'img[data-src], video[data-src], audio[data-src], iframe[data-src]' ) ).forEach( function( element ) { + var load = function( el ) { + el.setAttribute( 'src', el.getAttribute( 'data-src' ) ); + el.setAttribute( 'data-lazy-loaded', '' ); + el.removeAttribute( 'data-src' ); + }; + + if( element.tagName === 'IFRAME') { + if( shouldPreload(element) ) { + load(element); + } + } else { + load(element); + } } ); // Media elements with children @@ -3807,9 +3837,19 @@ } // Reset lazy-loaded media elements with src attributes - toArray( slide.querySelectorAll( 'video[data-lazy-loaded][src], audio[data-lazy-loaded][src]' ) ).forEach( function( element ) { - element.setAttribute( 'data-src', element.getAttribute( 'src' ) ); - element.removeAttribute( 'src' ); + toArray( slide.querySelectorAll( 'video[data-lazy-loaded][src], audio[data-lazy-loaded][src], iframe[data-lazy-loaded][src]' ) ).forEach( function( element ) { + var unload = function( el ) { + el.setAttribute( 'data-src', el.getAttribute( 'src' ) ); + el.removeAttribute( 'src' ); + }; + + if( element.tagName === 'IFRAME' ) { + if( shouldPreload(element) ) { + unload(element); + } + } else { + unload(element); + } } ); // Reset lazy-loaded media elements with children