自动扩展文本区域

IT技术 javascript
2021-02-24 21:47:17

我正在尝试做一个简单的自动扩展文本区域。这是我的代码:

textarea.onkeyup = function () {
  textarea.style.height = textarea.clientHeight + 'px';
}

但是 textarea 只是在您键入时无限期地增长......

我知道有 Dojo 和一个 jQuery 插件,但宁愿不必使用它们。我查看了他们的实现,最初正在使用,scrollHeight但也做了同样的事情。

您可以开始回答并使用文本区域来播放您的答案。

6个回答

在使用之前重置高度scrollHeight以正确扩展/缩小文本区域。Math.min()可用于设置 textarea 高度的限制。

代码:

var textarea = document.getElementById("textarea");
var heightLimit = 200; /* Maximum height: 200px */

textarea.oninput = function() {
  textarea.style.height = ""; /* Reset the height*/
  textarea.style.height = Math.min(textarea.scrollHeight, heightLimit) + "px";
};

小提琴:http : //jsfiddle.net/gjqWy/155

注意:IE8 及更早版本不支持input事件使用keydownkeyup使用onpaste和/或oncut如果你想支持这个古老的浏览器也是如此。

仅供参考 - 我已经发布了一个解决方案,允许您按行而不是像素进行限制:stackoverflow.com/a/24824750/126574
2021-04-22 21:47:17
+1:很好......我正在寻找一种可靠的方法来做到这一点。这是否也解释了细微差别,例如粘贴到文本区域等?
2021-05-08 21:47:17

我想让自动扩展区域受行数(例如 5 行)的限制。我已经考虑使用“em”单位,但是对于Rob 的解决方案,这很容易出错,并且不会考虑填充等内容。

所以这就是我想出的:

var textarea = document.getElementById("textarea");
var limitRows = 5;
var messageLastScrollHeight = textarea.scrollHeight;

textarea.oninput = function() {
    var rows = parseInt(textarea.getAttribute("rows"));
    // If we don't decrease the amount of rows, the scrollHeight would show the scrollHeight for all the rows
    // even if there is no text.
    textarea.setAttribute("rows", "1");

    if (rows < limitRows && textarea.scrollHeight > messageLastScrollHeight) {
        rows++;
    } else if (rows > 1 && textarea.scrollHeight < messageLastScrollHeight) {
        rows--;
    }

    messageLastScrollHeight = textarea.scrollHeight;
    textarea.setAttribute("rows", rows);
};

小提琴:http : //jsfiddle.net/cgSj3/

var rows = textarea.rows
2021-04-22 21:47:17
这是一个有趣的解决方案,但不能考虑预先存在的值,或占用超过 1 行的粘贴值。
2021-04-26 21:47:17

对于那些对 jQuery 版本的Rob W解决方案感兴趣的人

var textarea = jQuery('.textarea');
textarea.on("input", function () {
    jQuery(this).css("height", ""); //reset the height
    jQuery(this).css("height", Math.min(jQuery(this).prop('scrollHeight'), 200) + "px");
});
作者表示他们不想使用 jQuery 插件。此解决方案不是插件,而是普通的 jQuery。
2021-04-22 21:47:17
作者声明的解决方案大喊不要使用 jquery。
2021-05-04 21:47:17

...如果你需要一个无限扩展的 textarea(就像我一样),就这样做:

var textarea = document.getElementById("textarea");

textarea.oninput = function() {
  textarea.style.height = ""; /* Reset the height*/
  textarea.style.height = textarea.scrollHeight + "px";
};

与接受的答案不同,我的函数关心padding-{top,bottom}border-{top,bottom}-width它有很多参数。

功能:

