导读:本期聚焦于小伙伴创作的《如何在动态表单中新增输入项并自动更新 title 属性且删除后重排序》,敬请观看详情。表单在运行期动态增减输入项时,每个框的 title 属性若仍保留旧序号,屏幕阅读器会读错位置。用数组映射渲染可让索引即标题,删除某项后调用 splice 并依靠框架重渲染,title 随数据自动连续。相比手动改 DOM 属性,数据驱动方案不会漏更、不冲突,维护成本更低。下文给出原生 JS 与 Vue 两种实现,并说明重排序时如何避免焦点丢失与表单值错位。

在动态表单场景下,用户经常需要点击按钮追加输入框,或者删除某一行后让剩余输入框的序号和辅助说明保持连续。如果只操作 DOM 而不同步数据源,新增项的 title 属性容易写成固定值,删除中间项后后面的 title 也不会自动前移,导致无障碍访问和提示信息错乱。正确的做法是让 title 完全由数据索引决定,任何增删都先改数据,再由渲染逻辑生成正确的 title。

如何在动态表单中新增输入项并自动更新 title 属性且删除后重排序

为什么 title 属性需要跟随数据重排

title 属性在浏览器中表现为鼠标悬停提示,同时也被部分屏幕阅读器用于朗读表单控件的描述。当表单支持动态删除时,假如第二项被移除,原本第三项的 title 若仍写作“第3项”,对用户而言就变成了逻辑上的“第2项”,产生认知偏差。此外,手动用 setAttribute 去改每一个 DOM 节点的 title,在频繁操作时会遗漏节点,尤其在虚拟 DOM 或异步渲染环境中更不可靠。

从维护角度看,把 title 硬编码在 HTML 里,意味着增删逻辑和视图描述耦合在一起。一旦产品要求 title 文案变化,比如从“第N项”改成“候选人N”,就得到处改代码。用数据索引生成 title,只需改一处模板或渲染函数,所有项自动一致,也天然支持删除后的重排序。

原生 JavaScript 实现方案

下面用最基础的 JS 展示一个可新增、可删除、且 title 自动连续的表单。核心是一个数组 items,渲染函数根据数组下标输出 input,并在 title 中放入当前索引加一的值。删除时调用 splice 改变数组,再重新渲染即可。

let items = ['', ''];

function renderForm() {
  const box = document.getElementById('formBox');
  box.innerHTML = '';
  items.forEach((val, idx) => {
    const wrap = document.createElement('div');
    const input = document.createElement('input');
    input.type = 'text';
    input.value = val;
    // title 由索引决定,删除后重渲染自动连续
    input.title = '第' + (idx + 1) + '项';
    input.oninput = (e) => {
      items[idx] = e.target.value;
    };
    const del = document.createElement('button');
    del.textContent = '删除';
    del.onclick = () => {
      items.splice(idx, 1);
      renderForm();
    };
    wrap.appendChild(input);
    wrap.appendChild(del);
    box.appendChild(wrap);
  });
}

document.getElementById('addBtn').onclick = () => {
  items.push('');
  renderForm();
};

renderForm();

上述代码在每次增删后都整体重渲染,title 必然正确。但它的缺点是重渲染会丢失已有输入框的焦点,且如果输入框内有复杂状态,直接 innerHTML 清空会打断交互。在原生方案中,若想保留焦点,可以改为只增删差异节点,但那样就需要手动同步 title,复杂度上升。

一个折中办法是在删除时只移除对应 DOM 并遍历后续节点重设 title,而不重建全部。这样既能保焦点,又让 title 连续。不过只要数据仍是单一数据源,逻辑就不容易出错。下面的 Vue 示例展示了数据驱动如何天然规避这些麻烦。

Vue 中的声明式实现

在 Vue 里,我们用 v-for 渲染列表,并将 title 绑定为索引表达式。由于 Vue 的响应式系统会在数组变化后高效更新 DOM,title 会随着 key 的排布自动更新,不需要手写重排逻辑。

<div id="app">
  <div v-for="(item, idx) in items" :key="idx">
    <input type="text" v-model="items[idx]" :title="'第' + (idx + 1) + '项'">
    <button @click="remove(idx)">删除</button>
  </div>
  <button @click="add">新增</button>
</div>

<script>
new Vue({
  el: '#app',
  data() {
    return { items: ['', ''] };
  },
  methods: {
    add() { this.items.push(''); },
    remove(i) { this.items.splice(i, 1); }
  }
});
</script>

这里 :title 绑定了 '第' + (idx + 1) + '项',删除中间项后 Vue 会复用并更新对应 DOM 的 title,不会出现断号。需要注意,使用 index 作为 key 在数组 splice 时可能导致输入值错位,如果输入框内有独立状态,建议给每个 item 加稳定 id 作为 key,title 仍由显示索引计算。

如果担心 splice 带来的复用问题,可以把数据项设计为对象数组,例如 items: [{id: 1, val: ''}],key 用 id,title 用 items.indexOf(item) + 1 来算。这样删除后 indexOf 变化,title 自动前移,且输入框的值不会因位置变动而串号。

重排序时的常见坑与建议

第一,不要试图在删除按钮里用 DOM 查找去改后续兄弟节点的 title,这种命令式做法在框架中会和虚拟 DOM 冲突,造成状态不一致。第二,如果表单需要提交,提交前应从数据数组取值,而不是读 DOM,这样 title 的排布和值的位置天然对应。

第三,当输入项很多时,整体重渲染可能影响性能,此时应依赖框架的差异化更新,或原生中使用 DocumentFragment 批量操作。最后,title 只是辅助提示,复杂说明应配合 aria-label<label> 标签,但排布原则相同:都由数据索引生成,删除即重排。

小结

动态表单中新增项与删除后重排序的本质,是把视图描述交给数据索引。无论是原生 JS 的 render 函数,还是 Vue 的 v-for 绑定,只要 title 由 idx + 1 算出,就能保证新增有正确提示、删除后不断号。避免手动改属性,不仅减少 bug,也让代码更易扩展。

dynamic_forminput_titleauto_reorder修改时间:2026-08-07 05:51:14

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。