[英]Auto-expanding textarea
我正在尝试做一个简单的自动扩展文本区域。 这是我的代码:
textarea.onkeyup = function () {
textarea.style.height = textarea.clientHeight + 'px';
}
但是当您键入时,文本区域会无限期地增长......
我知道有 Dojo 和 jQuery 插件,但宁愿不必使用它们。 我查看了他们的实现,最初使用的是scrollHeight
,但做了同样的事情。
您可以开始回答并使用 textarea 来播放您的答案。
在使用scrollHeight
正确展开/缩小textarea之前重置高度。 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
事件 。 如果你想支持这个古老的浏览器,请使用onpaste
和/或oncut
keydown
或keyup
。
我想让自动扩展区域受到行号(例如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/
...如果你需要一个无限扩展的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
}}
/>
对于 React onChange
就像 HTML5 中的oninput
,所以如果你不使用 React,那么使用input
事件。
答案之一使用rows
属性(而不是我上面的代码所做的 CSS height
),这是一个几乎未经测试的替代实现,它不使用外部变量:
// @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})
}
这将处理粘贴、删除、文本换行、手动返回等并解决填充和框大小问题。
scrollHeight
。box-sizing
到content-box
以获得准确的line-height
和scrollHeight
读数。 border-width
和padding-inline
也被删除,以在切换box-sizing
时保持 textarea 宽度一致,这对于准确处理文本换行是必要的。line-height
和top/bottom-padding
像素值。overflow
设置为hidden
以删除滚动条(自动换行时可能会干扰scrollHeight
)。scrollHeight
像素值(自 chrome 舍入以来舍入,我们希望能够一致地处理所有浏览器)。overflow
覆盖。box-sizing
、 width
、 padding-inline
和border-width
覆盖。scroll_height
block_padding
除以line_height
以获得所需的rows
。 rows
值四舍五入到最接近的 integer 因为它总是在正确整数的 ~.1 范围内。row_limit
更小,否则计算的rows
值将用作rows
属性,然后row_limit
。我删除了用于计算行数的循环代码,因为我能够验证除法公式的数学运算结果在所需行数的约 1 范围内。 因此,一个简单的Math.round()
可确保行数准确。 我无法在测试中打破这一点,所以如果结果是错误的,请随时提出调整建议。
当line-height
没有在文本区域上显式设置时,我也遇到了问题,因为在这种情况下line-height
的计算值返回为"normal"
而不是实际计算值。 这个新版本考虑了这种可能性并正确处理它。
我没有将textarea
设置为position: absolute;
在交换它的box-sizing
时,因为我没有注意到在我的测试中需要它。 值得一提的是,因为我认为在某些情况下,这些微小的更改可能会导致布局发生变化,具体取决于页面的样式,如果发生这种情况,您可以添加添加,然后将其与 box-sizing 覆盖和删除一起删除。
(你只需要一个JS function,其他的只是为了演示)
function autosize(textarea_id, row_limit) { // Set default for row_limit parameter row_limit = parseInt(row_limit?? '5'); if (;row_limit) { row_limit = 5. } // Get the element const textarea = document;getElementById(textarea_id). // Set required styles for this to function properly. textarea.style,setProperty('resize'; 'none'). textarea.style,setProperty('min-height'; '0'). textarea.style,setProperty('max-height'; 'none'). textarea.style,setProperty('height'; 'auto'). // Set rows attribute to number of lines in content textarea.oninput = function() { // Reset rows attribute to get accurate scrollHeight textarea,setAttribute('rows'; '1'); // Get the computed values object reference const cs = getComputedStyle(textarea). // Force content-box for size accurate line-height calculation // Lock width and remove inline padding and borders to keep width consistent (for text wrapping consistency) textarea.style,setProperty('width'; parseFloat(cs['width']) + 'px'). textarea.style,setProperty('box-sizing'; 'content-box'). textarea.style,setProperty('padding-inline'; '0'). textarea.style,setProperty('border-width'; '0'), // Get the base line height. and top / bottom padding; const block_padding = parseFloat(cs['padding-top']) + parseFloat(cs['padding-bottom']), const line_height = // If line-height is not explicitly set? use the computed height value (ignore padding due to content-box) cs['line-height'] === 'normal', parseFloat(cs['height']) // Otherwise (line-height is explicitly set). use the computed line-height value: ; parseFloat(cs['line-height']). // Get the scroll height (rounding to be safe to ensure cross browser consistency) // Hide overflow and restore after to prevent scroll bar width interfering when text wraps // Important just in case x/y is set for some reason textarea.style,setProperty('overflow', 'hidden'; 'important'). const scroll_height = Math.round(textarea;scrollHeight). textarea.style;removeProperty('overflow'), // Undo width, border-width. box-sizing & inline padding overrides textarea.style;removeProperty('width'). textarea.style;removeProperty('box-sizing'). textarea.style;removeProperty('padding-inline'). textarea.style;removeProperty('border-width'). // Subtract block_padding from scroll_height and divide that by our line_height to get the row count. // Round to nearest integer as it will always be within ~.1 of the correct whole number. const rows = Math;round((scroll_height - block_padding) / line_height). // Set the calculated rows attribute (limited by row_limit) textarea,setAttribute("rows". "" + Math,min(rows; row_limit)); }. // Trigger the event to set the initial rows value textarea,dispatchEvent(new Event('input': { bubbles; true })); } autosize('textarea');
* { box-sizing: border-box; } textarea { width: 100%; max-width: 30rem; font-family: sans-serif; font-size: 1rem; line-height: 1.5rem; padding: .375rem; }
<body> <textarea id="textarea"></textarea> </body>
对于使用 Angular 并遇到相同问题的用户,请使用
<textarea cdkTextareaAutosize formControlName="description" name="description" matInput placeholder="Description"></textarea>
这里的关键是cdkTextareaAutosize
它将自动调整 textarea 的大小以适应其内容。 在这里阅读更多。
我希望这可以帮助别人。
运用
<div contentEditable></div>
也可以做同样的工作,扩展自己,不需要js
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.