← HTML 表单:从控件到提交与校验 / maxlength 数的是 UTF-16 code unit,不是字符 待审核 16 / 24
maxlength · code unit

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 === 2maxlength 直接沿用了这套长度。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 拦腰截断成乱码。