You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

动态增删表单字段时Select2功能随机失效问题求助

动态增删Select2字段时随机丢失组件功能的问题

我有一个包含单个select字段的表单,已为其绑定Select2组件。通过JavaScript在客户端动态增删此类字段时,会随机出现某一新增字段丢失Select2功能的情况。

复现步骤

  • 点击两次Add more
  • 点击第一个新增字段(列表中第二个字段)的Remove
  • 再次点击Add more

逻辑说明

字段ID格式为field-x-instance,其中x为字段的数字索引:

  • 删除字段时,会将被删字段之后的所有字段的索引减1,修改id、for、name等属性的索引值
  • 新增字段时,克隆隐藏的模板字段(ID为field-_-instance),将下划线替换为当前字段总数对应的索引值

初步排查结果

若使用单调递增的计数器为新字段分配索引,该问题不会出现。删除字段时索引调整正常,仅在以当前字段数为新字段索引(0基)新增字段时,会随机出现该问题。

代码实现

JavaScript

document.addEventListener('DOMContentLoaded', function(event) {
  // Add select2 to select tags when html is first loaded
  let fieldsContainer = document.getElementById('fields-container');
  let selectTags = fieldsContainer.querySelectorAll('select');
  for (let selectTag of selectTags) {
    $(selectTag).select2();
  }

  // Remove functionality.
  fieldsContainer.addEventListener('click', function(event) {
    event.preventDefault();
    if (!event.target.classList.contains('remove')) {
      return false;
    }

    let currentElement = event.target;
    let field = currentElement.closest('.field-instance');
    let fieldIndex = field.dataset.index;

    field.remove();

    adjustIndices(fieldIndex);
  });

  // Add functionality
  addButton = document.getElementById('add');
  addButton.addEventListener('click', function(event) {
    // clone template field
    let templateField = document.getElementById('field-_-instance');
    let newField = templateField.cloneNode(true);

    // find what should be the index and update the template field with the index.
    let lastField = document.getElementById('fields-container').querySelector('.field-instance:last-child');
    let lastFieldIndex = parseInt(lastField.dataset.index);

    updateFieldWithNewIndex(newField, /(-)_(-)/, lastFieldIndex + 1);

    // insert the element in the DOM
    fieldsContainer.insertAdjacentElement('beforeend', newField);

    newField.classList.remove('is_hidden');
    newField.classList.add('field-instance');

    // Initialize select2 on this new element
    let selectTags = newField.querySelectorAll('select');
    for (let selectTag of selectTags) {
      $(selectTag).select2();
    }
  });
});

// function to reduce index by 1 for all fields that are after the removed field.
function adjustIndices(removedIndex) {
  let fieldInstances = document.querySelectorAll('.field-instance');
  for (let fieldInstance of fieldInstances) {
    let index = fieldInstance.dataset.index;
    if (index < removedIndex) {
      continue;
    }
    updateFieldWithNewIndex(fieldInstance, /(-)\d+(-)/, index - 1);
  }
}

// function to replace the index with a new index
function updateFieldWithNewIndex(field, regex, newIndex) {
  field.id = field.id.replace(regex, `$1${newIndex}$2`);
  field.dataset.index = newIndex;

  for (let elem of field.querySelectorAll('*')) {
    for (let attr of ['id', 'name', 'for']) {
      if (elem.hasAttribute(attr)) {
        elem.setAttribute(attr, elem.getAttribute(attr).replace(/(-)_(-)/, `$1${newIndex}$2`));
      }
    }
  }
}

CSS

.is_hidden {
  display: none;
}

HTML

<html>

<head>
  <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/select2@4.0.13/dist/js/select2.min.js"></script>
  <link href="https://cdn.jsdelivr.net/npm/select2@4.0.13/dist/css/select2.min.css" rel="stylesheet">
</head>

<body>
  <h1>Hello</h1>
  <form action="" method="post">
    <div id="fields-container">
      <div id="field-0-instance" class="field-instance" data-index="0">
        <label for="field-0-phone_number">Phone number</label>
        <select id="field-0-phone_number" name="field-0-phone_number" data-select2-id="field-0-phone_number" tabindex="-1" class="select2-hidden-accessible" aria-hidden="true">
          <option value="" data-select2-id="2">None</option>
          <option value="1">1234</option>
          <option value="2">2345</option>
          <option value="3">3456</option>
        </select><span class="select2 select2-container select2-container--default" dir="ltr" data-select2-id="1" style="width: 60px;"><span class="selection"><span class="select2-selection select2-selection--single" role="combobox" aria-haspopup="true" aria-expanded="false" tabindex="0" aria-disabled="false" aria-labelledby="select2-field-0-phone_number-container"><span class="select2-selection__rendered" id="select2-field-0-phone_number-container" role="textbox" aria-readonly="true" title="None">None</span>
        <span class="select2-selection__arrow" role="presentation"><b role="presentation"></b></span>
        </span>
        </span><span class="dropdown-wrapper" aria-hidden="true"></span></span>
      </div>
    </div>
    <a id="add" href="#">Add more</a>
  </form>
  <div id="field-_-instance" class="is_hidden" data-index="_">
    <label for="field-_-phone_number">Phone number</label>
    <select id="field-_-phone_number" name="field-_-phone_number">
      <option value="">None</option>
      <option value="1">1234</option>
      <option value="2">2345</option>
      <option value="3">3456</option>
    </select>
    <a class="remove" href="#">Remove</a>
  </div>
</body>

</html>

内容的提问来源于stack exchange,提问作者R M

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 06:35:55