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

点击新增年度按钮时select下拉字段重复渲染翻倍问题

问题原因

两个核心逻辑错误导致下拉框重复叠加:

  • 用targetDiv.innerHTML +=拼接内容的写法会重绘formCON容器内所有已存在的DOM,之前TomSelect渲染生成的自定义下拉结构会被销毁,还原成原生select标签
  • initSelect方法里的重复初始化判断无效:TomSelect初始化后不会给原生select加dropdown类,导致每次调用方法都会遍历页面所有.js-select元素,给旧的select重复绑定TomSelect实例,点击次数越多重复绑定的实例越多,下拉框就会成倍叠加
  • 额外规范问题:动态生成的DOM存在大量重复id(比如多个id="locationLabel"、id="Cash"),子容器和父容器都用了id="formCON",违反HTML id唯一的规范,会引发其他选择异常。
修复步骤
  • 替换DOM拼接方式:不用innerHTML全量覆盖,改用insertAdjacentHTML在容器末尾追加新表单块,不破坏已有已初始化的DOM结构
  • 修正初始化逻辑:给已经完成TomSelect初始化的select加专属标记类,初始化时直接跳过带标记的元素;更稳妥的方式是追加完新表单后,只查找新表单块内部的select做初始化,不遍历全页面旧元素
  • 修正重复id问题:所有动态生成的元素id拼接递增索引值,保证全局id唯一
修复后完整代码
<!-- HTML部分 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.0/jquery.min.js"></script>
<input id="noOfYears" type="number" value="1" />
<div id="formCON"></div>
let i = 1;

function GetYear() {
  var targetDiv = document.getElementById("formCON");
  // 新表单块用唯一类名,不要和父容器重id,所有动态id拼接i保证唯一
  const newFormHtml = `<div class="cantainer form-item" data-index="${i}">        
        <div class="tom-select-custom mb-3 w-100 mt-4">
          <div class="d-flex justify-content-between align-items-center flex-wrap">
            <label>Year</label>
            <select class="js-select form-select locationlabel"  name="year_${i}" id="locationLabel_${i}">
              <option selected disabled>select</option>
              <option>2022</option>
              <option>2021</option>
              <option>2020</option>
              <option>2019</option>
              <option>2018</option>
              <option>2017</option>
              <option>2016</option>
              <option>2015</option>
              <option>2014</option>
            </select>
          </div>
        </div>
        
        <div class="d-flex justify-content-between maskNumber mb-4 align-items-center flex-wrap">
          <label>Total Community <br> investment($)</label>
            <input type="text" class="form-control locationlabel" name="totalinvestement_${i}" id="investement_${i}" placeholder="please enter">
          </div>
        
        <div class="d-flex justify-content-between align-items-center mb-4 flex-wrap">
          <label for="Addresslabel">Pre-tax-profit ($)</label>
            <input type="text" class="form-control locationlabel"  name="Pre-tax-profit_${i}" id="Pre-tax-profit_${i}" placeholder="please enter">
          </div>
        
            <div class="d-flex justify-content-between align-items-center mb-4 flex-wrap">
              <label for="Addresslabel">Earnings before<br> interest,<br> tax, depreciation<br> &
                 amortization<br>(EBITDA) ($)</label>
                <input type="text" class="form-control locationlabel"  name="depreciation_${i}" id="depreciation_${i}" placeholder="please enter">
              </div>
        
              <div class="tom-select-custom mb-3 w-100 mt-4">
                <div class="d-flex justify-content-between align-items-center flex-wrap">
                  <label>Balance Date</label>
                  <select class="js-select form-select locationlabel" name="date_${i}"  id="dateLabel_${i}">
                    <option active disabled selected>Select</option>
                    <option>June 30</option>
                    <option>September 30</option>
                    <option>December 31</option>
                    <option>March 31</option>
                    <option>Other</option>
                  </select>
                </div>
              </div>
        
        <h4 class="card-header-title mb-6 mt-6">Itemisation</h4>
        
        <div class="d-flex justify-content-between align-items-center w-100 mb-4 flex-wrap">
          <label for="Addresslabel">Cash ($)</label>
            <input type="text" class=" form-control locationlabel"name="Cash_${i}" id="Cash_${i}" placeholder="please enter">
          </div>
        
          <div class="d-flex justify-content-between align-items-center w-100 mb-4 flex-wrap">
            <label for="Addresslabel">Volunteering ($)</label>
              <input type="text" class=" form-control locationlabel" name="volunteercash_${i}" id="volunteerCash_${i}" placeholder="please enter">
            </div>
        
            <div class="d-flex justify-content-between align-items-center w-100 mb-4 flex-wrap">
              <label for="Addresslabel">Products ($)</label>
                <input type="text" class="form-control locationlabel" name="productcash_${i}" id="productCash_${i}" placeholder="please enter">
              </div>
        
              <div class="d-flex justify-content-between align-items-center w-100 mb-4 flex-wrap">
                <label for="Addresslabel">Partnerships ($)</label>
                  <input type="text" class="form-control locationlabel" name="partnercash_${i}" id="partnerCash_${i}" placeholder="please enter">
                </div>
        
                <div class="d-flex justify-content-between align-items-center w-100 mb-4 flex-wrap">
                  <label for="Addresslabel">Management <br> Cost ($)</label>
                    <input type="text" class=" form-control locationlabel" name="managecash_${i}" id="manageCash_${i}" placeholder="please enter">
                  </div>
        
                  <div class="d-flex justify-content-between align-items-center w-100 mb-4 flex-wrap">
                    <label for="Addresslabel">Foregone <br> Revenue($)</label>
                      <input type="text" class="form-control locationlabel" name="revenue_${i}" id="revenue_${i}" placeholder="please enter">
                    </div>
        
                    <div class="d-flex justify-content-between align-items-center w-100 mb-4 flex-wrap">
                      <label for="Addresslabel">Leverage ($)</label>
                        <input type="text" class="form-control locationlabel" name="leveragecash_${i}" id="leverageCash_${i}" placeholder="please enter">
                      </div>

                          <a href="javascript:;" class="js-create-field form-link" onclick="GetYear()">
                            <i class="bi-plus-circle me-1"></i> Add another Year
                          </a>
                          
                    </div>`
  // 用beforeend追加,不重绘已有DOM
  targetDiv.insertAdjacentHTML('beforeend', newFormHtml);
  i += 1;
  document.getElementById('noOfYears').value = parseInt(document.getElementById('noOfYears').value) + 1
  // 只初始化刚插入的最后一个表单块里的select,不处理旧的
  initSelect(targetDiv.lastElementChild);
}

function initSelect(container = document) {
  // 查找容器内未初始化的select,加tomselect-initialized标记避免重复绑定
  var selectList = container.querySelectorAll('.js-select:not(.tomselect-initialized)');
  for (var v = 0; v < selectList.length; v++) {
    try {
      new TomSelect(selectList[v], {
        render: {
          'option': function(data, escape) {
            return data.optionTemplate || `<div>${data.text}</div>>`
          },
          'item': function(data, escape) {
            return data.optionTemplate || `<div>${data.text}</div>>`
          }
        }
      })
      // 初始化完成加标记类
      selectList[v].classList.add('tomselect-initialized');
    } catch (err) {
      console.log(err)
    }
  }
}

// 页面加载初始化第一个表单
GetYear();

如果项目里有其他逻辑依赖原来的DOM结构,只需要保留insertAdjacentHTML替换innerHTML、给初始化完的select加标记类、只初始化新表单内select这三个核心修改即可,id重复问题可以后续逐步调整。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 00:24:37