如何复用JavaScript的reveal函数避免多选择框互相干扰?
问题描述
我有一个实用的JavaScript reveal函数,不确定它的来源(可能出自Stack Overflow),自己JS知识有限,不是我写的。这个函数能根据select选中的选项显示对应的表单区域,单次使用效果没问题,但在复杂表单里多次使用时,多个select字段会互相干扰。直接复制重命名函数会产生大量重复代码,我尝试用ID隔离但没成功,想找不用重复代码的隔离方案。
以下是最小复现代码:
原代码
JavaScript
var current; function reveal(element) { if (current !== undefined) { var chosen = document.getElementById(current); chosen.classList.remove("visible"); chosen.classList.add("hidden"); } var fetchMe = element.options[element.selectedIndex].getAttribute('data-show'); if (fetchMe !== null) { current = fetchMe; var fetched = document.getElementById(fetchMe); fetched.classList.remove("hidden"); fetched.classList.add("visible"); } }
CSS
.hidden { display: none; } .visible { display: block; }
HTML
<h2>Knowledge</h2> <select onchange="reveal(this)"> <option>Select...</option> <option data-show="known">Known</option> <option data-show="unknown">Unknown</option> </select> <div class="hidden" id="known"> <input type="text" name="known" value="Known"> </div> <div class="hidden" id="unknown"> <input type="text" name="unknown" value="Unknown"> </div> <h2>Superheroes</h2> <select onchange="reveal(this)"> <option>Select...</option> <option data-show="batman">Batman</option> <option data-show="superman">Superman</option> </select> <div class="hidden" id="batman"> <input type="text" name="batman" value="Batman"> </div> <div class="hidden" id="superman"> <input type="text" name="supermann" value="Superman"> </div>
问题根源
原代码用了全局变量current记录当前显示的元素ID,所有select共用这个变量,切换一个select时会关掉另一个select的显示区域,导致互相干扰。
解决方案
方法1:用闭包为每个select维护独立状态
通过闭包给每个select分配独立的状态变量,避免全局变量冲突:
修改后的JavaScript
function createRevealer() { let current; // 每个revealer实例拥有独立的current变量 return function(element) { if (current !== undefined) { const chosen = document.getElementById(current); chosen.classList.remove("visible"); chosen.classList.add("hidden"); } const fetchMe = element.options[element.selectedIndex].getAttribute('data-show'); if (fetchMe !== null) { current = fetchMe; const fetched = document.getElementById(fetchMe); fetched.classList.remove("hidden"); fetched.classList.add("visible"); } } } // 给每个需要的select绑定独立的revealer函数 document.querySelectorAll('select[data-reveal]').forEach(select => { select.onchange = createRevealer(); });
修改后的HTML
给每个select添加data-reveal标记,方便JS选择:
<h2>Knowledge</h2> <select data-reveal> <option>Select...</option> <option data-show="known">Known</option> <option data-show="unknown">Unknown</option> </select> <div class="hidden" id="known"> <input type="text" name="known" value="Known"> </div> <div class="hidden" id="unknown"> <input type="text" name="unknown" value="Unknown"> </div> <h2>Superheroes</h2> <select data-reveal> <option>Select...</option> <option data-show="batman">Batman</option> <option data-show="superman">Superman</option> </select> <div class="hidden" id="batman"> <input type="text" name="batman" value="Batman"> </div> <div class="hidden" id="superman"> <input type="text" name="supermann" value="Superman"> </div>
CSS保持不变。
方法2:用父容器分组隔离
如果表单区域是按模块分组的,可以给每个select和对应显示区域加父容器,操作时只影响当前容器内的元素:
修改后的JavaScript
function reveal(element) { // 获取当前select所在的父容器 const container = element.closest('.reveal-group'); // 隐藏当前容器内所有已显示的区域 container.querySelectorAll('.visible').forEach(el => { el.classList.remove('visible'); el.classList.add('hidden'); }); const fetchMe = element.options[element.selectedIndex].getAttribute('data-show'); if (fetchMe !== null) { const fetched = container.querySelector(`#${fetchMe}`); fetched.classList.remove('hidden'); fetched.classList.add('visible'); } }
修改后的HTML
给每组select和显示区域包裹父容器.reveal-group:
<div class="reveal-group"> <h2>Knowledge</h2> <select onchange="reveal(this)"> <option>Select...</option> <option data-show="known">Known</option> <option data-show="unknown">Unknown</option> </select> <div class="hidden" id="known"> <input type="text" name="known" value="Known"> </div> <div class="hidden" id="unknown"> <input type="text" name="unknown" value="Unknown"> </div> </div> <div class="reveal-group"> <h2>Superheroes</h2> <select onchange="reveal(this)"> <option>Select...</option> <option data-show="batman">Batman</option> <option data-show="superman">Superman</option> </select> <div class="hidden" id="batman"> <input type="text" name="batman" value="Batman"> </div> <div class="hidden" id="superman"> <input type="text" name="supermann" value="Superman"> </div> </div>
CSS保持不变。
内容的提问来源于stack exchange,提问作者morgyface
相关产品推荐
相关产品推荐

