You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何检测下拉菜单展开后元素是否存在指定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>

问题原因

  1. 检测代码执行时机错误:你注释掉的if else代码只会在页面加载时运行一次,而点击按钮添加类是后续操作,自然检测不到。
  2. 类名不匹配:代码里实际添加的是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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 02:35:48