必须每次操作后重新扫描DOM并按data-type分组重排——用querySelectorAll过滤可见项、Array.from转数组、forEach重设title,绑定事件委托实现删后即重排,确保序号语义严格对应当前DOM位置与类型。

直接操作 DOM 是唯一可靠路径——任何试图靠纯 HTML 或服务端重载实现“动态升级”的方案,都会在删除中间项、插入新字段、或跨类型混排时崩坏序号语义。
用 querySelectorAll 实时抓取可见项,别信全局计数器
依赖 counter++ 或闭包变量的编号逻辑,在用户删掉第 2 项后,第 3 项仍会叫 “Third”,但实际是列表里第 2 个。必须放弃状态缓存,每次操作后重新扫描 DOM:
-
document.querySelectorAll('.addSections > *:not([style*="display: none"])')—— 过滤掉被隐藏(非移除)的项 - 用
Array.from()转成数组再forEach((el, index) => {...}),确保索引从 0 开始连续 - 避免用
getElementsByClassName,它返回的是 live collection,遍历时可能因 DOM 变动引发不可预测行为
按 data-type 分组重排,防止日期和时段标题错乱
如果页面同时存在预约日期(Date)和预约时段(Time)两类字段,混在一起编号会导致语义污染:“Third Appointment Start Date” 可能指向一个 Time 字段。必须按类型隔离处理:
- 给容器加
data-type="Date"或data-type="Time" - 重排时先
document.querySelectorAll('[data-type="Date"]'),再单独处理Time组 - 每个组内只对
input[type="date"]、select[data-role="time"]等目标元素设title,跳过按钮、说明文字等干扰节点
绑定一次事件委托,删完立刻重排,不等下一次渲染
不要为每个删除按钮单独绑 onclick,更别用 setTimeout 延迟重排——DOM 更新和重排必须同步发生,否则用户看到的是“删了但标题没变”的错觉:
立即学习“前端免费学习笔记(深入)”;
- 监听父容器的
click事件,用event.target.matches('.delete-btn')判断是否点中删除图标 - 拿到
event.target.closest('.form-item')找到对应区块,调用.remove()或.style.display = 'none' - 紧接着调用重排函数,传入该容器的
dataset.type和子项选择器 - 删除后立即执行,不依赖
requestAnimationFrame或mutationObserver,它们引入额外延迟且难以控制执行时机
真正难的不是写对第一次编号,而是保证任意次增删后,每个 title 都严格对应它当前在 DOM 中的位置与类型——这要求你放弃“记住序号”的惯性思维,转而信任结构本身。



















