diff --git a/changelog.md b/changelog.md index 7606159..56a8fa8 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,8 @@ ## Changelog +##### v.6.0.1 - 2023-02-13 +* Improved performance when appending text onto previous textarea value + ##### v.6.0.0 - 2023-02-10 * Reworked to remove use of cached (potentially stale) style values. Fixes #404 * Dropped support for Internet Explorer diff --git a/dist/autosize.esm.js b/dist/autosize.esm.js index f9ef33d..224b143 100644 --- a/dist/autosize.esm.js +++ b/dist/autosize.esm.js @@ -1 +1 @@ -var e=new Map;function t(t){var r=e.get(t);r&&r.destroy()}function r(t){var r=e.get(t);r&&r.update()}var o=null;"undefined"==typeof window?((o=function(e){return e}).destroy=function(e){return e},o.update=function(e){return e}):((o=function(t,r){return t&&Array.prototype.forEach.call(t.length?t:[t],function(t){return function(t){if(t&&t.nodeName&&"TEXTAREA"===t.nodeName&&!e.has(t)){var r=null,o=window.getComputedStyle(t),n=function(r){window.removeEventListener("resize",l,!1),t.removeEventListener("input",l,!1),t.removeEventListener("keyup",l,!1),t.removeEventListener("autosize:destroy",n,!1),t.removeEventListener("autosize:update",l,!1),Object.keys(r).forEach(function(e){return t.style[e]=r[e]}),e.delete(t)}.bind(t,{height:t.style.height,resize:t.style.resize,textAlign:t.style.textAlign,overflowY:t.style.overflowY,overflowX:t.style.overflowX,wordWrap:t.style.wordWrap});t.addEventListener("autosize:destroy",n,!1),window.addEventListener("resize",l,!1),t.addEventListener("input",l,!1),t.addEventListener("autosize:update",l,!1),t.style.overflowX="hidden",t.style.wordWrap="break-word",e.set(t,{destroy:n,update:l}),l()}function l(e){void 0===e&&(e=null);var n=o.overflowY;if(0!==t.scrollHeight){var i,a=function(e){for(var t=[];e&&e.parentNode&&e.parentNode instanceof Element;)e.parentNode.scrollTop&&t.push([e.parentNode,e.parentNode.scrollTop]),e=e.parentNode;return function(){return t.forEach(function(e){var t=e[0],r=e[1];t.style.scrollBehavior="auto",t.scrollTop=r,t.style.scrollBehavior=null})}}(t);if(t.style.height="","vertical"===o.resize?t.style.resize="none":"both"===o.resize&&(t.style.resize="horizontal"),i="content-box"===o.boxSizing?t.scrollHeight-(parseFloat(o.paddingTop)+parseFloat(o.paddingBottom)):t.scrollHeight+parseFloat(o.borderTopWidth)+parseFloat(o.borderBottomWidth),"none"!==o.maxHeight&&i>parseFloat(o.maxHeight)?("hidden"===o.overflowY&&(t.style.overflow="scroll"),i=parseFloat(o.maxHeight)):"hidden"!==o.overflowY&&(t.style.overflow="hidden"),t.style.height=i+"px",e&&(t.style.textAlign=e),a(),r!==i&&(t.dispatchEvent(new Event("autosize:resized",{bubbles:!0})),r=i),n!==o.overflow&&!e){var s=o.textAlign;"hidden"===o.overflow&&(t.style.textAlign="start"===s?"end":"start"),l(s)}}}}(t)}),t}).destroy=function(e){return e&&Array.prototype.forEach.call(e.length?e:[e],t),e},o.update=function(e){return e&&Array.prototype.forEach.call(e.length?e:[e],r),e});var n=o;export default n; +var e=new Map;function t(t){var o=e.get(t);o&&o.destroy()}function o(t){var o=e.get(t);o&&o.update()}var r=null;"undefined"==typeof window?((r=function(e){return e}).destroy=function(e){return e},r.update=function(e){return e}):((r=function(t,o){return t&&Array.prototype.forEach.call(t.length?t:[t],function(t){return function(t){if(t&&t.nodeName&&"TEXTAREA"===t.nodeName&&!e.has(t)){var o,r=null,n=window.getComputedStyle(t),i=(o=t.value,function(){a({testForHeightReduction:""===o||!t.value.startsWith(o),restoreTextAlign:null}),o=t.value}),l=function(o){t.removeEventListener("autosize:destroy",l),t.removeEventListener("autosize:update",s),t.removeEventListener("input",i),window.removeEventListener("resize",s),Object.keys(o).forEach(function(e){return t.style[e]=o[e]}),e.delete(t)}.bind(t,{height:t.style.height,resize:t.style.resize,textAlign:t.style.textAlign,overflowY:t.style.overflowY,overflowX:t.style.overflowX,wordWrap:t.style.wordWrap});t.addEventListener("autosize:destroy",l),t.addEventListener("autosize:update",s),t.addEventListener("input",i),window.addEventListener("resize",s),t.style.overflowX="hidden",t.style.wordWrap="break-word",e.set(t,{destroy:l,update:s}),s()}function a(e){var o,i,l=e.restoreTextAlign,s=void 0===l?null:l,d=e.testForHeightReduction,u=void 0===d||d,c=n.overflowY;if(0!==t.scrollHeight&&("vertical"===n.resize?t.style.resize="none":"both"===n.resize&&(t.style.resize="horizontal"),u&&(o=function(e){for(var t=[];e&&e.parentNode&&e.parentNode instanceof Element;)e.parentNode.scrollTop&&t.push([e.parentNode,e.parentNode.scrollTop]),e=e.parentNode;return function(){return t.forEach(function(e){var t=e[0],o=e[1];t.style.scrollBehavior="auto",t.scrollTop=o,t.style.scrollBehavior=null})}}(t),t.style.height=""),i="content-box"===n.boxSizing?t.scrollHeight-(parseFloat(n.paddingTop)+parseFloat(n.paddingBottom)):t.scrollHeight+parseFloat(n.borderTopWidth)+parseFloat(n.borderBottomWidth),"none"!==n.maxHeight&&i>parseFloat(n.maxHeight)?("hidden"===n.overflowY&&(t.style.overflow="scroll"),i=parseFloat(n.maxHeight)):"hidden"!==n.overflowY&&(t.style.overflow="hidden"),t.style.height=i+"px",s&&(t.style.textAlign=s),o&&o(),r!==i&&(t.dispatchEvent(new Event("autosize:resized",{bubbles:!0})),r=i),c!==n.overflow&&!s)){var v=n.textAlign;"hidden"===n.overflow&&(t.style.textAlign="start"===v?"end":"start"),a({restoreTextAlign:v,testForHeightReduction:!0})}}function s(){a({testForHeightReduction:!0,restoreTextAlign:null})}}(t)}),t}).destroy=function(e){return e&&Array.prototype.forEach.call(e.length?e:[e],t),e},r.update=function(e){return e&&Array.prototype.forEach.call(e.length?e:[e],o),e});var n=r;export default n; diff --git a/dist/autosize.js b/dist/autosize.js index 130c550..e227c1a 100644 --- a/dist/autosize.js +++ b/dist/autosize.js @@ -33,22 +33,18 @@ var computed = window.getComputedStyle(ta); - function update(cachedTextAlign) { - if (cachedTextAlign === void 0) { - cachedTextAlign = null; - } - + function setHeight(_ref2) { + var _ref2$restoreTextAlig = _ref2.restoreTextAlign, + restoreTextAlign = _ref2$restoreTextAlig === void 0 ? null : _ref2$restoreTextAlig, + _ref2$testForHeightRe = _ref2.testForHeightReduction, + testForHeightReduction = _ref2$testForHeightRe === void 0 ? true : _ref2$testForHeightRe; var initialOverflowY = computed.overflowY; if (ta.scrollHeight === 0) { // If the scrollHeight is 0, then the element probably has display:none or is detached from the DOM. return; - } // ensure the scrollTop values of parent elements are not modified as a consequence of calculating the textarea height - + } // disallow vertical resizing - var restoreScrollTops = cacheScrollTops(ta); - ta.style.height = ''; // this is necessary for to scrollHeight to accurately reflect situations where the textarea should shrink - // disallow vertical resizing if (computed.resize === 'vertical') { ta.style.resize = 'none'; @@ -56,6 +52,15 @@ ta.style.resize = 'horizontal'; } + var restoreScrollTops; // remove inline height style to accurately measure situations where the textarea should shrink + // however, skip this step if the new value appends to the previous value, as textarea height should only have grown + + if (testForHeightReduction) { + // ensure the scrollTop values of parent elements are not modified as a consequence of shrinking the textarea height + restoreScrollTops = cacheScrollTops(ta); + ta.style.height = ''; + } + var newHeight; if (computed.boxSizing === 'content-box') { @@ -76,11 +81,13 @@ ta.style.height = newHeight + 'px'; - if (cachedTextAlign) { - ta.style.textAlign = cachedTextAlign; + if (restoreTextAlign) { + ta.style.textAlign = restoreTextAlign; } - restoreScrollTops(); + if (restoreScrollTops) { + restoreScrollTops(); + } if (previousHeight !== newHeight) { ta.dispatchEvent(new Event('autosize:resized', { @@ -89,7 +96,7 @@ previousHeight = newHeight; } - if (initialOverflowY !== computed.overflow && !cachedTextAlign) { + if (initialOverflowY !== computed.overflow && !restoreTextAlign) { var textAlign = computed.textAlign; if (computed.overflow === 'hidden') { @@ -99,16 +106,39 @@ ta.style.textAlign = textAlign === 'start' ? 'end' : 'start'; } - update(textAlign); + setHeight({ + restoreTextAlign: textAlign, + testForHeightReduction: true + }); } } + function fullSetHeight() { + setHeight({ + testForHeightReduction: true, + restoreTextAlign: null + }); + } + + var handleInput = function () { + var previousValue = ta.value; + return function () { + setHeight({ + // if previousValue is '', check for height shrinkage because the placeholder may be taking up space instead + // if new value is merely appending to previous value, skip checking for height deduction + testForHeightReduction: previousValue === '' || !ta.value.startsWith(previousValue), + restoreTextAlign: null + }); + previousValue = ta.value; + }; + }(); + var destroy = function (style) { - window.removeEventListener('resize', update, false); - ta.removeEventListener('input', update, false); - ta.removeEventListener('keyup', update, false); - ta.removeEventListener('autosize:destroy', destroy, false); - ta.removeEventListener('autosize:update', update, false); + 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(function (key) { return ta.style[key] = style[key]; }); @@ -122,17 +152,18 @@ wordWrap: ta.style.wordWrap }); - ta.addEventListener('autosize:destroy', destroy, false); - window.addEventListener('resize', update, false); - ta.addEventListener('input', update, false); - ta.addEventListener('autosize:update', update, false); + ta.addEventListener('autosize:destroy', destroy); + ta.addEventListener('autosize:update', fullSetHeight); + ta.addEventListener('input', handleInput); + window.addEventListener('resize', fullSetHeight); // future todo: consider replacing with ResizeObserver + ta.style.overflowX = 'hidden'; ta.style.wordWrap = 'break-word'; assignedElements.set(ta, { destroy: destroy, - update: update + update: fullSetHeight }); - update(); + fullSetHeight(); } function destroy(ta) { diff --git a/dist/autosize.min.js b/dist/autosize.min.js index c89b132..1b24155 100644 --- a/dist/autosize.min.js +++ b/dist/autosize.min.js @@ -1 +1 @@ -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e||self).autosize=t()}(this,function(){var e=new Map;function t(t){var o=e.get(t);o&&o.destroy()}function o(t){var o=e.get(t);o&&o.update()}var r=null;return"undefined"==typeof window?((r=function(e){return e}).destroy=function(e){return e},r.update=function(e){return e}):((r=function(t,o){return t&&Array.prototype.forEach.call(t.length?t:[t],function(t){return function(t){if(t&&t.nodeName&&"TEXTAREA"===t.nodeName&&!e.has(t)){var o=null,r=window.getComputedStyle(t),n=function(o){window.removeEventListener("resize",i,!1),t.removeEventListener("input",i,!1),t.removeEventListener("keyup",i,!1),t.removeEventListener("autosize:destroy",n,!1),t.removeEventListener("autosize:update",i,!1),Object.keys(o).forEach(function(e){return t.style[e]=o[e]}),e.delete(t)}.bind(t,{height:t.style.height,resize:t.style.resize,textAlign:t.style.textAlign,overflowY:t.style.overflowY,overflowX:t.style.overflowX,wordWrap:t.style.wordWrap});t.addEventListener("autosize:destroy",n,!1),window.addEventListener("resize",i,!1),t.addEventListener("input",i,!1),t.addEventListener("autosize:update",i,!1),t.style.overflowX="hidden",t.style.wordWrap="break-word",e.set(t,{destroy:n,update:i}),i()}function i(e){void 0===e&&(e=null);var n=r.overflowY;if(0!==t.scrollHeight){var l,a=function(e){for(var t=[];e&&e.parentNode&&e.parentNode instanceof Element;)e.parentNode.scrollTop&&t.push([e.parentNode,e.parentNode.scrollTop]),e=e.parentNode;return function(){return t.forEach(function(e){var t=e[0],o=e[1];t.style.scrollBehavior="auto",t.scrollTop=o,t.style.scrollBehavior=null})}}(t);if(t.style.height="","vertical"===r.resize?t.style.resize="none":"both"===r.resize&&(t.style.resize="horizontal"),l="content-box"===r.boxSizing?t.scrollHeight-(parseFloat(r.paddingTop)+parseFloat(r.paddingBottom)):t.scrollHeight+parseFloat(r.borderTopWidth)+parseFloat(r.borderBottomWidth),"none"!==r.maxHeight&&l>parseFloat(r.maxHeight)?("hidden"===r.overflowY&&(t.style.overflow="scroll"),l=parseFloat(r.maxHeight)):"hidden"!==r.overflowY&&(t.style.overflow="hidden"),t.style.height=l+"px",e&&(t.style.textAlign=e),a(),o!==l&&(t.dispatchEvent(new Event("autosize:resized",{bubbles:!0})),o=l),n!==r.overflow&&!e){var s=r.textAlign;"hidden"===r.overflow&&(t.style.textAlign="start"===s?"end":"start"),i(s)}}}}(t)}),t}).destroy=function(e){return e&&Array.prototype.forEach.call(e.length?e:[e],t),e},r.update=function(e){return e&&Array.prototype.forEach.call(e.length?e:[e],o),e}),r}); +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e||self).autosize=t()}(this,function(){var e=new Map;function t(t){var o=e.get(t);o&&o.destroy()}function o(t){var o=e.get(t);o&&o.update()}var r=null;return"undefined"==typeof window?((r=function(e){return e}).destroy=function(e){return e},r.update=function(e){return e}):((r=function(t,o){return t&&Array.prototype.forEach.call(t.length?t:[t],function(t){return function(t){if(t&&t.nodeName&&"TEXTAREA"===t.nodeName&&!e.has(t)){var o,r=null,n=window.getComputedStyle(t),i=(o=t.value,function(){s({testForHeightReduction:""===o||!t.value.startsWith(o),restoreTextAlign:null}),o=t.value}),l=function(o){t.removeEventListener("autosize:destroy",l),t.removeEventListener("autosize:update",a),t.removeEventListener("input",i),window.removeEventListener("resize",a),Object.keys(o).forEach(function(e){return t.style[e]=o[e]}),e.delete(t)}.bind(t,{height:t.style.height,resize:t.style.resize,textAlign:t.style.textAlign,overflowY:t.style.overflowY,overflowX:t.style.overflowX,wordWrap:t.style.wordWrap});t.addEventListener("autosize:destroy",l),t.addEventListener("autosize:update",a),t.addEventListener("input",i),window.addEventListener("resize",a),t.style.overflowX="hidden",t.style.wordWrap="break-word",e.set(t,{destroy:l,update:a}),a()}function s(e){var o,i,l=e.restoreTextAlign,a=void 0===l?null:l,d=e.testForHeightReduction,u=void 0===d||d,f=n.overflowY;if(0!==t.scrollHeight&&("vertical"===n.resize?t.style.resize="none":"both"===n.resize&&(t.style.resize="horizontal"),u&&(o=function(e){for(var t=[];e&&e.parentNode&&e.parentNode instanceof Element;)e.parentNode.scrollTop&&t.push([e.parentNode,e.parentNode.scrollTop]),e=e.parentNode;return function(){return t.forEach(function(e){var t=e[0],o=e[1];t.style.scrollBehavior="auto",t.scrollTop=o,t.style.scrollBehavior=null})}}(t),t.style.height=""),i="content-box"===n.boxSizing?t.scrollHeight-(parseFloat(n.paddingTop)+parseFloat(n.paddingBottom)):t.scrollHeight+parseFloat(n.borderTopWidth)+parseFloat(n.borderBottomWidth),"none"!==n.maxHeight&&i>parseFloat(n.maxHeight)?("hidden"===n.overflowY&&(t.style.overflow="scroll"),i=parseFloat(n.maxHeight)):"hidden"!==n.overflowY&&(t.style.overflow="hidden"),t.style.height=i+"px",a&&(t.style.textAlign=a),o&&o(),r!==i&&(t.dispatchEvent(new Event("autosize:resized",{bubbles:!0})),r=i),f!==n.overflow&&!a)){var c=n.textAlign;"hidden"===n.overflow&&(t.style.textAlign="start"===c?"end":"start"),s({restoreTextAlign:c,testForHeightReduction:!0})}}function a(){s({testForHeightReduction:!0,restoreTextAlign:null})}}(t)}),t}).destroy=function(e){return e&&Array.prototype.forEach.call(e.length?e:[e],t),e},r.update=function(e){return e&&Array.prototype.forEach.call(e.length?e:[e],o),e}),r}); diff --git a/package.json b/package.json index cf521d0..0f3ec21 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "autosize", "description": "Autosize is a small, stand-alone script to automatically adjust textarea height to fit text.", - "version": "6.0.0", + "version": "6.0.1", "keywords": [ "textarea", "form", diff --git a/src/autosize.js b/src/autosize.js index e8d4186..c3f1a13 100644 --- a/src/autosize.js +++ b/src/autosize.js @@ -24,7 +24,10 @@ function assign(ta) { const computed = window.getComputedStyle(ta); - function update(cachedTextAlign = null) { + function setHeight({ + restoreTextAlign = null, + testForHeightReduction = true, + }) { let initialOverflowY = computed.overflowY; if (ta.scrollHeight === 0) { @@ -32,11 +35,6 @@ function assign(ta) { return; } - // ensure the scrollTop values of parent elements are not modified as a consequence of calculating the textarea height - const restoreScrollTops = cacheScrollTops(ta); - - ta.style.height = ''; // this is necessary for to scrollHeight to accurately reflect situations where the textarea should shrink - // disallow vertical resizing if (computed.resize === 'vertical') { ta.style.resize = 'none'; @@ -44,6 +42,16 @@ function assign(ta) { ta.style.resize = 'horizontal'; } + let restoreScrollTops + + // remove inline height style to accurately measure situations where the textarea should shrink + // however, skip this step if the new value appends to the previous value, as textarea height should only have grown + if (testForHeightReduction) { + // ensure the scrollTop values of parent elements are not modified as a consequence of shrinking the textarea height + restoreScrollTops = cacheScrollTops(ta); + ta.style.height = ''; + } + let newHeight; if (computed.boxSizing === 'content-box') { @@ -63,18 +71,20 @@ function assign(ta) { ta.style.height = newHeight+'px'; - if (cachedTextAlign) { - ta.style.textAlign = cachedTextAlign; + if (restoreTextAlign) { + ta.style.textAlign = restoreTextAlign; } - restoreScrollTops(); + if (restoreScrollTops) { + restoreScrollTops(); + } if (previousHeight !== newHeight) { ta.dispatchEvent(new Event('autosize:resized', {bubbles: true})); previousHeight = newHeight; } - if (initialOverflowY !== computed.overflow && !cachedTextAlign) { + if (initialOverflowY !== computed.overflow && !restoreTextAlign) { const textAlign = computed.textAlign; if (computed.overflow === 'hidden') { @@ -84,16 +94,40 @@ function assign(ta) { ta.style.textAlign = textAlign === 'start' ? 'end' : 'start'; } - update(textAlign); + setHeight({ + restoreTextAlign: textAlign, + testForHeightReduction: true, + }); } } + function fullSetHeight() { + setHeight({ + testForHeightReduction: true, + restoreTextAlign: null, + }); + } + + const handleInput = (function(){ + let previousValue = ta.value; + + return ()=> { + setHeight({ + // if previousValue is '', check for height shrinkage because the placeholder may be taking up space instead + // if new value is merely appending to previous value, skip checking for height deduction + testForHeightReduction: previousValue === '' || !ta.value.startsWith(previousValue), + restoreTextAlign: null, + }); + + previousValue = ta.value; + } + }()) + const destroy = (style => { - window.removeEventListener('resize', update, false); - ta.removeEventListener('input', update, false); - ta.removeEventListener('keyup', update, false); - ta.removeEventListener('autosize:destroy', destroy, false); - ta.removeEventListener('autosize:update', update, false); + 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); }).bind(ta, { @@ -105,20 +139,19 @@ function assign(ta) { wordWrap: ta.style.wordWrap, }); - ta.addEventListener('autosize:destroy', destroy, false); - - window.addEventListener('resize', update, false); - ta.addEventListener('input', update, false); - ta.addEventListener('autosize:update', update, false); + ta.addEventListener('autosize:destroy', destroy); + ta.addEventListener('autosize:update', fullSetHeight); + ta.addEventListener('input', handleInput); + window.addEventListener('resize', fullSetHeight); // future todo: consider replacing with ResizeObserver ta.style.overflowX = 'hidden'; ta.style.wordWrap = 'break-word'; assignedElements.set(ta, { destroy, - update, + update: fullSetHeight, }); - update(); + fullSetHeight(); } function destroy(ta) {