修复国际区号选择器:点击容器外部隐藏下拉的反向Bug
问题分析与修复方案
核心问题
- 冗余且逻辑错误的事件监听:顶部提前添加的
document.addEventListener不仅在container变量声明前就调用(会触发报错),而且判断逻辑完全反向——点击容器内部时关闭下拉,和需求完全相反。 - 未实现的外部点击关闭逻辑:
handleDocumentClick函数为空,没有处理外部点击的判断逻辑。 - 事件冒泡冲突:点击切换按钮时,事件会冒泡到document,导致下拉刚打开就被关闭。
修复步骤
- 移除顶部那段逻辑错误且提前执行的
document.addEventListener代码块。 - 完善
handleDocumentClick函数,判断点击目标是否在**整个区号选择器容器(.country-code-selector)**之外,若是则关闭下拉。 - 在
handleButton函数中添加event.stopPropagation(),阻止按钮点击事件冒泡到document,避免下拉开关冲突。
修改后的完整代码
JavaScript
// Cache the elements const xbutton = document.querySelector('.country-code-selector .telcode'); const container = document.querySelector('.country-code-selector .container'); const input = document.querySelector('.country-code-selector input'); const list = document.querySelector('.country-code-selector .list'); const selectorWrapper = document.querySelector('.country-code-selector'); // 缓存整个选择器容器 // Add event listeners to the button, input, and list xbutton.addEventListener('click', handleButton); input.addEventListener('input', handleInput); list.addEventListener('click', handleListClick); document.addEventListener('click', handleDocumentClick); // 处理外部点击关闭下拉 function handleDocumentClick(e) { // 判断点击目标是否不在选择器容器内 if (!selectorWrapper.contains(e.target)) { container.classList.remove('show'); } } // 国家区号数据 const data = [ { name: 'Afganistan', code: '69', flag: 'afg' }, { name: 'Barbados', code: '1-246', flag: 'brb' }, { name: 'Bolivia', code: '591', flag: 'bol' }, { name: 'Cuba', code: '53', flag: 'cub' }, { name: 'Fiji', code: '679', flag: 'fji' }, ]; // 根据输入值过滤数据 function filterData(data, value) { return data.filter(obj => { return ( obj.name.toLowerCase().startsWith(value.toLowerCase()) || obj.code.toLowerCase().startsWith(value.toLowerCase()) ); }); } // 生成列表项HTML function createListHtml(data) { return data.map(obj => { const { name, code, flag } = obj; return ` <li class="item" data-name="${name}" data-code="${code}" data-flag="${flag}" > <div class="flag-icon flag-icon-${flag}"></div> <div class="name">${name} (+${code})</div> </li> `; }).join(''); } // 切换下拉菜单显示/隐藏 function handleButton(e) { e.stopPropagation(); // 阻止事件冒泡,避免触发document的click事件 container.classList.toggle('show'); } // 生成无数据提示项 function createNoDataHtml() { return '<li class="nodata">No data available</li>'; } // 输入框变化时过滤并更新列表 function handleInput(e) { const { value } = e.target; if (value) { const filtered = filterData(data, value); if (filtered.length) { list.innerHTML = createListHtml(filtered); } else { list.innerHTML = createNoDataHtml(); } } else { list.innerHTML = createListHtml(data); } } // 生成按钮显示的HTML function createButtonHtml(code, flag) { return ` <div class="flag-icon flag-icon-${flag}"></div> <div class="code">+${code}</div> `; } // 更新选中的列表项样式 function updateSelected(list, item) { const items = list.querySelectorAll('.item'); items.forEach(item => item.classList.remove('selected')); item.classList.add('selected'); } // 点击列表项时更新按钮并关闭下拉 function handleListClick(e) { e.stopPropagation(); // 阻止事件冒泡,避免触发document的click const item = e.target.closest('li') || e.target; if (item.classList.contains('item')) { const { code, flag } = item.dataset; xbutton.innerHTML = createButtonHtml(code, flag); updateSelected(list, item); container.classList.remove('show'); } } // 初始化列表 list.innerHTML = createListHtml(data);
CSS
.country-code-selector .telcode { margin-bottom: 1em; display: flex; } .country-code-selector .telcode div.code, .item div.name { margin-left: 0.25em; } .country-code-selector .container { display: none; border: 1px solid grey; } .country-code-selector .show { display: block; } .country-code-selector .list { height: 100px; list-style: none; margin: 1em 0 0 0; padding: 0; overflow-y: scroll; border: 1px solid darkgray; /* 修复原CSS拼写错误:soldi → solid */ } .country-code-selector .item { display: flex; padding: 0.25em; border: 1px solid lightgray; } .country-code-selector .item:hover { cursor: pointer; } .country-code-selector .item:hover, .item.selected { background-color: lightyellow; }
HTML
<link href="https://amitdutta.co.in/flag/css/flag-icon.css" rel="stylesheet"/> <div class="country-code-selector"> <button type="button" class="telcode">Tel code</button> <section class="container"> <input type="text" placeholder="Search for country" /> <ul class="list"></ul> </section> </div>
额外修复说明
- 修复了CSS中的拼写错误:
border: 1px soldi darkgray;→border: 1px solid darkgray; - 在
handleListClick中添加了e.stopPropagation(),避免列表点击事件冒泡到document,逻辑更严谨
内容的提问来源于stack exchange,提问作者Gupa Dutta
相关产品推荐
相关产品推荐

