From b7487b8b4f4b9f6a230cdfc3de327473c5e900b8 Mon Sep 17 00:00:00 2001 From: Hakim El Hattab Date: Tue, 26 May 2020 10:45:05 +0200 Subject: polyfill element.closest for ie11 --- js/controllers/autoanimate.js | 10 +++++----- js/controllers/focus.js | 4 +++- js/controllers/slidecontent.js | 20 ++++++++++---------- js/reveal.js | 2 +- js/utils/util.js | 27 ++++++++++++--------------- 5 files changed, 31 insertions(+), 32 deletions(-) (limited to 'js') diff --git a/js/controllers/autoanimate.js b/js/controllers/autoanimate.js index 01711a2..22f7e7c 100644 --- a/js/controllers/autoanimate.js +++ b/js/controllers/autoanimate.js @@ -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' // Counter used to generate unique IDs for auto-animated elements @@ -299,8 +299,8 @@ export default class AutoAnimate { options = extend( options, inheritedOptions ); // Inherit options from parent elements - if( element.closest && element.parentNode ) { - let autoAnimatedParent = element.parentNode.closest( '[data-auto-animate-target]' ); + if( element.parentNode ) { + let autoAnimatedParent = closest( element.parentNode, '[data-auto-animate-target]' ); if( autoAnimatedParent ) { options = this.getAutoAnimateOptions( autoAnimatedParent, options ); } @@ -463,11 +463,11 @@ export default class AutoAnimate { // Disable scale transformations on text nodes, we transiition // each individual text property instead - if( matchesSelector( pair.from, textNodes ) ) { + if( matches( pair.from, textNodes ) ) { pair.options = { scale: false }; } // 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 // to prevent its content from being squished diff --git a/js/controllers/focus.js b/js/controllers/focus.js index 8e2d8fa..2191807 100644 --- a/js/controllers/focus.js +++ b/js/controllers/focus.js @@ -1,3 +1,5 @@ +import { closest } from '../utils/util.js' + /** * Manages focus when a presentation is embedded. This * helps us only capture keyboard from the presentation @@ -85,7 +87,7 @@ export default class Focus { onDocumentPointerDown( event ) { - let revealElement = event.target.closest( '.reveal' ); + let revealElement = closest( event.target, '.reveal' ); if( !revealElement || revealElement !== this.Reveal.getRevealElement() ) { this.blur(); } diff --git a/js/controllers/slidecontent.js b/js/controllers/slidecontent.js index 2ed2a47..7ed5573 100644 --- a/js/controllers/slidecontent.js +++ b/js/controllers/slidecontent.js @@ -1,5 +1,5 @@ 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' /** @@ -240,7 +240,7 @@ export default class SlideContent { // HTML5 media elements queryAll( element, 'video, audio' ).forEach( el => { - if( closestParent( el, '.fragment' ) && !closestParent( el, '.fragment.visible' ) ) { + if( closest( el, '.fragment' ) && !closest( el, '.fragment.visible' ) ) { return; } @@ -250,7 +250,7 @@ export default class SlideContent { // If no global setting is available, fall back on the element's // own autoplay setting 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' ) { @@ -288,7 +288,7 @@ export default class SlideContent { // Normal iframes queryAll( element, 'iframe[src]' ).forEach( el => { - if( closestParent( el, '.fragment' ) && !closestParent( el, '.fragment.visible' ) ) { + if( closest( el, '.fragment' ) && !closest( el, '.fragment.visible' ) ) { return; } @@ -297,7 +297,7 @@ export default class SlideContent { // Lazy loading iframes queryAll( element, 'iframe[data-src]' ).forEach( el => { - if( closestParent( el, '.fragment' ) && !closestParent( el, '.fragment.visible' ) ) { + if( closest( el, '.fragment' ) && !closest( el, '.fragment.visible' ) ) { return; } @@ -320,8 +320,8 @@ export default class SlideContent { */ startEmbeddedMedia( event ) { - let isAttachedToDOM = !!closestParent( event.target, 'html' ), - isVisible = !!closestParent( event.target, '.present' ); + let isAttachedToDOM = !!closest( event.target, 'html' ), + isVisible = !!closest( event.target, '.present' ); if( isAttachedToDOM && isVisible ) { event.target.currentTime = 0; @@ -344,8 +344,8 @@ export default class SlideContent { if( iframe && iframe.contentWindow ) { - let isAttachedToDOM = !!closestParent( event.target, 'html' ), - isVisible = !!closestParent( event.target, '.present' ); + let isAttachedToDOM = !!closest( event.target, 'html' ), + isVisible = !!closest( event.target, '.present' ); if( isAttachedToDOM && isVisible ) { @@ -355,7 +355,7 @@ export default class SlideContent { // If no global setting is available, fall back on the element's // own autoplay setting 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 diff --git a/js/reveal.js b/js/reveal.js index 7c3c7f9..4807369 100644 --- a/js/reveal.js +++ b/js/reveal.js @@ -148,7 +148,7 @@ export default function( revealElement, options ) { // Embedded decks use the reveal element as their viewport 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 else { diff --git a/js/utils/util.js b/js/utils/util.js index 392ca72..68ff085 100644 --- a/js/utils/util.js +++ b/js/utils/util.js @@ -94,14 +94,10 @@ export const transformElement = ( element, transform ) => { * * @return {Boolean} */ -export const matchesSelector = ( target, selector ) => { +export const matches = ( target, selector ) => { - // There's some overhead doing this each time, we don't - // 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; + let matchesMethod = target.matches || target.matchesSelector || target.msMatchesSelector; - // If we find a match, we're all set return !!( matchesMethod && matchesMethod.call( target, selector ) ); } @@ -117,20 +113,21 @@ export const matchesSelector = ( target, selector ) => { * @return {HTMLElement} The matched parent or null * if no matching parent was found */ -export const closestParent = ( target, selector ) => { +export const closest = ( target, selector ) => { - let parent = target.parentNode; - - while( parent ) { + // Native Element.closest + if( typeof target.closest === 'function' ) { + return target.closest( selector ); + } - // If we find a match, we're all set - if( matchesSelector( parent, selector ) ) { - return parent; + // Polyfill + while( target ) { + if( matches( target, selector ) ) { + return target; } // Keep searching - parent = parent.parentNode; - + target = target.parentNode; } return null; -- cgit v1.2.3