如何检测下拉菜单展开后元素是否存在指定CSS类
问题:无法检测下拉菜单展开时的CSS类并实现动画效果
我知道可以用classList.contains()方法检测元素是否包含指定CSS类,但这个方法得等DOM加载完成后才能生效。当下拉菜单展开时,.cc-container元素会被添加show-cc-list类,我想在这个类存在的时候实现动画效果,但用if else语句点击下拉菜单后根本检测不到这个类。
提供的代码片段
JS代码
/*---- NOT WORKING ------*/ /* if(document.querySelector(".cc-container").classList.contains("show")){ alert("YES"); }else{ alert("NO"); } */ const countryData = [ { name: "阿富汗", code: "93", flag: "afg" }, { name: "阿尔巴尼亚", code: "355", flag: "alb" }, { name: "阿尔及利亚", code: "213", flag: "dza" }, { name: "美属萨摩亚", code: "1-684", flag: "asm" }, { name: "安道尔", code: "376", flag: "and" }, { name: "安哥拉", code: "244", flag: "ago" }, { name: "安圭拉", code: "1-264", flag: "aia" }, { name: "南极洲", code: "672", flag: "ata" }, { name: "安提瓜和巴布达", code: "1-268", flag: "atg" }, { name: "阿根廷", code: "54", flag: "arg" }, { name: "亚美尼亚", code: "374", flag: "arm" }, { name: "阿鲁巴", code: "297", flag: "abw" }, { name: "澳大利亚", code: "61", flag: "aus" }, { name: "奥地利", code: "43", flag: "aut" }, { name: "阿塞拜疆", code: "994", flag: "aze" }, { name: "巴哈马", code: "1-242", flag: "bhs" }, { name: "巴林", code: "973", flag: "bhr" }, { name: "孟加拉国", code: "880", flag: "bgd" }, { name: "巴巴多斯", code: "1-246", flag: "brb" }, { name: "白俄罗斯", code: "375", flag: "blr" }, { name: "比利时", code: "32", flag: "bel" }, { name: "伯利兹", code: "501", flag: "blz" }, { name: "贝宁", code: "229", flag: "ben" }, { name: "百慕大", code: "1-441", flag: "bmu" }, { name: "不丹", code: "975", flag: "btn" }, { name: "玻利维亚", code: "591", flag: "bol" }, { name: "波斯尼亚和黑塞哥维那", code: "387", flag: "bih" }, { name: "博茨瓦纳", code: "267", flag: "bwa" }, { name: "巴西", code: "55", flag: "bra" }, { name: "英属印度洋领地", code: "246", flag: "iot" }, { name: "英属维尔京群岛", code: "1-284", flag: "vgb" }, { name: "文莱", code: "673", flag: "brn" }, { name: "保加利亚", code: "359", flag: "bgr" }, { name: "布基纳法索", code: "226", flag: "bfa" }, { name: "布隆迪", code: "257", flag: "bdi" }, { name: "柬埔寨", code: "855", flag: "khm" }, ]; function registerForm(formRoot) { const ccButton = formRoot.querySelector(".cc-telcode"); const ccContainer = formRoot.querySelector(".cc-container"); const ccSearchInput = formRoot.querySelector(".cc-search-box"); const ccList = formRoot.querySelector(".cc-data-list"); let selectedCountry = ""; ccButton.addEventListener("click", handleButton); ccSearchInput.addEventListener("input", handleInput); ccList.addEventListener("click", handleListClick); document.addEventListener("click", handleDocumentClick); function handleDocumentClick(e) { const { parentElement } = e.target; document.addEventListener("click", (event) => { if (!formRoot.contains(event.target)) { ccContainer.classList.remove("show-cc-list"); } }); } function handleButton() { ccContainer.classList.toggle("show-cc-list"); ccList.innerHTML = createListHtml(countryData); } function createListHtml(countryData) { return countryData.map((obj) => { const { name, code, flag } = obj; let isSelected = ""; if (obj.name == selectedCountry) isSelected = "selected-country"; return ` <li class="cc-list-items ${isSelected}" 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 handleInput(e) { const { value } = e.target; if (value) { const filtered = filterData(countryData, value); if (filtered.length) { ccList.innerHTML = createListHtml(filtered); } else { ccList.innerHTML = createNoDataHtml(); } } else { ccList.innerHTML = createListHtml(countryData); } } function handleListClick(e) { const item = e.target.closest("li") || e.target; if (item.classList.contains("cc-list-items")) { const { code, flag } = item.dataset; selectedCountry = item.dataset.name; ccButton.innerHTML = createButtonHtml(code, flag); ccContainer.classList.remove("show-cc-list"); } } } function filterData(countryData, value) { return countryData.filter((obj) => { return ( obj.name.toLowerCase().startsWith(value.toLowerCase()) || obj.code.toLowerCase().startsWith(value.toLowerCase()) ); }); } function createButtonHtml(code, flag) { return ` <div class="flag-icon flag-icon-${flag}"></div> <option class="cc-code" value="+${code}">+${code}</option> `; } function createNoDataHtml() { return '<li class="no-data-found">抱歉,未找到数据</li>'; } const contactDiv = document.querySelector(".contact-frm-cc"); registerForm(contactDiv);
CSS代码
.cc-telcode { margin-bottom: 1em; display: flex; justify-content: center; width: 100%; } .cc-telcode div.cc-code, .cc-list-items div.name { margin-left: 0.25em; } .cc-container { display: none; width: 300px; position: absolute; } .show-cc-list { display: block; z-index: 999; } .cc-data-list { max-height: 100px; list-style: none; margin: 1em 0 0 0; padding: 0; overflow-y: scroll; border: 1px solid darkgray; } .cc-list-items { display: flex; padding: 0.25em; border: 1px solid lightgray; } .cc-list-items:hover { cursor: pointer; background-color: lightyellow; } .selected-country { cursor: pointer; background-color: rgb(73, 118, 241); } .contact-frm-cc, .consult-frm-cc { width: 100px; }
HTML代码
<link rel="stylesheet" href="https://amitdutta.co.in/flag/css/flag-icon.css"> <div class="contact-frm-cc"> <button class="cc-telcode">电话区号</button> <section class="cc-container"> <input type="text" class="cc-search-box" placeholder="搜索国家" /> <ul class="cc-data-list"> </ul> </section> </div>
问题原因
- 检测代码执行时机错误:你注释掉的if else代码只会在页面加载时运行一次,而点击按钮添加类是后续操作,自然检测不到。
- 类名不匹配:代码里实际添加的是
show-cc-list类,但你检测的是show类,完全是两个不同的类。
解决方法
方法1:用CSS直接实现动画(最简洁)
不需要JS检测,通过类选择器绑定过渡/动画效果:
.cc-container { display: none; width: 300px; position: absolute; /* 添加过渡属性,实现平滑动画 */ opacity: 0; transform: translateY(-10px); transition: opacity 0.3s ease, transform 0.3s ease; } .show-cc-list { display: block; z-index: 999; /* 展开时的状态 */ opacity: 1; transform: translateY(0); }
方法2:在按钮点击事件中直接处理
既然类的变更发生在handleButton函数里,直接在这个函数里判断状态并执行动画:
function handleButton() { ccContainer.classList.toggle("show-cc-list"); ccList.innerHTML = createListHtml(countryData); // 切换后立即判断类是否存在 if (ccContainer.classList.contains('show-cc-list')) { // 展开时的动画逻辑 ccContainer.style.animation = "fadeIn 0.3s forwards"; } else { // 收起时的动画逻辑 ccContainer.style.animation = "fadeOut 0.3s forwards"; } } // 补充关键帧动画 @keyframes fadeIn { from { opacity: 0; transform: translateY(-10px); } to { opacity: 1; transform: translateY(0); } } @keyframes fadeOut { from { opacity: 1; transform: translateY(0); } to { opacity: 0; transform: translateY(-10px); } }
方法3:用MutationObserver监听类变化
如果需要在类变更时做更复杂的逻辑,可以用观察者监听元素的类属性变化:
// 在registerForm函数内部添加 const observer = new MutationObserver((mutations) => { mutations.forEach(mutation => { if (mutation.type === 'attributes' && mutation.attributeName === 'class') { if (ccContainer.classList.contains('show-cc-list')) { // 菜单展开,执行动画 console.log("菜单已展开"); } else { // 菜单收起,执行对应逻辑 console.log("菜单已收起"); } } }); }); // 开始监听.cc-container的类属性变化 observer.observe(ccContainer, { attributes: true });
额外修复:document点击事件重复绑定问题
你当前的handleDocumentClick函数里重复添加了document点击事件,会导致多次触发,改成如下:
function handleDocumentClick(e) { if (!formRoot.contains(e.target)) { ccContainer.classList.remove("show-cc-list"); } } // 只添加一次事件监听,不要在函数内部重复绑定 document.addEventListener("click", handleDocumentClick);
内容的提问来源于stack exchange,提问作者Gupa Dutta
相关产品推荐
相关产品推荐

