maxlength 数的是 UTF-16 code unit,不是字符
maxlength="6" 读起来像「最多 6 个字」,但规范说的是「值的长度不超过 6」,而这个长度指的是 UTF-16 code unit 数——也就是 JS 里的 str.length。对 ASCII 与多数中文字符,一个字符正好是一个 code unit,差别看不出来;一旦遇到
emoji、罕用汉字、组合字符,「字符数」和「code unit 数」就会分道扬镳。
1 · 对着一个真实的 maxlength 输入框打字
下面是一个 <input maxlength="8">。试着粘贴或用按钮塞入 emoji——你会发现远不到「8 个字」就被拦住,因为浏览器拦的是第 8 个 code unit:
2 · 逐字素拆开:每个「字」占几个 code unit
把任意一段文本逐 字素 (grapheme) 拆开,标出每个字素由哪些 code point 组成、合计几个 UTF-16 code unit。边框更醒目的格子表示这个「字」不止 1 个 code unit——它在 maxlength 眼里被算作多个:
为什么是 UTF-16? JavaScript 字符串、DOM 的文本内容在内存里都以 UTF-16 表示。基本多文种平面 (BMP) 内的字符占 1 个 code unit;之外的字符(emoji、𝕏、罕用字…)用一对 surrogate 表示,即 2 个 code unit。str.length 数的是 code unit,所以
'😀'.length === 2。maxlength 直接沿用了这套长度。Unicode 字符模型的完整脉络见 Regex × Unicode 系列。
3 · 几个连带的陷阱
| 情形 | 行为 |
|---|---|
脚本赋值 el.value = '...' |
maxlength 不拦。它只约束用户输入与粘贴,程序赋值可超长——提交前需自行校验。 |
<textarea> 里的换行 |
提交时换行归一化为 CRLF(2 个 code unit),maxlength 也按归一化后计数,可能比你数到的行数更「吃额度」。 |
type="number" 上的 maxlength |
无效。数字类控件不认 maxlength,要限位数得用 min / max 或改 type="text" inputmode="numeric"(见 inputmode 页)。 |
| 想按「肉眼字数」限制 | HTML 没有现成属性。需 JS 监听 input,用 Intl.Segmenter 数字素后截断——但要小心别把 surrogate pair 拦腰截断成乱码。 |