polyfill element.closest for ie11

This commit is contained in:
Hakim El Hattab 2020-05-26 10:45:05 +02:00
parent e6244a57b5
commit b7487b8b4f
10 changed files with 45 additions and 34 deletions

2
dist/reveal.esm.js vendored

File diff suppressed because one or more lines are too long

2
dist/reveal.js vendored

File diff suppressed because one or more lines are too long

View file

@ -36,6 +36,18 @@
<audio src="assets/beeping.wav" data-autoplay></audio> <audio src="assets/beeping.wav" data-autoplay></audio>
</section> </section>
<section>
<h2>Audio inside slide fragments</h2>
<div class="fragment">
Beep 1
<audio src="assets/beeping.wav" data-autoplay></audio>
</div>
<div class="fragment">
Beep 2
<audio src="assets/beeping.wav" data-autoplay></audio>
</div>
</section>
<section> <section>
<h2>Audio with controls</h2> <h2>Audio with controls</h2>
<audio src="assets/beeping.wav" controls></audio> <audio src="assets/beeping.wav" controls></audio>

View file

@ -1,4 +1,4 @@
import { queryAll, extend, createStyleSheet, matchesSelector } from '../utils/util.js' import { queryAll, extend, createStyleSheet, matches, closest } from '../utils/util.js'
import { FRAGMENT_STYLE_REGEX } from '../utils/constants.js' import { FRAGMENT_STYLE_REGEX } from '../utils/constants.js'
// Counter used to generate unique IDs for auto-animated elements // Counter used to generate unique IDs for auto-animated elements
@ -299,8 +299,8 @@ export default class AutoAnimate {
options = extend( options, inheritedOptions ); options = extend( options, inheritedOptions );
// Inherit options from parent elements // Inherit options from parent elements
if( element.closest && element.parentNode ) { if( element.parentNode ) {
let autoAnimatedParent = element.parentNode.closest( '[data-auto-animate-target]' ); let autoAnimatedParent = closest( element.parentNode, '[data-auto-animate-target]' );
if( autoAnimatedParent ) { if( autoAnimatedParent ) {
options = this.getAutoAnimateOptions( autoAnimatedParent, options ); options = this.getAutoAnimateOptions( autoAnimatedParent, options );
} }
@ -463,11 +463,11 @@ export default class AutoAnimate {
// Disable scale transformations on text nodes, we transiition // Disable scale transformations on text nodes, we transiition
// each individual text property instead // each individual text property instead
if( matchesSelector( pair.from, textNodes ) ) { if( matches( pair.from, textNodes ) ) {
pair.options = { scale: false }; pair.options = { scale: false };
} }
// Animate individual lines of code // Animate individual lines of code
else if( matchesSelector( pair.from, codeNodes ) ) { else if( matches( pair.from, codeNodes ) ) {
// Transition the code block's width and height instead of scaling // Transition the code block's width and height instead of scaling
// to prevent its content from being squished // to prevent its content from being squished

View file

@ -1,3 +1,5 @@
import { closest } from '../utils/util.js'
/** /**
* Manages focus when a presentation is embedded. This * Manages focus when a presentation is embedded. This
* helps us only capture keyboard from the presentation * helps us only capture keyboard from the presentation
@ -85,7 +87,7 @@ export default class Focus {
onDocumentPointerDown( event ) { onDocumentPointerDown( event ) {
let revealElement = event.target.closest( '.reveal' ); let revealElement = closest( event.target, '.reveal' );
if( !revealElement || revealElement !== this.Reveal.getRevealElement() ) { if( !revealElement || revealElement !== this.Reveal.getRevealElement() ) {
this.blur(); this.blur();
} }

View file

@ -1,5 +1,5 @@
import { HORIZONTAL_SLIDES_SELECTOR, VERTICAL_SLIDES_SELECTOR } from '../utils/constants.js' import { HORIZONTAL_SLIDES_SELECTOR, VERTICAL_SLIDES_SELECTOR } from '../utils/constants.js'
import { extend, queryAll, closestParent } from '../utils/util.js' import { extend, queryAll, closest } from '../utils/util.js'
import { isMobile } from '../utils/device.js' import { isMobile } from '../utils/device.js'
/** /**
@ -240,7 +240,7 @@ export default class SlideContent {
// HTML5 media elements // HTML5 media elements
queryAll( element, 'video, audio' ).forEach( el => { queryAll( element, 'video, audio' ).forEach( el => {
if( closestParent( el, '.fragment' ) && !closestParent( el, '.fragment.visible' ) ) { if( closest( el, '.fragment' ) && !closest( el, '.fragment.visible' ) ) {
return; return;
} }
@ -250,7 +250,7 @@ export default class SlideContent {
// If no global setting is available, fall back on the element's // If no global setting is available, fall back on the element's
// own autoplay setting // own autoplay setting
if( typeof autoplay !== 'boolean' ) { if( typeof autoplay !== 'boolean' ) {
autoplay = el.hasAttribute( 'data-autoplay' ) || !!closestParent( el, '.slide-background' ); autoplay = el.hasAttribute( 'data-autoplay' ) || !!closest( el, '.slide-background' );
} }
if( autoplay && typeof el.play === 'function' ) { if( autoplay && typeof el.play === 'function' ) {
@ -288,7 +288,7 @@ export default class SlideContent {
// Normal iframes // Normal iframes
queryAll( element, 'iframe[src]' ).forEach( el => { queryAll( element, 'iframe[src]' ).forEach( el => {
if( closestParent( el, '.fragment' ) && !closestParent( el, '.fragment.visible' ) ) { if( closest( el, '.fragment' ) && !closest( el, '.fragment.visible' ) ) {
return; return;
} }
@ -297,7 +297,7 @@ export default class SlideContent {
// Lazy loading iframes // Lazy loading iframes
queryAll( element, 'iframe[data-src]' ).forEach( el => { queryAll( element, 'iframe[data-src]' ).forEach( el => {
if( closestParent( el, '.fragment' ) && !closestParent( el, '.fragment.visible' ) ) { if( closest( el, '.fragment' ) && !closest( el, '.fragment.visible' ) ) {
return; return;
} }
@ -320,8 +320,8 @@ export default class SlideContent {
*/ */
startEmbeddedMedia( event ) { startEmbeddedMedia( event ) {
let isAttachedToDOM = !!closestParent( event.target, 'html' ), let isAttachedToDOM = !!closest( event.target, 'html' ),
isVisible = !!closestParent( event.target, '.present' ); isVisible = !!closest( event.target, '.present' );
if( isAttachedToDOM && isVisible ) { if( isAttachedToDOM && isVisible ) {
event.target.currentTime = 0; event.target.currentTime = 0;
@ -344,8 +344,8 @@ export default class SlideContent {
if( iframe && iframe.contentWindow ) { if( iframe && iframe.contentWindow ) {
let isAttachedToDOM = !!closestParent( event.target, 'html' ), let isAttachedToDOM = !!closest( event.target, 'html' ),
isVisible = !!closestParent( event.target, '.present' ); isVisible = !!closest( event.target, '.present' );
if( isAttachedToDOM && isVisible ) { if( isAttachedToDOM && isVisible ) {
@ -355,7 +355,7 @@ export default class SlideContent {
// If no global setting is available, fall back on the element's // If no global setting is available, fall back on the element's
// own autoplay setting // own autoplay setting
if( typeof autoplay !== 'boolean' ) { if( typeof autoplay !== 'boolean' ) {
autoplay = iframe.hasAttribute( 'data-autoplay' ) || !!closestParent( iframe, '.slide-background' ); autoplay = iframe.hasAttribute( 'data-autoplay' ) || !!closest( iframe, '.slide-background' );
} }
// YouTube postMessage API // YouTube postMessage API

View file

@ -148,7 +148,7 @@ export default function( revealElement, options ) {
// Embedded decks use the reveal element as their viewport // Embedded decks use the reveal element as their viewport
if( config.embedded === true ) { if( config.embedded === true ) {
dom.viewport = revealElement.closest( '.reveal-viewport' ) || revealElement; dom.viewport = Util.closest( revealElement, '.reveal-viewport' ) || revealElement;
} }
// Full-page decks use the body as their viewport // Full-page decks use the body as their viewport
else { else {

View file

@ -94,14 +94,10 @@ export const transformElement = ( element, transform ) => {
* *
* @return {Boolean} * @return {Boolean}
*/ */
export const matchesSelector = ( target, selector ) => { export const matches = ( target, selector ) => {
// There's some overhead doing this each time, we don't let matchesMethod = target.matches || target.matchesSelector || target.msMatchesSelector;
// want to rewrite the element prototype but should still
// be enough to feature detect once at startup...
let matchesMethod = parent.matches || parent.matchesSelector || parent.msMatchesSelector;
// If we find a match, we're all set
return !!( matchesMethod && matchesMethod.call( target, selector ) ); return !!( matchesMethod && matchesMethod.call( target, selector ) );
} }
@ -117,20 +113,21 @@ export const matchesSelector = ( target, selector ) => {
* @return {HTMLElement} The matched parent or null * @return {HTMLElement} The matched parent or null
* if no matching parent was found * if no matching parent was found
*/ */
export const closestParent = ( target, selector ) => { export const closest = ( target, selector ) => {
let parent = target.parentNode; // Native Element.closest
if( typeof target.closest === 'function' ) {
return target.closest( selector );
}
while( parent ) { // Polyfill
while( target ) {
// If we find a match, we're all set if( matches( target, selector ) ) {
if( matchesSelector( parent, selector ) ) { return target;
return parent;
} }
// Keep searching // Keep searching
parent = parent.parentNode; target = target.parentNode;
} }
return null; return null;