Created
January 24, 2022 14:28
-
-
Save mr47/4158caaca9607f5e39665a59996d5a0a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| export default function createDetectElementResize(nonce, hostWindow) { | |
| // Check `document` and `window` in case of server-side rendering | |
| let _window; | |
| if (typeof hostWindow !== 'undefined') { | |
| _window = hostWindow; | |
| } else if (typeof window !== 'undefined') { | |
| _window = window; | |
| } else if (typeof self !== 'undefined') { | |
| _window = self; | |
| } else { | |
| _window = global; | |
| } | |
| let attachEvent = | |
| typeof _window.document !== 'undefined' && _window.document.attachEvent; | |
| if (!attachEvent) { | |
| let requestFrame = (function() { | |
| let raf = | |
| _window.requestAnimationFrame || | |
| _window.mozRequestAnimationFrame || | |
| _window.webkitRequestAnimationFrame || | |
| function(fn) { | |
| return _window.setTimeout(fn, 20); | |
| }; | |
| return function(fn) { | |
| return raf(fn); | |
| }; | |
| })(); | |
| let cancelFrame = (function() { | |
| let cancel = | |
| _window.cancelAnimationFrame || | |
| _window.mozCancelAnimationFrame || | |
| _window.webkitCancelAnimationFrame || | |
| _window.clearTimeout; | |
| return function(id) { | |
| return cancel(id); | |
| }; | |
| })(); | |
| let resetTriggers = function(element) { | |
| let triggers = element.__resizeTriggers__, | |
| expand = triggers.firstElementChild, | |
| contract = triggers.lastElementChild, | |
| expandChild = expand.firstElementChild; | |
| contract.scrollLeft = contract.scrollWidth; | |
| contract.scrollTop = contract.scrollHeight; | |
| expandChild.style.width = expand.offsetWidth + 1 + 'px'; | |
| expandChild.style.height = expand.offsetHeight + 1 + 'px'; | |
| expand.scrollLeft = expand.scrollWidth; | |
| expand.scrollTop = expand.scrollHeight; | |
| }; | |
| let checkTriggers = function(element) { | |
| return ( | |
| element.offsetWidth != element.__resizeLast__.width || | |
| element.offsetHeight != element.__resizeLast__.height | |
| ); | |
| }; | |
| let scrollListener = function(e) { | |
| // Don't measure (which forces) reflow for scrolls that happen inside of children! | |
| if ( | |
| e.target.className && | |
| typeof e.target.className.indexOf === 'function' && | |
| e.target.className.indexOf('contract-trigger') < 0 && | |
| e.target.className.indexOf('expand-trigger') < 0 | |
| ) { | |
| return; | |
| } | |
| let element = this; | |
| resetTriggers(this); | |
| if (this.__resizeRAF__) { | |
| cancelFrame(this.__resizeRAF__); | |
| } | |
| this.__resizeRAF__ = requestFrame(function() { | |
| if (checkTriggers(element)) { | |
| element.__resizeLast__.width = element.offsetWidth; | |
| element.__resizeLast__.height = element.offsetHeight; | |
| element.__resizeListeners__.forEach(function(fn) { | |
| fn.call(element, e); | |
| }); | |
| } | |
| }); | |
| }; | |
| /* Detect CSS Animations support to detect element display/re-attach */ | |
| let animation = false, | |
| keyframeprefix = '', | |
| animationstartevent = 'animationstart', | |
| domPrefixes = 'Webkit Moz O ms'.split(' '), | |
| startEvents = 'webkitAnimationStart animationstart oAnimationStart MSAnimationStart'.split( | |
| ' ', | |
| ), | |
| pfx = ''; | |
| { | |
| let elm = _window.document.createElement('fakeelement'); | |
| if (elm.style.animationName !== undefined) { | |
| animation = true; | |
| } | |
| if (animation === false) { | |
| for (let i = 0; i < domPrefixes.length; i++) { | |
| if (elm.style[domPrefixes[i] + 'AnimationName'] !== undefined) { | |
| pfx = domPrefixes[i]; | |
| keyframeprefix = '-' + pfx.toLowerCase() + '-'; | |
| animationstartevent = startEvents[i]; | |
| animation = true; | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| let animationName = 'resizeanim'; | |
| let animationKeyframes = | |
| '@' + | |
| keyframeprefix + | |
| 'keyframes ' + | |
| animationName + | |
| ' { from { opacity: 0; } to { opacity: 0; } } '; | |
| let animationStyle = | |
| keyframeprefix + 'animation: 1ms ' + animationName + '; '; | |
| } | |
| let createStyles = function(doc) { | |
| if (!doc.getElementById('detectElementResize')) { | |
| //opacity:0 works around a chrome bug https://code.google.com/p/chromium/issues/detail?id=286360 | |
| let css = | |
| (animationKeyframes ? animationKeyframes : '') + | |
| '.resize-triggers { ' + | |
| (animationStyle ? animationStyle : '') + | |
| 'visibility: hidden; opacity: 0; } ' + | |
| '.resize-triggers, .resize-triggers > div, .contract-trigger:before { content: " "; display: block; position: absolute; top: 0; left: 0; height: 100%; width: 100%; overflow: hidden; z-index: -1; } .resize-triggers > div { background: #eee; overflow: auto; } .contract-trigger:before { width: 200%; height: 200%; }', | |
| head = doc.head || doc.getElementsByTagName('head')[0], | |
| style = doc.createElement('style'); | |
| style.id = 'detectElementResize'; | |
| style.type = 'text/css'; | |
| if (nonce != null) { | |
| style.setAttribute('nonce', nonce); | |
| } | |
| if (style.styleSheet) { | |
| style.styleSheet.cssText = css; | |
| } else { | |
| style.appendChild(doc.createTextNode(css)); | |
| } | |
| head.appendChild(style); | |
| } | |
| }; | |
| let addResizeListener = function(element, fn) { | |
| if (attachEvent) { | |
| element.attachEvent('onresize', fn); | |
| } else { | |
| if (!element.__resizeTriggers__) { | |
| let doc = element.ownerDocument; | |
| let elementStyle = _window.getComputedStyle(element); | |
| if (elementStyle && elementStyle.position == 'static') { | |
| element.style.position = 'relative'; | |
| } | |
| createStyles(doc); | |
| element.__resizeLast__ = {}; | |
| element.__resizeListeners__ = []; | |
| (element.__resizeTriggers__ = doc.createElement('div')).className = | |
| 'resize-triggers'; | |
| let expandTrigger = doc.createElement('div'); | |
| expandTrigger.className = 'expand-trigger'; | |
| expandTrigger.appendChild(doc.createElement('div')); | |
| let contractTrigger = doc.createElement('div'); | |
| contractTrigger.className = 'contract-trigger'; | |
| element.__resizeTriggers__.appendChild(expandTrigger); | |
| element.__resizeTriggers__.appendChild(contractTrigger); | |
| element.appendChild(element.__resizeTriggers__); | |
| resetTriggers(element); | |
| element.addEventListener('scroll', scrollListener, true); | |
| /* Listen for a css animation to detect element display/re-attach */ | |
| if (animationstartevent) { | |
| element.__resizeTriggers__.__animationListener__ = function animationListener( | |
| e, | |
| ) { | |
| if (e.animationName == animationName) { | |
| resetTriggers(element); | |
| } | |
| }; | |
| element.__resizeTriggers__.addEventListener( | |
| animationstartevent, | |
| element.__resizeTriggers__.__animationListener__, | |
| ); | |
| } | |
| } | |
| element.__resizeListeners__.push(fn); | |
| } | |
| }; | |
| let removeResizeListener = function(element, fn) { | |
| if (attachEvent) { | |
| element.detachEvent('onresize', fn); | |
| } else { | |
| element.__resizeListeners__.splice( | |
| element.__resizeListeners__.indexOf(fn), | |
| 1, | |
| ); | |
| if (!element.__resizeListeners__.length) { | |
| element.removeEventListener('scroll', scrollListener, true); | |
| if (element.__resizeTriggers__.__animationListener__) { | |
| element.__resizeTriggers__.removeEventListener( | |
| animationstartevent, | |
| element.__resizeTriggers__.__animationListener__, | |
| ); | |
| element.__resizeTriggers__.__animationListener__ = null; | |
| } | |
| try { | |
| element.__resizeTriggers__ = !element.removeChild( | |
| element.__resizeTriggers__, | |
| ); | |
| } catch (e) { | |
| // Preact compat; see developit/preact-compat/issues/228 | |
| } | |
| } | |
| } | |
| }; | |
| return { | |
| addResizeListener, | |
| removeResizeListener, | |
| }; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment