const set = typeof Set === "function" ? new Set() : (function() { const list = []; return { has(key) { return Boolean(list.indexOf(key) > -1); }, add(key) { list.push(key); }, delete(key) { list.splice(list.indexOf(key), 1); } }; })(); function assign(ta, { setOverflowX = true, setOverflowY = true } = {}) { if (!ta || !ta.nodeName || ta.nodeName !== "TEXTAREA" || set.has(ta)) return; let heightOffset = null; let overflowY = null; let clientWidth = ta.clientWidth; function init() { const style = window.getComputedStyle(ta, null); overflowY = style.overflowY; if (style.resize === "vertical") { ta.style.resize = "none"; } else if (style.resize === "both") { ta.style.resize = "horizontal"; } if (style.boxSizing === "content-box") { heightOffset = -( parseFloat(style.paddingTop) + parseFloat(style.paddingBottom) ); } else { heightOffset = parseFloat(style.borderTopWidth) + parseFloat(style.borderBottomWidth); } // Fix when a textarea is not on document body and heightOffset is Not a Number if (isNaN(heightOffset)) { heightOffset = 0; } update(); } function changeOverflow(value) { { // Chrome/Safari-specific fix: // When the textarea y-overflow is hidden, Chrome/Safari do not reflow the text to account for the space // made available by removing the scrollbar. The following forces the necessary text reflow. const width = ta.style.width; ta.style.width = "0px"; // Force reflow: /* jshint ignore:start */ ta.offsetWidth; /* jshint ignore:end */ ta.style.width = width; } overflowY = value; if (setOverflowY) { ta.style.overflowY = value; } resize(); } function resize() { const htmlTop = window.pageYOffset; const bodyTop = document.body.scrollTop; const originalHeight = ta.style.height; ta.style.height = "auto"; let endHeight = ta.scrollHeight + heightOffset; if (ta.scrollHeight === 0) { // If the scrollHeight is 0, then the element probably has display:none or is detached from the DOM. ta.style.height = originalHeight; return; } ta.style.height = endHeight + "px"; // used to check if an update is actually necessary on window.resize clientWidth = ta.clientWidth; // prevents scroll-position jumping document.documentElement.scrollTop = htmlTop; document.body.scrollTop = bodyTop; } function update() { const startHeight = ta.style.height; resize(); const style = window.getComputedStyle(ta, null); if (style.height !== ta.style.height) { if (overflowY !== "visible") { changeOverflow("visible"); } } else { if (overflowY !== "hidden") { changeOverflow("hidden"); } } if (startHeight !== ta.style.height) { const evt = document.createEvent("Event"); evt.initEvent("autosize:resized", true, false); ta.dispatchEvent(evt); } } const pageResize = () => { if (ta.clientWidth !== clientWidth) { update(); } }; const destroy = style => { window.removeEventListener("resize", pageResize, false); ta.removeEventListener("input", update, false); ta.removeEventListener("keyup", update, false); ta.removeEventListener("autosize:destroy", destroy, false); ta.removeEventListener("autosize:update", update, false); set.delete(ta); Object.keys(style).forEach(key => { ta.style[key] = style[key]; }); }; ta.addEventListener("autosize:destroy", destroy, false); // IE9 does not fire onpropertychange or oninput for deletions, // so binding to onkeyup to catch most of those events. // There is no way that I know of to detect something like 'cut' in IE9. if ("onpropertychange" in ta && "oninput" in ta) { ta.addEventListener("keyup", update, false); } window.addEventListener("resize", pageResize, false); ta.addEventListener("input", update, false); ta.addEventListener("autosize:update", update, false); set.add(ta); if (setOverflowX) { ta.style.overflowX = "hidden"; ta.style.wordWrap = "break-word"; } init(); } function exportDestroy(ta) { if (!(ta && ta.nodeName && ta.nodeName === "TEXTAREA")) return; const evt = document.createEvent("Event"); evt.initEvent("autosize:destroy", true, false); ta.dispatchEvent(evt); } function exportUpdate(ta) { if (!(ta && ta.nodeName && ta.nodeName === "TEXTAREA")) return; const evt = document.createEvent("Event"); evt.initEvent("autosize:update", true, false); ta.dispatchEvent(evt); } let autosize = (el, options) => { if (el) { Array.prototype.forEach.call(el.length ? el : [el], x => assign(x, options) ); } return el; }; autosize.destroy = el => { if (el) { Array.prototype.forEach.call(el.length ? el : [el], exportDestroy); } return el; }; autosize.update = el => { if (el) { Array.prototype.forEach.call(el.length ? el : [el], exportUpdate); } return el; }; export default autosize;