应监听 input 事件而非 keyup 或 change,因其能实时捕获粘贴、输入法上屏等所有值变更;计数优先用 value.length,视觉字数要求高时用 array.from(el.value).length;超限时需截断并 setselectionrange() 保光标。

只监听 input 事件,别碰 keyup 或 change
用户粘贴、语音输入、中文输入法上屏、拖放文本——这些操作 keyup 全部捕获不到;change 要等失焦才触发,根本不是“实时”。input 是唯一在值真正改变后触发的事件,且 IE11+、iOS Safari、Android Chrome 全支持。
常见错误:用 keyup 写了计数逻辑,结果用户右键粘贴完字数不动;或用 change,写完 200 字却直到点别处才提示超限。
- 必须绑定到
<textarea></textarea>或<input type="text">元素本身,不是父容器 - 不要重复绑定:如果动态插入新输入框,需单独为它加监听,不能靠事件委托(
input不冒泡) - 移动端 iOS Safari 偶有 50ms 左右延迟,可加
setTimeout(() => update(), 50)防 UI 闪动,但别超 100ms
value.length 和 Array.from(el.value).length 怎么选
value.length 返回 UTF-16 码元个数,对普通中英文、数字、标点都准,性能快;但遇到部分 emoji(如 ??)或生僻汉字会返回 2,而用户只当 1 个字。
如果业务要求“显示字数 = 用户视觉感知字数”(比如微博、微信文案),必须用 Array.from(el.value).length;如果只是前端友好提示 + 后端兜底校验,value.length 更轻量、够用。
- 后端若按 MySQL
utf8mb4字节长度校验(一个 emoji 占 4 字节),JS 无法精确模拟,前端只做提示,以服务端返回为准 - 空格、换行符(
\n)、制表符(\t)默认各算 1 字,符合多数编辑场景直觉 - 别用
innerText或textContent取值——前者受 CSS 影响,后者对<textarea></textarea>和<input>没意义
超限时怎么处理才不丢光标、不跳字
禁止输入不可行:preventDefault() 在 input 里基本无效,尤其对 iOS 输入法。真正可靠的是“截断 + 重置光标”。
核心三步:先记当前光标位置 → 截断字符串 → 用 setSelectionRange() 把光标放回末尾。
- 不这么做会怎样:用户在第 15 字位置粘贴 300 字文本,光标跳到开头或末尾,继续打字就覆盖原内容或“跳字”
- iOS Safari 对
setSelectionRange()支持不稳定,可降级为仅加class="over-limit"提示,不主动截断 - 粘贴时超限需额外处理:
paste事件中调e.clipboardData.setData('text/plain', truncatedText),但 Safari 限制严,降级为粘贴后立即el.value = truncatedText
maxlength 和 JS 实时统计为什么总打架
浏览器 maxlength 按 UTF-16 码元截断,JS 用 Array.from().length 算 Unicode 码点,同一段文本两者结果可能差 1–2 位。更麻烦的是,中文输入法组合过程中 input 不触发,用户看到“还剩 3 字”,上屏瞬间直接爆限。
解决思路不是废掉 maxlength,而是协调:前端提示留余量,后端校验定乾坤。
- 设
maxlength="102"但提示“最多 100 字”,防输入法临界错位 - 别让 JS 主动
el.value = el.value.slice(0, max)后不调setSelectionRange() - 如果用了
contenteditable,不能直接取textContent——要遍历 DOM,把<br>、<p></p>转成换行符再统计
最常被忽略的一点:计数单位没对齐。前端显示“还剩 5 字”,后端按字节判定超限,用户提交失败——问题不在代码,而在前后端约定没写进文档。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