// @author Arzet Ro, 2021 <arzeth0@gmail.com>
// @license CC0 (Creative Commons Zero v1.0 Universal) (i.e. Public Domain)
// @source https://stackoverflow.com/a/70341077/332012
// Useful for elements with overflow-y: scroll and <textarea>
// Tested only on <textarea> in desktop Firefox 95 and desktop Chromium 96.
export function autoResizeScrollableElement (
    el: HTMLElement,
    {
        canShrink = true,
        minHeightPx = 0,
        maxHeightPx,
        minLines,
        maxLines,
    }: {
        canShrink?: boolean,
        minHeightPx?: number,
        maxHeightPx?: number,
        minLines?: number,
        maxLines?: number,
    } = {}
): void
{
    const FN_NAME = 'autoResizeScrollableElement'
    canShrink = (
        canShrink === true
        ||
        // @ts-ignore
        canShrink === 1 || canShrink === void 0 || canShrink === null
    )

    const style = window.getComputedStyle(el)
    const lineHeightPx: number = parseFloat(style.getPropertyValue('line-height'))

    // @ts-ignore
    minHeightPx = parseFloat(minHeightPx) || 0
    //minHeight = Math.max(lineHeightPx, parseFloat(style.getPropertyValue('min-height')))
    // @ts-ignore
    maxHeightPx = parseFloat(maxHeightPx) || Infinity
    minLines = minLines ? (Math.round(minLines) > 1 ? Math.round(minLines) : 1) : 1
    maxLines = maxLines ? (Math.round(maxLines) || Infinity) : Infinity
    //console.log('%O:: old ov.x=%O ov.y=%O, ov=%O', FN_NAME, style.getPropertyValue('overflow-x'), style.getPropertyValue('overflow-y'), style.getPropertyValue('overflow'))
    /*if (overflowY !== 'scroll' && overflowY === 'hidden')
    {
        console.warn('%O:: setting overflow-y to scroll', FN_NAME)
    }*/
    if (minLines > maxLines)
    {
        console.warn('%O:: minLines > maxLines, therefore both parameters are ignored', FN_NAME)
        minLines = 1
        maxLines = Infinity
    }
    if (minHeightPx > maxHeightPx)
    {
        console.warn('%O:: minHeightPx > maxHeightPx, therefore both parameters are ignored', FN_NAME)
        minHeightPx = 0
        maxHeightPx = Infinity
    }
    const topBottomBorderWidths: number = (
        parseFloat(style.getPropertyValue('border-top-width'))
        + parseFloat(style.getPropertyValue('border-bottom-width'))
    )
    let verticalPaddings: number = 0
    if (style.getPropertyValue('box-sizing') === 'border-box')
    {
        verticalPaddings += (
            parseFloat(style.getPropertyValue('padding-top'))
            + parseFloat(style.getPropertyValue('padding-bottom'))
            + topBottomBorderWidths
        )
    }
    else
    {
        console.warn(
            '%O:: %O has `box-sizing: content-box`'
            + ' which is untested; you should set it to border-box. Continuing anyway.',
            FN_NAME, el
        )
    }
    const oldHeightPx = parseFloat(style.height)
    if (el.tagName === 'TEXTAREA')
    {
        el.setAttribute('rows', '1')
        //el.style.overflowY = 'hidden'
    }
    // @ts-ignore
    const oldScrollbarWidth: string|void = el.style.scrollbarWidth
    el.style.height = ''

    // Even when there is nothing to scroll,
    // it causes an extra height at the bottom in the content area (tried Firefox 95).
    // scrollbar-width is present only on Firefox 64+,
    // other browsers use ::-webkit-scrollbar
    // @ts-ignore
    el.style.scrollbarWidth = 'none'

    const maxHeightForMinLines = lineHeightPx * minLines + verticalPaddings // can be float
    // .scrollHeight is always an integer unfortunately
    const scrollHeight = el.scrollHeight + topBottomBorderWidths
    /*console.log(
        '%O:: lineHeightPx=%O * minLines=%O + verticalPaddings=%O, el.scrollHeight=%O, scrollHeight=%O',
        FN_NAME, lineHeightPx, minLines, verticalPaddings,
        el.scrollHeight, scrollHeight
    )*/
    const newHeightPx = Math.max(
        canShrink === true ? minHeightPx : oldHeightPx,
        Math.min(
            maxHeightPx,
            Math.max(
                maxHeightForMinLines,
                Math.min(
                      Math.max(scrollHeight, maxHeightForMinLines)
                    - Math.min(scrollHeight, maxHeightForMinLines) < 1
                    ? maxHeightForMinLines
                    : scrollHeight,
                    (
                        maxLines > 0 && maxLines !== Infinity
                        ? lineHeightPx * maxLines + verticalPaddings
                        : Infinity
                    )
                )
            )
        )
    )
    // @ts-ignore
    el.style.scrollbarWidth = oldScrollbarWidth
    if (!Number.isFinite(newHeightPx) || newHeightPx < 0)
    {
        console.error('%O:: BUG:: Invalid return value: `%O`', FN_NAME, newHeightPx)
        return
    }
    el.style.height = newHeightPx + 'px'
    //console.log('%O:: height: %O → %O', FN_NAME, oldHeightPx, newHeightPx)
    /*if (el.tagName === 'TEXTAREA' && el.scrollHeight > newHeightPx)
    {
        el.style.overflowY = 'scroll'
    }*/
}

与 React (TypeScript) 一起使用:

<textarea
    onKeyDown={(e) => {
        if (!(e.key === 'Enter' && !e.shiftKey)) return true
        e.preventDefault()
        // send the message, then this.scrollToTheBottom()
        return false
    }}
    onChange={(e) => {
        if (this.state.isSending)
        {
            e.preventDefault()
            return false
        }
        this.setState({
            pendingMessage: e.currentTarget.value
        }, () => {
            const el = this.chatSendMsgRef.current!
            engine.autoResizeScrollableElement(el, {maxLines: 5})
        })
        return true
    }}
/>

因为 ReactonChange就像oninput在 HTML5 中一样,所以如果你不使用 React,那么使用input事件。


答案之一使用rows属性(而不是height像我上面的代码那样使用CSS ),这是一个几乎未经测试的替代实现,它不使用外部变量:

// @author Arzet Ro, 2021 <arzeth0@gmail.com>
// @license CC0 (Creative Commons Zero v1.0 Universal) (i.e. Public Domain)
// @source https://stackoverflow.com/a/70341077/332012
function autoResizeTextareaByChangingRows (
    el,
    {minLines, maxLines}
)
{
    minLines = minLines ? (Math.round(minLines) > 1 ? Math.round(minLines) : 1) : 1
    maxLines = maxLines ? (Math.round(maxLines) || Infinity) : Infinity
    el.setAttribute(
        'rows',
        '1',
    )
    const style = window.getComputedStyle(el)
    const rows = Math.max(minLines, Math.min(maxLines,
        Math.round(
            (
                el.scrollHeight
                - parseFloat(style.getPropertyValue('padding-top'))
                - parseFloat(style.getPropertyValue('padding-bottom'))
            ) / parseFloat(style.getPropertyValue('line-height'))
        )
    ))
    el.setAttribute(
        'rows',
        rows.toString()
    )
}

const textarea = document.querySelector('textarea')
textarea.oninput = function ()
{
    autoResizeTextareaByChangingRows(textarea, {maxLines: 5})
}