use-element-resize
Version:
Detect element resize with hooks
195 lines • 9.06 kB
JavaScript
;
/**
* Detect Element Resize.
* https://github.com/sdecima/javascript-detect-element-resize
* Sebastian Decima
*
* Forked from version 0.5.3; includes the following modifications:
* 1) Guard against unsafe 'window' and 'document' references (to support SSR).
* 2) Defer initialization code via a top-level function wrapper (to support SSR).
* 3) Avoid unnecessary reflows by not measuring size for scroll events bubbling from children.
* 4) Add nonce for style element.
**/
Object.defineProperty(exports, "__esModule", { value: true });
function createDetectElementResize(nonce) {
// Check `document` and `window` in case of server-side rendering
var _window;
if (typeof window !== "undefined") {
_window = window;
}
else if (typeof self !== "undefined") {
_window = self;
}
else {
_window = global;
}
var attachEvent = typeof document !== "undefined" && document.attachEvent;
if (!attachEvent) {
var requestFrame = (function () {
var raf = _window.requestAnimationFrame ||
_window.mozRequestAnimationFrame ||
_window.webkitRequestAnimationFrame ||
function (fn) {
return _window.setTimeout(fn, 20);
};
return function (fn) {
return raf(fn);
};
})();
var cancelFrame = (function () {
var cancel = _window.cancelAnimationFrame ||
_window.mozCancelAnimationFrame ||
_window.webkitCancelAnimationFrame ||
_window.clearTimeout;
return function (id) {
return cancel(id);
};
})();
var resetTriggers = function (element) {
var 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;
};
var checkTriggers = function (element) {
return (element.offsetWidth != element.__resizeLast__.width ||
element.offsetHeight != element.__resizeLast__.height);
};
var 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;
}
var 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 */
var animation = false, keyframeprefix = "", animationstartevent = "animationstart", domPrefixes = "Webkit Moz O ms".split(" "), startEvents = "webkitAnimationStart animationstart oAnimationStart MSAnimationStart".split(" "), pfx = "";
{
var elm = document.createElement("fakeelement");
if (elm.style.animationName !== undefined) {
animation = true;
}
if (animation === false) {
for (var 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;
}
}
}
}
var animationName = "resizeanim";
var animationKeyframes = "@" +
keyframeprefix +
"keyframes " +
animationName +
" { from { opacity: 0; } to { opacity: 0; } } ";
var animationStyle = keyframeprefix + "animation: 1ms " + animationName + "; ";
}
var createStyles = function (doc) {
if (!doc.getElementById("detectElementResize")) {
//opacity:0 works around a chrome bug https://code.google.com/p/chromium/issues/detail?id=286360
var 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);
}
};
var addResizeListener = function (element, fn) {
if (attachEvent) {
element.attachEvent("onresize", fn);
}
else {
if (!element.__resizeTriggers__) {
var doc = element.ownerDocument;
var 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";
element.__resizeTriggers__.innerHTML =
'<div class="expand-trigger"><div></div></div>' +
'<div class="contract-trigger"></div>';
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);
}
};
var 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: addResizeListener,
removeResizeListener: removeResizeListener
};
}
exports.default = createDetectElementResize;
//# sourceMappingURL=detectElementResize.js.map