maxlength 仅按 UTF-16 码元计数,导致生僻字、emoji 和输入法未上屏时长度判断偏差;需结合 compositionend 事件、String.prototype.slice 裁剪、光标位置保存及前后端统一用 grapheme cluster 统计,才能准确限制用户感知的字符数。

能限制,但只按 UTF-16 码元(code unit)计数,不是用户感知的“字”或“字符”,绝大多数中文没问题,但遇到生僻字、emoji 或输入法未上屏状态时会出偏差。
maxlength 对中文输入的实际表现
浏览器把 maxlength 当作字符串 .length 的硬上限:一个常见汉字(如“中”“国”)在基本多文种平面(BMP)内,占 1 个码元,maxlength="10" 就真能输 10 个;但像 U+20BB7(?)这种辅助平面汉字,会被拆成两个代理对,.length 为 2,maxlength="10" 实际最多存 5 个这类字。
- 拼音输入法打“zhongguo”时,value 仍是空的,
maxlength完全不触发 - 用户按空格确认,“中国”上屏,value 突然变成 2 字符,此时才检查是否超限
- 拖入一段含 emoji 的文本(如“你好??”),即使显示为 4 个“字”,
.length可能是 6 或 8,maxlength截断位置可能切在 emoji 中间,显示为
input 事件截断必须配合 compositionend
只监听 input 事件不够——中文输入法处于组词状态时,input 不触发;得同时监听 compositionstart 和 compositionend,在上屏完成那一刻再做长度判断和裁剪。
- 用
event.isComposing判断当前是否在输入法组合中,避免中途截断干扰候选词 - 裁剪必须用
String.prototype.slice(0, N),不能用substr或正则,否则可能撕裂代理对 - 直接赋值
el.value = ...会让光标跳到末尾;需提前用el.getSelectionRange()记录位置,裁剪后再用el.setSelectionRange()恢复
服务端校验必须和前端逻辑一致
前端用 Array.from(value).length 统计真实字形簇(grapheme cluster)个数,后端就不能只用 Python 的 len(str) 或 Node.js 的 str.length——它们都返回 UTF-16 码元数,和前端不等价。
立即学习“前端免费学习笔记(深入)”;
- Python 推荐用
regex.findall(r'\X', str)或unicodedata拆分字形簇 - Node.js 应保持和前端一致:
Array.from(str).length - MySQL
VARCHAR(100)是按字节数定义的(utf8mb4 下 1 字符最多占 4 字节),字段宽度要按最大字节预留,不能只看字符数
真正难的不是写一行 slice,而是让输入法上屏、光标定位、字形统计、数据库字段、后端校验全部对齐——漏掉任意一环,用户在安卓手机上输到第 99 个字时,就可能看到提交失败或乱码。



















