如何创建 textarea
<textarea> 是可以输入多行内容的文本框,适合留言、评论和描述。rows 以行数设置它的可见高度。输入几行文字,然后点击 Send:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Textarea</title>
</head>
<body>
<form>
<label for="msg">Message</label><br>
<textarea id="msg" name="message" rows="4" cols="40" placeholder="What would you like to ask?"></textarea>
<br>
<button type="submit">Send</button>
</form>
</body>
</html>Master HTML the hands-on way
Interactive lessons, real projects, and instant feedback.
START NOW
它和 <input> 有两点不同:textarea 需要结束标签,而且它的初始文本写在标签之间,而不是写在 value 属性里。提交时,它的 name 和文本一起发送,文本作为值,其中的换行也会保留。
textarea 属性
属性 作用 name发送文本时使用的键名 rows以行数计的可见高度(默认 2) cols以字符数计的可见宽度(默认 20);通常用 CSS width 更好 placeholder内容为空时显示的提示 maxlength, minlength以字符计的长度限制 required不能为空 readonly可以选中和复制,但不能编辑;仍会发送 disabled无法使用;不会发送 wrapsoft(默认)或 hard,见下文spellchecktrue 或 false
默认文本写在标签之间
<textarea> 和 </textarea> 之间的所有内容都是初始文本,原样保留:空格、制表符和换行都算在内。只有紧跟在开始标签后面的一个换行会被忽略。所以缩进是一个陷阱:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Default text</title>
<style>
textarea { display: block; margin-bottom: 12px; font: inherit; }
</style>
</head>
<body>
<textarea rows="2">Hello, this is the default text.</textarea>
<textarea rows="3">
Indented to match the HTML
</textarea>
<p id="out"></p>
<script>
const [a, b] = document.querySelectorAll('textarea');
document.querySelector('#out').textContent =
'Lengths: ' + a.value.length + ' and ' + b.value.length +
' (the second adds 4 leading spaces, a line break and 2 more spaces)';
</script>
</body>
</html>
第二个 textarea 以四个空格开头,又以一个换行加两个空格结尾,读者必须把它们全部删掉。默认文本要紧贴标签书写。
textarea 里的 HTML 不会被渲染;标签之间的 <b>hi</b> 会按字面显示成这段文字。
宽度、调整大小与自动增高
浏览器允许读者拖动右下角来调整 textarea 的大小。CSS resize 可以限制这一点,而 width: 100% 加上 box-sizing: border-box 能让它正好填满容器:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Resize</title>
<style>
body { font-family: system-ui, sans-serif; }
textarea {
display: block;
width: 100%;
box-sizing: border-box;
margin: 4px 0 14px;
padding: 8px;
font: inherit;
border: 2px solid #9ca3af;
border-radius: 8px;
}
.vertical { resize: vertical; min-height: 60px; }
.none { resize: none; }
.grow { resize: none; overflow: hidden; }
</style>
</head>
<body>
<label>resize: vertical <textarea class="vertical" rows="2"></textarea></label>
<label>resize: none <textarea class="none" rows="2"></textarea></label>
<label>grows as you type <textarea class="grow" rows="2" id="grow"></textarea></label>
<script>
const grow = document.querySelector('#grow');
grow.addEventListener('input', () => {
grow.style.height = 'auto';
const border = grow.offsetHeight - grow.clientHeight;
grow.style.height = grow.scrollHeight + border + 'px';
});
</script>
</body>
</html>
最后一个在每次按键时先重置高度,再把高度设为 scrollHeight(内容加内边距的高度)再加上边框。在里面输入几行试试:它永远不会出现滚动条。resize: vertical 是最友好的默认选择:长回答有足够空间,也没人能把输入框拖得比布局还宽。
Chromium 内核的浏览器只用 CSS 的 field-sizing: content 就能实现自动增高。不支持它的浏览器会忽略这一行,保持正常大小,所以加上它是安全的;但如果希望每位读者都能看到自动增高的输入框,就保留这段脚本。
maxlength 与字数统计
maxlength 在达到指定字符数时停止输入。单独使用时它不会给出任何提示,读者只有在打不出字时才会发现。加一个计数器就能解决这个问题:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Character counter</title>
<style>
body { font-family: system-ui, sans-serif; }
textarea { display: block; width: 100%; box-sizing: border-box; font: inherit; padding: 8px; }
.count { font-size: 14px; color: #4b5563; }
.count.near { color: #b45309; font-weight: 600; }
</style>
</head>
<body>
<form>
<label for="bio">Short bio</label>
<textarea id="bio" name="bio" rows="3" maxlength="100" aria-describedby="bio-count"></textarea>
<p class="count" id="bio-count" aria-live="polite">100 characters left</p>
<button type="submit">Save</button>
</form>
<script>
const bio = document.querySelector('#bio');
const count = document.querySelector('#bio-count');
bio.addEventListener('input', () => {
const left = bio.maxLength - bio.value.length;
count.textContent = left + ' characters left';
count.classList.toggle('near', left <= 20);
});
</script>
</body>
</html>
aria-describedby 让屏幕阅读器在读输入框时一并读出计数器,aria-live="polite" 则会在按键间隙播报它的变化。maxlength 只由浏览器执行,所以服务器也必须检查长度。
提交文本中的换行
读者输入的换行会随文本一起发送。在提交的数据中,每个换行都会变成一对回车符和换行符(在 URL 编码的表单中是 %0D%0A),这也是大多数服务器所期望的格式。之后要在页面上显示这段文本并保留换行,可以在输出时把换行转换成 <br>,或者给元素加上 white-space: pre-line 样式。
wrap="hard"(需要配合 cols)更进一步:它还会在文本视觉上自动折行的位置插入换行,这样服务器收到的每一行都不超过 cols 个字符。默认的 wrap="soft" 只发送读者自己输入的换行。
设置 textarea 样式
Chrome 和 Firefox 给 textarea 使用约 13px 的等宽字体,这就是 textarea 常常显得格格不入的原因。font: inherit 可以解决这个问题。如果你修改了边框,要保留清晰可见的焦点样式:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Styled textarea</title>
<style>
body { font-family: system-ui, sans-serif; font-size: 16px; }
textarea { display: block; margin: 4px 0 12px; }
.styled {
font: inherit;
padding: 10px 12px;
border: 1px solid #9ca3af;
border-radius: 8px;
resize: vertical;
}
.styled:focus-visible { outline: 3px solid #2563eb; outline-offset: 1px; border-color: #2563eb; }
.styled::placeholder { color: #6b7280; }
</style>
</head>
<body>
<label>Browser default <textarea rows="2">Monospace, small</textarea></label>
<label>Styled <textarea class="styled" rows="2">Same font as the page</textarea></label>
</body>
</html>
placeholder 文本的对比度要足够清晰;很多设计用的浅灰色很难看清。而且和所有输入框一样,placeholder 不是标签:读者一开始输入它就消失了。标签的用法见 HTML label 。
常见错误
<textarea value="...">。 它没有 value 属性;要把文本写在标签之间。
让默认文本跟着 HTML 一起缩进。 这些空格会成为值的一部分。
自闭合的 <textarea />。 它不是空元素;没有 </textarea>,页面的剩余部分都会被当作文本吞进去。
没有写 font: inherit ,导致输入框使用很小的等宽字体。
resize: none 配上很小的固定高度。 长回答只能挤在一个需要滚动的小框里。
只依赖 maxlength。 在服务器端也要验证长度。