intersection-observer-admin
Advanced tools
Comparing version 0.0.5 to 0.1.0
@@ -1,3 +0,1 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var IntersectionObserverAdmin = /** @class */ (function () { | ||
@@ -255,3 +253,3 @@ function IntersectionObserverAdmin() { | ||
}()); | ||
exports.default = IntersectionObserverAdmin; | ||
export default IntersectionObserverAdmin; | ||
//# sourceMappingURL=index.js.map |
(function (global, factory) { | ||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : | ||
typeof define === 'function' && define.amd ? define(factory) : | ||
(global.intersectionObserverAdmin = factory()); | ||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : | ||
typeof define === 'function' && define.amd ? define(factory) : | ||
(global.intersectionObserverAdmin = factory()); | ||
}(this, (function () { 'use strict'; | ||
function unwrapExports (x) { | ||
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x.default : x; | ||
} | ||
var IntersectionObserverAdmin = /** @class */ (function () { | ||
function IntersectionObserverAdmin() { | ||
this.DOMRef = new WeakMap(); | ||
} | ||
/** | ||
* Adds element to observe via IntersectionObserver and stores element + relevant callbacks and observer options in static | ||
* administrator for lookup in the future | ||
* | ||
* @method add | ||
* @param {HTMLElement | Window} element | ||
* @param {Function} enterCallback | ||
* @param {Function} exitCallback | ||
* @param {Object} observerOptions | ||
* @param {String} scrollableArea | ||
* @public | ||
*/ | ||
IntersectionObserverAdmin.prototype.observe = function (element, enterCallback, exitCallback, observerOptions, scrollableArea) { | ||
var _a; | ||
if (!element || !observerOptions) { | ||
return; | ||
} | ||
var _b = observerOptions.root, root = _b === void 0 ? window : _b; | ||
// first find shared root element (window or scrollable area) | ||
var potentialRootMatch = this._findRoot(root); | ||
// second if there is a matching root, find an entry with the same observerOptions | ||
var matchingEntryForRoot; | ||
if (potentialRootMatch) { | ||
matchingEntryForRoot = this._determineMatchingElements(observerOptions, potentialRootMatch); | ||
} | ||
if (matchingEntryForRoot) { | ||
var elements = matchingEntryForRoot.elements, intersectionObserver = matchingEntryForRoot.intersectionObserver; | ||
elements.push({ element: element, enterCallback: enterCallback, exitCallback: exitCallback }); | ||
intersectionObserver.observe(element); | ||
return; | ||
} | ||
// No matching entry for root in static admin, thus create new IntersectionObserver instance | ||
var newIO = new IntersectionObserver(this._setupOnIntersection(observerOptions, scrollableArea).bind(this), observerOptions); | ||
newIO.observe(element); | ||
var observerEntry = { | ||
elements: [{ element: element, enterCallback: enterCallback, exitCallback: exitCallback }], | ||
intersectionObserver: newIO, | ||
observerOptions: observerOptions | ||
}; | ||
var stringifiedOptions = this._stringifyObserverOptions(observerOptions, scrollableArea); | ||
if (potentialRootMatch) { | ||
// if share same root and need to add new entry to root match | ||
potentialRootMatch[stringifiedOptions] = observerEntry; | ||
} | ||
else { | ||
// no root exists, so add to WeakMap | ||
if (this.DOMRef) { | ||
this.DOMRef.set(root, (_a = {}, _a[stringifiedOptions] = observerEntry, _a)); | ||
} | ||
} | ||
}; | ||
/** | ||
* Unobserve target element and remove element from static admin | ||
* | ||
* @method unobserve | ||
* @param {HTMLElement|Window} target | ||
* @param {Object} observerOptions | ||
* @param {String} scrollableArea | ||
* @public | ||
*/ | ||
IntersectionObserverAdmin.prototype.unobserve = function (target, observerOptions, scrollableArea) { | ||
var matchingRootEntry = this._findMatchingRootEntry(observerOptions, scrollableArea); | ||
if (matchingRootEntry) { | ||
var intersectionObserver = matchingRootEntry.intersectionObserver, elements = matchingRootEntry.elements; | ||
intersectionObserver.unobserve(target); | ||
// important to do this in reverse order | ||
for (var i = elements.length - 1; i >= 0; i--) { | ||
if (elements[i] && elements[i].element === target) { | ||
elements.splice(i, 1); | ||
break; | ||
} | ||
} | ||
} | ||
}; | ||
/** | ||
* cleanup data structures and unobserve elements | ||
* | ||
* @method destroy | ||
* @public | ||
*/ | ||
IntersectionObserverAdmin.prototype.destroy = function () { | ||
this.DOMRef = null; | ||
}; | ||
/** | ||
* use function composition to curry observerOptions | ||
* | ||
* @method _setupOnIntersection | ||
* @param {Object} observerOptions | ||
* @param {String} scrollableArea | ||
*/ | ||
IntersectionObserverAdmin.prototype._setupOnIntersection = function (observerOptions, scrollableArea) { | ||
var _this = this; | ||
return function (entries) { | ||
return _this._onIntersection(observerOptions, scrollableArea, entries); | ||
}; | ||
}; | ||
/** | ||
* IntersectionObserver callback when element is intersecting viewport | ||
* | ||
* @method _onIntersection | ||
* @param {Object} observerOptions | ||
* @param {String} scrollableArea | ||
* @param {Array} ioEntries | ||
* @private | ||
*/ | ||
IntersectionObserverAdmin.prototype._onIntersection = function (observerOptions, scrollableArea, ioEntries) { | ||
var _this = this; | ||
ioEntries.forEach(function (entry) { | ||
var isIntersecting = entry.isIntersecting, intersectionRatio = entry.intersectionRatio; | ||
// first determine if entry intersecting | ||
if (isIntersecting) { | ||
// then find entry's callback in static administration | ||
var matchingRootEntry = _this._findMatchingRootEntry(observerOptions, scrollableArea); | ||
if (matchingRootEntry) { | ||
matchingRootEntry.elements.some(function (obj) { | ||
if (obj.element === entry.target) { | ||
// call entry's enter callback | ||
if (obj.enterCallback) { | ||
obj.enterCallback(); | ||
} | ||
return true; | ||
} | ||
return false; | ||
}); | ||
} | ||
} | ||
else if (intersectionRatio <= 0) { | ||
// then find entry's callback in static administration | ||
var matchingRootEntry = _this._findMatchingRootEntry(observerOptions, scrollableArea); | ||
if (matchingRootEntry) { | ||
matchingRootEntry.elements.some(function (obj) { | ||
if (obj.element === entry.target) { | ||
// call entry's enter callback | ||
if (obj.exitCallback) { | ||
obj.exitCallback(); | ||
} | ||
return true; | ||
} | ||
return false; | ||
}); | ||
} | ||
} | ||
}); | ||
}; | ||
/** | ||
* { root: { stringifiedOptions: { elements: []...] } } | ||
* @method _findRoot | ||
* @param {HTMLElement|Window} root | ||
* @private | ||
* @return {Object} of elements that share same root | ||
*/ | ||
IntersectionObserverAdmin.prototype._findRoot = function (root) { | ||
if (this.DOMRef) { | ||
return this.DOMRef.get(root); | ||
} | ||
}; | ||
/** | ||
* Used for onIntersection callbacks and unobserving the IntersectionObserver | ||
* We don't care about observerOptions key order because we already added | ||
* to the static administrator or found an existing IntersectionObserver with the same | ||
* root && observerOptions to reuse | ||
* | ||
* @method _findMatchingRootEntry | ||
* @param {Object} observerOptions | ||
* @param {String} scrollableArea | ||
* @return {Object} entry with elements and other options | ||
*/ | ||
IntersectionObserverAdmin.prototype._findMatchingRootEntry = function (observerOptions, scrollableArea) { | ||
var _a = observerOptions.root, root = _a === void 0 ? window : _a; | ||
var matchingRoot = this._findRoot(root); | ||
if (matchingRoot) { | ||
var stringifiedOptions = this._stringifyObserverOptions(observerOptions, scrollableArea); | ||
return matchingRoot[stringifiedOptions]; | ||
} | ||
}; | ||
/** | ||
* Determine if existing elements for a given root based on passed in observerOptions | ||
* regardless of sort order of keys | ||
* | ||
* @method _determineMatchingElements | ||
* @param {Object} observerOptions | ||
* @param {Object} potentialRootMatch e.g. { stringifiedOptions: { elements: [], ... }, stringifiedOptions: { elements: [], ... }} | ||
* @private | ||
* @return {Object} containing array of elements and other meta | ||
*/ | ||
IntersectionObserverAdmin.prototype._determineMatchingElements = function (observerOptions, potentialRootMatch) { | ||
var _this = this; | ||
if (!potentialRootMatch) { | ||
return; | ||
} | ||
var matchingKey = Object.keys(potentialRootMatch).filter(function (key) { | ||
var comparableOptions = potentialRootMatch[key].observerOptions; | ||
return _this._areOptionsSame(observerOptions, comparableOptions); | ||
})[0]; | ||
return potentialRootMatch[matchingKey]; | ||
}; | ||
/** | ||
* recursive method to test primitive string, number, null, etc and complex | ||
* object equality. | ||
* | ||
* @method _areOptionsSame | ||
* @param {Object} observerOptions | ||
* @param {Object} comparableOptions | ||
* @private | ||
* @return {Boolean} | ||
*/ | ||
IntersectionObserverAdmin.prototype._areOptionsSame = function (observerOptions, comparableOptions) { | ||
// simple comparison of string, number or even null/undefined | ||
var type1 = Object.prototype.toString.call(observerOptions); | ||
var type2 = Object.prototype.toString.call(comparableOptions); | ||
if (type1 !== type2) { | ||
return false; | ||
} | ||
else if (type1 !== '[object Object]' && type2 !== '[object Object]') { | ||
return observerOptions === comparableOptions; | ||
} | ||
// complex comparison for only type of [object Object] | ||
for (var key in observerOptions) { | ||
if (observerOptions.hasOwnProperty(key)) { | ||
// recursion to check nested | ||
if (this._areOptionsSame(observerOptions[key], comparableOptions[key]) === | ||
false) { | ||
return false; | ||
} | ||
} | ||
} | ||
return true; | ||
}; | ||
/** | ||
* Stringify observerOptions for use as a key. | ||
* Excludes observerOptions.root so that the resulting key is stable | ||
* | ||
* @param {Object} observerOptions | ||
* @param {String} scrollableArea | ||
* @private | ||
* @return {String} | ||
*/ | ||
IntersectionObserverAdmin.prototype._stringifyObserverOptions = function (observerOptions, scrollableArea) { | ||
var replacer = function (key, value) { | ||
if (key === 'root' && scrollableArea) { | ||
return scrollableArea; | ||
} | ||
return value; | ||
}; | ||
return JSON.stringify(observerOptions, replacer); | ||
}; | ||
return IntersectionObserverAdmin; | ||
}()); | ||
function createCommonjsModule(fn, module) { | ||
return module = { exports: {} }, fn(module, module.exports), module.exports; | ||
} | ||
return IntersectionObserverAdmin; | ||
var es = createCommonjsModule(function (module, exports) { | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var IntersectionObserverAdmin = /** @class */ (function () { | ||
function IntersectionObserverAdmin() { | ||
this.DOMRef = new WeakMap(); | ||
} | ||
/** | ||
* Adds element to observe via IntersectionObserver and stores element + relevant callbacks and observer options in static | ||
* administrator for lookup in the future | ||
* | ||
* @method add | ||
* @param {HTMLElement | Window} element | ||
* @param {Function} enterCallback | ||
* @param {Function} exitCallback | ||
* @param {Object} observerOptions | ||
* @param {String} scrollableArea | ||
* @public | ||
*/ | ||
IntersectionObserverAdmin.prototype.observe = function (element, enterCallback, exitCallback, observerOptions, scrollableArea) { | ||
var _a; | ||
if (!element || !observerOptions) { | ||
return; | ||
} | ||
var _b = observerOptions.root, root = _b === void 0 ? window : _b; | ||
// first find shared root element (window or scrollable area) | ||
var potentialRootMatch = this._findRoot(root); | ||
// second if there is a matching root, find an entry with the same observerOptions | ||
var matchingEntryForRoot; | ||
if (potentialRootMatch) { | ||
matchingEntryForRoot = this._determineMatchingElements(observerOptions, potentialRootMatch); | ||
} | ||
if (matchingEntryForRoot) { | ||
var elements = matchingEntryForRoot.elements, intersectionObserver = matchingEntryForRoot.intersectionObserver; | ||
elements.push({ element: element, enterCallback: enterCallback, exitCallback: exitCallback }); | ||
intersectionObserver.observe(element); | ||
return; | ||
} | ||
// No matching entry for root in static admin, thus create new IntersectionObserver instance | ||
var newIO = new IntersectionObserver(this._setupOnIntersection(observerOptions, scrollableArea).bind(this), observerOptions); | ||
newIO.observe(element); | ||
var observerEntry = { | ||
elements: [{ element: element, enterCallback: enterCallback, exitCallback: exitCallback }], | ||
intersectionObserver: newIO, | ||
observerOptions: observerOptions | ||
}; | ||
var stringifiedOptions = this._stringifyObserverOptions(observerOptions, scrollableArea); | ||
if (potentialRootMatch) { | ||
// if share same root and need to add new entry to root match | ||
potentialRootMatch[stringifiedOptions] = observerEntry; | ||
} | ||
else { | ||
// no root exists, so add to WeakMap | ||
if (this.DOMRef) { | ||
this.DOMRef.set(root, (_a = {}, _a[stringifiedOptions] = observerEntry, _a)); | ||
} | ||
} | ||
}; | ||
/** | ||
* Unobserve target element and remove element from static admin | ||
* | ||
* @method unobserve | ||
* @param {HTMLElement|Window} target | ||
* @param {Object} observerOptions | ||
* @param {String} scrollableArea | ||
* @public | ||
*/ | ||
IntersectionObserverAdmin.prototype.unobserve = function (target, observerOptions, scrollableArea) { | ||
var matchingRootEntry = this._findMatchingRootEntry(observerOptions, scrollableArea); | ||
if (matchingRootEntry) { | ||
var intersectionObserver = matchingRootEntry.intersectionObserver, elements = matchingRootEntry.elements; | ||
intersectionObserver.unobserve(target); | ||
// important to do this in reverse order | ||
for (var i = elements.length - 1; i >= 0; i--) { | ||
if (elements[i] && elements[i].element === target) { | ||
elements.splice(i, 1); | ||
break; | ||
} | ||
} | ||
} | ||
}; | ||
/** | ||
* cleanup data structures and unobserve elements | ||
* | ||
* @method destroy | ||
* @public | ||
*/ | ||
IntersectionObserverAdmin.prototype.destroy = function () { | ||
this.DOMRef = null; | ||
}; | ||
/** | ||
* use function composition to curry observerOptions | ||
* | ||
* @method _setupOnIntersection | ||
* @param {Object} observerOptions | ||
* @param {String} scrollableArea | ||
*/ | ||
IntersectionObserverAdmin.prototype._setupOnIntersection = function (observerOptions, scrollableArea) { | ||
var _this = this; | ||
return function (entries) { | ||
return _this._onIntersection(observerOptions, scrollableArea, entries); | ||
}; | ||
}; | ||
/** | ||
* IntersectionObserver callback when element is intersecting viewport | ||
* | ||
* @method _onIntersection | ||
* @param {Object} observerOptions | ||
* @param {String} scrollableArea | ||
* @param {Array} ioEntries | ||
* @private | ||
*/ | ||
IntersectionObserverAdmin.prototype._onIntersection = function (observerOptions, scrollableArea, ioEntries) { | ||
var _this = this; | ||
ioEntries.forEach(function (entry) { | ||
var isIntersecting = entry.isIntersecting, intersectionRatio = entry.intersectionRatio; | ||
// first determine if entry intersecting | ||
if (isIntersecting) { | ||
// then find entry's callback in static administration | ||
var matchingRootEntry = _this._findMatchingRootEntry(observerOptions, scrollableArea); | ||
if (matchingRootEntry) { | ||
matchingRootEntry.elements.some(function (obj) { | ||
if (obj.element === entry.target) { | ||
// call entry's enter callback | ||
if (obj.enterCallback) { | ||
obj.enterCallback(); | ||
} | ||
return true; | ||
} | ||
return false; | ||
}); | ||
} | ||
} | ||
else if (intersectionRatio <= 0) { | ||
// then find entry's callback in static administration | ||
var matchingRootEntry = _this._findMatchingRootEntry(observerOptions, scrollableArea); | ||
if (matchingRootEntry) { | ||
matchingRootEntry.elements.some(function (obj) { | ||
if (obj.element === entry.target) { | ||
// call entry's enter callback | ||
if (obj.exitCallback) { | ||
obj.exitCallback(); | ||
} | ||
return true; | ||
} | ||
return false; | ||
}); | ||
} | ||
} | ||
}); | ||
}; | ||
/** | ||
* { root: { stringifiedOptions: { elements: []...] } } | ||
* @method _findRoot | ||
* @param {HTMLElement|Window} root | ||
* @private | ||
* @return {Object} of elements that share same root | ||
*/ | ||
IntersectionObserverAdmin.prototype._findRoot = function (root) { | ||
if (this.DOMRef) { | ||
return this.DOMRef.get(root); | ||
} | ||
}; | ||
/** | ||
* Used for onIntersection callbacks and unobserving the IntersectionObserver | ||
* We don't care about observerOptions key order because we already added | ||
* to the static administrator or found an existing IntersectionObserver with the same | ||
* root && observerOptions to reuse | ||
* | ||
* @method _findMatchingRootEntry | ||
* @param {Object} observerOptions | ||
* @param {String} scrollableArea | ||
* @return {Object} entry with elements and other options | ||
*/ | ||
IntersectionObserverAdmin.prototype._findMatchingRootEntry = function (observerOptions, scrollableArea) { | ||
var _a = observerOptions.root, root = _a === void 0 ? window : _a; | ||
var matchingRoot = this._findRoot(root); | ||
if (matchingRoot) { | ||
var stringifiedOptions = this._stringifyObserverOptions(observerOptions, scrollableArea); | ||
return matchingRoot[stringifiedOptions]; | ||
} | ||
}; | ||
/** | ||
* Determine if existing elements for a given root based on passed in observerOptions | ||
* regardless of sort order of keys | ||
* | ||
* @method _determineMatchingElements | ||
* @param {Object} observerOptions | ||
* @param {Object} potentialRootMatch e.g. { stringifiedOptions: { elements: [], ... }, stringifiedOptions: { elements: [], ... }} | ||
* @private | ||
* @return {Object} containing array of elements and other meta | ||
*/ | ||
IntersectionObserverAdmin.prototype._determineMatchingElements = function (observerOptions, potentialRootMatch) { | ||
var _this = this; | ||
if (!potentialRootMatch) { | ||
return; | ||
} | ||
var matchingKey = Object.keys(potentialRootMatch).filter(function (key) { | ||
var comparableOptions = potentialRootMatch[key].observerOptions; | ||
return _this._areOptionsSame(observerOptions, comparableOptions); | ||
})[0]; | ||
return potentialRootMatch[matchingKey]; | ||
}; | ||
/** | ||
* recursive method to test primitive string, number, null, etc and complex | ||
* object equality. | ||
* | ||
* @method _areOptionsSame | ||
* @param {Object} observerOptions | ||
* @param {Object} comparableOptions | ||
* @private | ||
* @return {Boolean} | ||
*/ | ||
IntersectionObserverAdmin.prototype._areOptionsSame = function (observerOptions, comparableOptions) { | ||
// simple comparison of string, number or even null/undefined | ||
var type1 = Object.prototype.toString.call(observerOptions); | ||
var type2 = Object.prototype.toString.call(comparableOptions); | ||
if (type1 !== type2) { | ||
return false; | ||
} | ||
else if (type1 !== '[object Object]' && type2 !== '[object Object]') { | ||
return observerOptions === comparableOptions; | ||
} | ||
// complex comparison for only type of [object Object] | ||
for (var key in observerOptions) { | ||
if (observerOptions.hasOwnProperty(key)) { | ||
// recursion to check nested | ||
if (this._areOptionsSame(observerOptions[key], comparableOptions[key]) === | ||
false) { | ||
return false; | ||
} | ||
} | ||
} | ||
return true; | ||
}; | ||
/** | ||
* Stringify observerOptions for use as a key. | ||
* Excludes observerOptions.root so that the resulting key is stable | ||
* | ||
* @param {Object} observerOptions | ||
* @param {String} scrollableArea | ||
* @private | ||
* @return {String} | ||
*/ | ||
IntersectionObserverAdmin.prototype._stringifyObserverOptions = function (observerOptions, scrollableArea) { | ||
var replacer = function (key, value) { | ||
if (key === 'root' && scrollableArea) { | ||
return scrollableArea; | ||
} | ||
return value; | ||
}; | ||
return JSON.stringify(observerOptions, replacer); | ||
}; | ||
return IntersectionObserverAdmin; | ||
}()); | ||
exports.default = IntersectionObserverAdmin; | ||
}); | ||
var index = unwrapExports(es); | ||
return index; | ||
}))); |
{ | ||
"name": "intersection-observer-admin", | ||
"version": "0.0.5", | ||
"version": "0.1.0", | ||
"description": "Intersection Observer Admin for better performance", | ||
"main": "dist/intersection-observer-admin.umd.js", | ||
"module": "dist/intersection-observer-admin.es5.js", | ||
"types": "dist/types/index.d.ts", | ||
"scripts": { | ||
"build": "tsc && tsc --module commonjs --outDir dist/es && rollup -c rollup.config.ts", | ||
"build": "tsc && tsc --outDir dist/es && rollup -c rollup.config.ts", | ||
"lint": "tslint -t codeFrame 'src/**/*.ts' 'test/index.test.ts'", | ||
@@ -52,3 +53,3 @@ "prepare": "npm run build", | ||
"rollup": "^0.67.3", | ||
"rollup-plugin-commonjs": "^9.2.0", | ||
"rollup-plugin-typescript2": "^0.18.1", | ||
"rollup-plugin-node-resolve": "^3.4.0", | ||
@@ -55,0 +56,0 @@ "rollup-plugin-sourcemaps": "^0.4.2", |
@@ -55,3 +55,3 @@ intersection-observer-admin | ||
IntersectionObserver's Browser Support](https://platform-status.mozilla.org/) | ||
[IntersectionObserver's Browser Support](https://platform-status.mozilla.org/) | ||
------------------------------------------------------------------------------ | ||
@@ -58,0 +58,0 @@ ### Out of the box |
Sorry, the diff of this file is not supported yet
53697
12
1018