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

如何在页面多个元素间复用JavaScript/HTML组件?

复用Bootstrap树选择模态框的最佳实践

嘿,这个问题提得特别好——重复代码不仅难维护,还容易藏bug,咱们可以通过抽象配置+复用核心逻辑来彻底解决这个问题,下面给你一步步拆解方案:

1. 重构HTML:只保留一份模态框

把原来复制的两个模态框合并成一个,把可变的部分(标题、树视图容器)做成动态可替换的:

<!-- 通用的树选择模态框 -->
<div class="modal fade" id="treeSelectModal" tabindex="-1" aria-labelledby="treeSelectModalLabel" aria-hidden="true">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <!-- 动态标题占位符 -->
        <h5 class="modal-title" id="treeSelectModalLabel"></h5>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
      </div>
      <div class="modal-body">
        <!-- 搜索框(复用搜索逻辑) -->
        <input type="text" class="form-control mb-3" id="treeSearchInput" placeholder="搜索树节点...">
        <!-- 树视图容器,动态加载不同数据源 -->
        <div id="treeContainer"></div>
      </div>
    </div>
  </div>
</div>

<!-- 两个文本框 -->
<input type="text" id="textbox1" class="form-control mb-2" readonly placeholder="点击选择项">
<input type="text" id="textbox2" class="form-control" readonly placeholder="点击选择项">

2. 封装可配置的JavaScript核心逻辑

把所有通用逻辑(模态框开关、树加载、搜索高亮、节点选择回调)封装成一个可复用的函数或者类,接收文本框元素、模态框标题、数据源接口这几个关键参数:

// 封装树选择模态框的核心逻辑
function initTreeSelect(textboxSelector, modalTitle, dataApi) {
  const textbox = document.querySelector(textboxSelector);
  const modal = new bootstrap.Modal(document.getElementById('treeSelectModal'));
  const modalTitleEl = document.getElementById('treeSelectModalLabel');
  const treeContainer = document.getElementById('treeContainer');
  const searchInput = document.getElementById('treeSearchInput');

  // 绑定文本框点击事件:打开模态框并加载数据
  textbox.addEventListener('click', async () => {
    // 1. 设置模态框标题
    modalTitleEl.textContent = modalTitle;
    // 2. 清空之前的树数据和搜索框
    treeContainer.innerHTML = '';
    searchInput.value = '';
    // 3. 加载新的树数据源
    try {
      const response = await fetch(dataApi);
      const treeData = await response.json();
      // 渲染树视图(这里假设你有现成的renderTree函数,比如用jQuery Treeview或者自定义逻辑)
      renderTree(treeContainer, treeData);
    } catch (error) {
      console.error('加载树数据失败:', error);
      treeContainer.innerHTML = '<div class="text-danger">加载数据失败</div>';
    }
    // 4. 打开模态框
    modal.show();
  });

  // 复用搜索高亮逻辑(这里简化示例,实际根据你的高亮实现调整)
  searchInput.addEventListener('input', () => {
    const keyword = searchInput.value.trim().toLowerCase();
    // 遍历树节点,匹配关键词并高亮
    document.querySelectorAll('#treeContainer .tree-node').forEach(node => {
      const nodeText = node.textContent.toLowerCase();
      if (keyword && nodeText.includes(keyword)) {
        node.classList.add('highlight');
        // 可选:滚动到第一个匹配的节点
        node.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
      } else {
        node.classList.remove('highlight');
      }
    });
  });

  // 树节点选择事件(委托绑定,避免每次渲染树都重新绑定)
  treeContainer.addEventListener('click', (e) => {
    const selectedNode = e.target.closest('.tree-node');
    if (selectedNode) {
      // 获取选中节点的文本(根据你的树结构调整)
      const selectedText = selectedNode.textContent.trim();
      // 回填到对应的文本框
      textbox.value = selectedText;
      // 关闭模态框
      modal.hide();
    }
  });
}

// 示例:渲染树视图的通用函数(根据你的实际树组件调整)
function renderTree(container, data) {
  // 这里假设data是树形结构数组,比如[{id:1, name:'节点1', children:[]}, ...]
  const ul = document.createElement('ul');
  ul.className = 'list-unstyled tree-view';
  data.forEach(node => {
    const li = document.createElement('li');
    li.className = 'tree-node';
    li.textContent = node.name;
    // 如果有子节点,递归渲染
    if (node.children && node.children.length > 0) {
      li.appendChild(renderTree(document.createElement('div'), node.children));
    }
    ul.appendChild(li);
  });
  container.appendChild(ul);
}

3. 初始化两个文本框的功能

只需要调用封装好的initTreeSelect函数,传入不同的配置参数即可:

// 初始化第一个文本框
initTreeSelect('#textbox1', '选择分类', '/api/categories');
// 初始化第二个文本框
initTreeSelect('#textbox2', '选择部门', '/api/departments');

关键细节说明

  • 事件委托:树节点的点击事件绑定在容器上,而不是每个节点,这样每次重新渲染树的时候不需要重新绑定事件,避免内存泄漏。
  • 动态重置:每次打开模态框时清空树容器和搜索框,确保不会残留上一次的数据。
  • 配置化参数:把所有可变的部分(文本框、标题、接口)作为参数传入,核心逻辑完全复用。
  • 高亮逻辑复用:搜索高亮的逻辑写在封装函数里,不管哪个文本框调用,都用同一套逻辑。

这样一来,你只需要维护一份模态框HTML和一份核心JS逻辑,后续如果要加第三个、第四个文本框,只需要一行initTreeSelect调用即可,完美解决重复代码的问题!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 09:02:36