如何在页面多个元素间复用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
相关产品推荐
相关产品推荐

