diff --git a/src/autosize.js b/src/autosize.js index c3f1a13..5c7c303 100644 --- a/src/autosize.js +++ b/src/autosize.js @@ -1,9 +1,38 @@ -const assignedElements = new Map(); +const modernEnv = typeof ResizeObserver === "function" && typeof WeakMap === "function"; + +const assignedElements = modernEnv ? new WeakMap() : new Map(); + +const resizeObserver = createResizeObserver(); + +function createResizeObserver() { + if (modernEnv) { + return new ResizeObserver((entries) => entries.forEach(e => onResize(e.target))); + } + // If not a modern environment, we use a Map instead of a WeakMap, which is iterable. + const resizeCallback = () => assignedElements.forEach((_, el) => onResize(el)); + window.addEventListener('resize', resizeCallback); + return { observe: () => {}, unobserve: () => {}, disconnect: () => window.removeEventListener('resize', resizeCallback)}; +} + +function getRelevantStyles(c) { + return `${c.width}-${c.height}-${c.padding}-${c.borderWidth}-${c.overflow}-${c.boxSizing}-${c.textAlign}`; +} + +function onResize(el) { + const instance = assignedElements.get(el); + if (instance !== undefined && el.scrollHeight > 0) { + const styles = getRelevantStyles(instance.computed); + if (styles !== instance.previousStyles) { + instance.update(); + } + } +} function assign(ta) { if (!ta || !ta.nodeName || ta.nodeName !== 'TEXTAREA' || assignedElements.has(ta)) return; let previousHeight = null; + let previousStyles = null; function cacheScrollTops(el) { const arr = []; @@ -99,6 +128,8 @@ function assign(ta) { testForHeightReduction: true, }); } + + previousStyles = getRelevantStyles(computed); } function fullSetHeight() { @@ -127,9 +158,9 @@ function assign(ta) { ta.removeEventListener('autosize:destroy', destroy); ta.removeEventListener('autosize:update', fullSetHeight); ta.removeEventListener('input', handleInput); - window.removeEventListener('resize', fullSetHeight); // future todo: consider replacing with ResizeObserver Object.keys(style).forEach(key => ta.style[key] = style[key]); assignedElements.delete(ta); + resizeObserver.unobserve(ta); }).bind(ta, { height: ta.style.height, resize: ta.style.resize, @@ -142,13 +173,15 @@ function assign(ta) { ta.addEventListener('autosize:destroy', destroy); ta.addEventListener('autosize:update', fullSetHeight); ta.addEventListener('input', handleInput); - window.addEventListener('resize', fullSetHeight); // future todo: consider replacing with ResizeObserver + resizeObserver.observe(ta); ta.style.overflowX = 'hidden'; ta.style.wordWrap = 'break-word'; assignedElements.set(ta, { destroy, update: fullSetHeight, + get previousStyles() { return previousStyles; }, + computed, }); fullSetHeight();