diff --git a/src/emitter.js b/src/emitter.js new file mode 100644 index 0000000..802ed01 --- /dev/null +++ b/src/emitter.js @@ -0,0 +1,43 @@ +class Emitter { + constructor() { + this._listeners = []; + this._queue = []; + } + + on(listener) { + if (typeof listener === 'function') { + this._listeners.push( listener ); + } + this.run(); + } + + emit(...args) { + this._queue.push(args); + this.run(); + } + + run() { + const listeners = this._listeners; + const queue = this._queue; + + // consume data in queue + const queueLength = queue.length; + const listenersLength = listeners.length; + + if ((queueLength > 0) && (listenersLength > 0)) { + for (let i = 0; i < queueLength; i++) { + for (let j = 0; j < listenersLength; j++) { + listeners[j](...queue[i]); + } + } + } + } + + clear() { + this._listeners.length = 0; + this._queue.length = 0; + } +} + +export default new Emitter() + diff --git a/src/index.js b/src/index.js index a070c3e..c728710 100644 --- a/src/index.js +++ b/src/index.js @@ -1,35 +1,10 @@ /** * Simulate appear & disappear events. */ -import { createIntersectionObserver, destroyIntersectionObserver, observerElement } from './intersectionObserverManager'; - -// hijack Node.prototype.addEventListener -const injectEventListenerHook = (instances = [], Node) => { - let nativeAddEventListener = Node.prototype.addEventListener; - - Node.prototype.addEventListener = function(eventName, eventHandler, useCapture, doNotWatch) { - const lowerCaseEventName = eventName && String(eventName).toLowerCase(); - const isAppearEvent = lowerCaseEventName === 'appear' || lowerCaseEventName === 'disappear'; - if (isAppearEvent) observerElement(this); - - nativeAddEventListener.call(this, eventName, eventHandler, useCapture); - }; - - return function unsetup() { - Node.prototype.addEventListener = nativeAddEventListener; - destroyIntersectionObserver(); - }; -}; +import inject from './inject'; +import setup from './setup'; export function setupAppear(win) { - if (!win) { - if (typeof window !== 'undefined') { - win = window; - } else { - return; - } - } - - createIntersectionObserver(); - return injectEventListenerHook([], win.Node); + inject(win); + return setup(win); } diff --git a/src/inject.js b/src/inject.js new file mode 100644 index 0000000..6d6db4d --- /dev/null +++ b/src/inject.js @@ -0,0 +1,27 @@ +import emitter from './emitter'; + +// hijack Node.prototype.addEventListener +const injectEventListenerHook = (win) => { + if (!win) { + if (typeof window !== 'undefined') { + win = window; + } else { + return; + } + } + + const Node = win.Node; + const nativeAddEventListener = Node.prototype.addEventListener; + // for restore + Node.prototype.__appearHijackedAddEventListener = nativeAddEventListener; + + Node.prototype.addEventListener = function(eventName, eventHandler, useCapture, doNotWatch) { + const lowerCaseEventName = eventName && String(eventName).toLowerCase(); + const isAppearEvent = lowerCaseEventName === 'appear' || lowerCaseEventName === 'disappear'; + if (isAppearEvent) emitter.emit(this); + + nativeAddEventListener.call(this, eventName, eventHandler, useCapture); + }; +}; + +export default injectEventListenerHook diff --git a/src/setup.js b/src/setup.js new file mode 100644 index 0000000..bd67c3a --- /dev/null +++ b/src/setup.js @@ -0,0 +1,30 @@ +import { createIntersectionObserver, destroyIntersectionObserver, observerElement } from './intersectionObserverManager'; +import emitter from "./emitter"; + +export default function setup(win) { + if (!win) { + if (typeof window !== 'undefined') { + win = window; + } else { + return; + } + } + + emitter.on((node) => observerElement(node)); + + createIntersectionObserver(); + + return function unsetup() { + emitter.clear(); + + const nativeAddEventListener = Node.prototype.__appearHijackedAddEventListener; + + if (typeof nativeAddEventListener === 'function') { + Node.prototype.addEventListener = nativeAddEventListener; + } + + delete Node.prototype.__appearHijackedAddEventListener; + + destroyIntersectionObserver(); + }; +} \ No newline at end of file