国家代码选择器下拉框:点击外部隐藏功能反向bug修复求助
问题修复:点击容器外部隐藏下拉菜单
问题根源
你的代码存在三个核心问题:
- 监听document点击的逻辑完全写反:当前代码判断点击容器内部时关闭菜单,和需求的“点击外部关闭”正好相反。
- 同时绑定了两个document的click事件,其中
handleDocumentClick是空函数,造成逻辑冗余。 - 点击触发下拉的按钮时,事件会冒泡到document,导致菜单刚打开就被关闭。
修复方案
- 删除无用的
handleDocumentClick函数及其事件监听 - 修正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'); // Add event listeners to the button, input, and list // We use a process called "event delegation" on the list // to catch events from its children as they "bubble up" the DOM xbutton.addEventListener('click', handleButton); input.addEventListener('input', handleInput); list.addEventListener('click', handleListClick); document.addEventListener("click", (event) => { // 判断点击目标不在容器内,也不在触发按钮内,且菜单处于显示状态时关闭 if (!container.contains(event.target) && !xbutton.contains(event.target)) { if (container.classList.contains('show')) { container.classList.remove('show'); } } }); // All of the data held as objects within an array 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' }, ]; // Filters the data based on the characters // at the start of the provided name function filterData(data, value) { return data.filter(obj => { return ( obj.name.toLowerCase().startsWith(value.toLowerCase()) || obj.code.toLowerCase().startsWith(value.toLowerCase()) ); }); } // Create a series of list items based on the // data passed to it 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(''); } // Toggle the container on/off function handleButton(e) { // 阻止事件冒泡,避免触发document的click事件 e.stopPropagation(); container.classList.toggle('show'); } // No data available list item function createNoDataHtml() { return '<li class="nodata">No data available</li>'; } // When the input is changed filter the data // according to the current value, and then // create some list items using that filtered data 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); } } // Create some button HTML function createButtonHtml(code, flag) { return ` <div class="flag-icon flag-icon-${flag}"></div> <div class="code">+${code}</div> `; } // Updates the selected list by removing the `selected` // class from all items, and then adding one to the clicked // item function updateSelected(list, item) { const items = list.querySelectorAll('.item'); items.forEach(item => item.classList.remove('selected')); item.classList.add('selected'); } // When an item is clicked, grab the relevant data // attributes, create the new button HTML, and then // close the container function handleListClick(e) { 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; width: 350px; border: 1px solid black; } .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; } .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>
关键修复点说明
- 修正document点击逻辑:使用
!container.contains(event.target) && !xbutton.contains(event.target)判断点击位置是否在容器和按钮之外,符合需求。 - 阻止按钮事件冒泡:在
handleButton中调用e.stopPropagation(),避免点击按钮时触发document的click事件导致菜单刚打开就关闭。 - 删除冗余代码:移除了空的
handleDocumentClick函数及其事件监听,减少逻辑冲突。
内容的提问来源于stack exchange,提问作者Gupa Dutta
相关产品推荐
相关产品推荐

