先记住这个答案
复选框的 indeterminate 是视觉状态,通常显示为横杠,和表示真正勾选与否的 checked 独立。全选场景里有些子项被选中、有些没有时,可以把全选框设为混合态。它不会改变表单成功控件的提交规则,提交时不能指望服务端收到第三种布尔值。
- 混合态由 DOM 属性设置
checked仍是独立布尔状态- 提交不包含混合态标记
- 子项变化后要重新计算全选框
全选框如何跟着子项变化
把子项状态当事实来源:选中数为零时,全选框不选中且不混合;全部选中时,全选框选中且不混合;介于两者之间时,显示混合态。每次子项变化都重新计算,避免只在首次渲染时写一次属性。
const parent = document.querySelector("#all")
const children = [...document.querySelectorAll(".item-check")]
function syncAll() {
const count = children.filter(el => el.checked).length
parent.checked = count === children.length
parent.indeterminate = count > 0 && count < children.length
}
children.forEach(el => el.addEventListener("change", syncAll))
syncAll()例子中的 indeterminate 是 DOM 属性赋值,不是写在标签上的持久 HTML 属性。子项列表若动态增删,闭包里固定的 children 也要更新,或者重新查询当前列表。
点击混合态后怎样处理
用户点击全选框时,通常按产品约定把所有子项设为全选或全不选,再调用同步函数重算父框。不要仅把 parent.indeterminate 从 true 改成 false,否则子项仍保留原状态,界面与事实不一致。
如果要把结果发给服务端,提交每个子项的真实值或一份明确的选中 ID 列表。全选框本身若有 name,是否提交由它的 checked 状态决定;视觉横杠不会变成第三种表单数据。若子项被远程数据刷新,也需要在数据更新后重新同步。
提交数据时要特别小心:indeterminate 只改变复选框的视觉状态,不改变 checked,也不会把“部分选中”自动提交成第三种值。比如四个子项选了两个,顶部全选框可以显示横线,但后端仍应接收各子项的选中值。顶部复选框若有 name,它是否进入表单数据只取决于 checked。页面重新渲染后还要按子项状态重新设置 indeterminate,因为它不是 HTML 属性,不能只靠初始标记恢复。
容易答错的地方
- 把
indeterminate写成 HTML 属性就算完成 - 它是需要通过脚本设置的 DOM 状态,组件重新挂载后也要再次同步。
- 认为混合态会自动计算
- 浏览器不会根据子复选框自动推导父框;应用必须维护两者关系。
面试官还会怎么问?
混合态的框提交什么值?
仍按普通复选框的 checked 与 name 规则提交,没有专门的第三种值。
父框和子框谁应当作为数据来源?
通常子项的选中集合才是事实,父框只是其汇总显示与批量操作入口。
React 中怎样设置混合态?
通过引用在渲染后给底层输入元素赋 indeterminate,并在选中集合变化时同步。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。