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.
This commit is contained in:
Max Rothman 2019-03-19 22:28:30 -04:00
parent c7db11d9cf
commit 93b1abc736
2 changed files with 68 additions and 7 deletions

View file

@ -316,6 +316,13 @@ Reveal.initialize({
// - false: No media will autoplay, regardless of individual setting // - false: No media will autoplay, regardless of individual setting
autoPlayMedia: null, 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 // Number of milliseconds between automatically proceeding to the
// next slide, disabled when set to 0, this value can be overwritten // next slide, disabled when set to 0, this value can be overwritten
// by using a data-autoslide attribute on your slides // 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 #### 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. 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. 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
<section>
<iframe data-src="http://hakim.se" data-preload></iframe>
</section>
```
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 ### API

View file

@ -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 * Called when the given slide is within the configured view
* distance. Shows the slide element and loads any content * distance. Shows the slide element and loads any content
@ -3686,10 +3706,20 @@
slide.style.display = config.display; slide.style.display = config.display;
// Media elements with data-src attributes // Media elements with data-src attributes
toArray( slide.querySelectorAll( 'img[data-src], video[data-src], audio[data-src]' ) ).forEach( function( element ) { toArray( slide.querySelectorAll( 'img[data-src], video[data-src], audio[data-src], iframe[data-src]' ) ).forEach( function( element ) {
element.setAttribute( 'src', element.getAttribute( 'data-src' ) ); var load = function( el ) {
element.setAttribute( 'data-lazy-loaded', '' ); el.setAttribute( 'src', el.getAttribute( 'data-src' ) );
element.removeAttribute( '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 <source> children // Media elements with <source> children
@ -3807,9 +3837,19 @@
} }
// Reset lazy-loaded media elements with src attributes // Reset lazy-loaded media elements with src attributes
toArray( slide.querySelectorAll( 'video[data-lazy-loaded][src], audio[data-lazy-loaded][src]' ) ).forEach( function( element ) { toArray( slide.querySelectorAll( 'video[data-lazy-loaded][src], audio[data-lazy-loaded][src], iframe[data-lazy-loaded][src]' ) ).forEach( function( element ) {
element.setAttribute( 'data-src', element.getAttribute( 'src' ) ); var unload = function( el ) {
element.removeAttribute( 'src' ); 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 <source> children // Reset lazy-loaded media elements with <source> children