动态增删表单字段时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
相关产品推荐
相关产品推荐

