应使用 input 事件监听 textarea 实时统计字数,配合 maxlength 属性基础限制,并在 input 中截断超长内容且手动触发事件更新计数;字节统计需用 TextEncoder 或遍历码点判断。

怎么给 <textarea> 加实时字数统计
直接在 <textarea> 旁边放一个只读 <span> 或 <output>,用 input 事件监听输入变化,取 textarea.value.length 就是当前字符数。别用 keyup —— 它捕获不到粘贴、拖入文本、IME 输入(比如中文拼音上屏)等操作,会漏统计。
常见错误是把计数逻辑写在 change 事件里,这个事件只在失焦时触发,完全不适合实时反馈。
示例关键代码:
<textarea id="note"></textarea>
<output id="count">0</output> / 200
<script>
const ta = document.getElementById('note');
const out = document.getElementById('count');
ta.addEventListener('input', () => {
out.textContent = ta.value.length;
});
</script>
限制最大长度并阻止超限输入
<textarea maxlength="200"> 在现代浏览器中有效,但只是“阻止键入”,不阻止粘贴超长内容,也不影响通过 JS 赋值。真要严控,得在 input 事件里截断:
立即学习“前端免费学习笔记(深入)”;
- 检查
textarea.value.length > 200,如果是,立即执行textarea.value = textarea.value.slice(0, 200) - 截断后手动触发
input事件(textarea.dispatchEvent(new Event('input'))),否则计数器不会更新 - 注意:不要用
keydown+preventDefault()拦截,它会破坏 IME 输入体验,中文用户打字会卡顿或丢字
按「字节数」而非「字符数」计数(比如适配后端存储限制)
中文、emoji 等字符在 UTF-8 下占多个字节,而 .length 返回的是 Unicode 码点数。如果后端字段是 VARCHAR(500) CHARACTER SET utf8mb4,实际限制的是字节数。
简单估算可用 new TextEncoder().encode(str).length,但注意兼容性(IE 不支持);更稳妥的做法是遍历字符串,对每个码点用 str.codePointAt(i) 判断是否大于 0xFFFF(即是否为 4 字节 emoji):
function getUtf8ByteLength(str) {
let len = 0;
for (let i = 0; i < str.length; i++) {
const cp = str.codePointAt(i);
if (cp < 0x80) len += 1;
else if (cp < 0x800) len += 2;
else if (cp < 0x10000) len += 3;
else len += 4;
}
return len;
}
这个函数比 TextEncoder 兼容老浏览器,且不依赖 polyfill。
移动端软键盘弹出时计数器被遮挡怎么办
这不是计数器本身的问题,而是布局问题。iOS Safari 和部分安卓 WebView 在软键盘弹出时不会自动滚动到聚焦的 <textarea>,导致底部计数器不可见。
解决方法很直接:
- 确保
<textarea>和计数器容器有明确的position: relative,且父容器高度不固定(别设height: 100vh) - 在
focus事件里调用textarea.scrollIntoView({ behavior: 'smooth', block: 'nearest' }) - 避免给计数器加
position: fixed或绝对定位到底部——它会脱离文档流,在键盘弹出时失去参照
最易忽略的是:某些 UI 框架(如 Ionic、Cordova)会注入自己的键盘处理逻辑,覆盖原生行为,这时得查对应框架的 keyboard 插件配置,而不是硬改 CSS。


















