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

如何为国家代码选择Dropdown Menu添加slide-down/slide-up动画

国际国家代码选择组件滑入滑出动画修复方案

问题根源

原代码使用display: none/block控制组件显示隐藏,这类属性切换无法触发CSS过渡动画,导致只能单向生效。我们改用**max-height + opacity + CSS过渡**实现平滑的滑入滑出效果。

修改后的完整代码

HTML(无需改动)

<link rel="stylesheet" href="https://amitdutta.co.in/flag/css/flag-icon.css">

<div class="contact-frm-cc">
  <button class="cc-telcode">Tel code</button>
  <section class="cc-container">
    <input type="text" class="cc-search-box" placeholder="Search for country" />
    <ul class="cc-data-list">
    </ul>
  </section>
</div>

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;
}

/* 关键修改:用max-height和opacity实现动画 */
.cc-container {
  width: 300px;
  position: absolute;
  max-height: 0;
  opacity: 0;
  overflow: hidden;
  transition: max-height 0.3s ease, opacity 0.3s ease;
}

.show-cc-list {
  max-height: 300px; /* 足够容纳内容的高度,可按需调整 */
  opacity: 1;
  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; /* 修正原代码拼写错误:soldi → solid */
}

.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 {
  width: 100px;
}

JS(修复事件绑定重复问题)

const countryData = [
   { name: "Afganistan", code: "93", flag: "afg" },
   { name: "Albania", code: "355", flag: "alb" },
   { name: "Algeria", code: "213", flag: "dza" },
   { name: "American Samoa", code: "1-684", flag: "asm" },
   { name: "Andorra", code: "376", flag: "and" },
   { name: "Angola", code: "244", flag: "ago" },
   { name: "Anguilla", code: "1-264", flag: "aia" },
   { name: "Antarctica", code: "672", flag: "ata" },
   { name: "Antigua and Barbuda", code: "1-268", flag: "atg" },
   { name: "Argentina", code: "54", flag: "arg" },
   { name: "Armenia", code: "374", flag: "arm" },
   { name: "Aruba", code: "297", flag: "abw" },
   { name: "Australia", code: "61", flag: "aus" },
   { name: "Austria", code: "43", flag: "aut" },
   { name: "Azerbaijan", code: "994", flag: "aze" },
   { name: "Bahamas", code: "1-242", flag: "bhs" },
   { name: "Bahrain", code: "973", flag: "bhr" },
   { name: "Bangladesh", code: "880", flag: "bgd" },
   { name: "Barbados", code: "1-246", flag: "brb" },
   { name: "Belarus", code: "375", flag: "blr" },
   { name: "Belgium", code: "32", flag: "bel" },
   { name: "Belize", code: "501", flag: "blz" },
   { name: "Benin", code: "229", flag: "ben" },
   { name: "Bermuda", code: "1-441", flag: "bmu" },
   { name: "Bhutan", code: "975", flag: "btn" },
   { name: "Bolivia", code: "591", flag: "bol" },
   { name: "Bosnia and Herzegovina", code: "387", flag: "bih" },
   { name: "Botswana", code: "267", flag: "bwa" },
   { name: "Brazil", code: "55", flag: "bra" },
   { name: "British Indian Ocean Territory", code: "246", flag: "iot" },
   { name: "British Virgin Islands", code: "1-284", flag: "vgb" },
   { name: "Brunei", code: "673", flag: "brn" },
   { name: "Bulgaria", code: "359", flag: "bgr" },
   { name: "Burkina Faso", code: "226", flag: "bfa" },
   { name: "Burundi", code: "257", flag: "bdi" },
   { name: "Cambodia", code: "855", flag: "khm" },
   { name: "Cameroon", code: "237", flag: "cmr" },
   { name: "Canada", code: "1", flag: "can" },
   { name: "Cape Verde", code: "238", flag: "cpv" },
   { name: "Cayman Islands", code: "1-345", flag: "cym" },
   { name: "Central African Republic", code: "236", flag: "caf" },
   { name: "Chad", code: "235", flag: "tcd" },
   { name: "Chile", code: "56", flag: "chl" },
   { name: "China", code: "86", flag: "chn" },
   { name: "Christmas Island", code: "61", flag: "cxr" },
   { name: "Cocos Islands", code: "61", flag: "cck" },
   { name: "Colombia", code: "57", flag: "col" },
   { name: "Comoros", code: "269", flag: "com" },
   { name: "Cook Islands", code: "682", flag: "cok" },
   { name: "Costa Rica", code: "506", flag: "cri" },
   { name: "Croatia", code: "385", flag: "hrv" },
   { name: "Cuba", code: "53", flag: "cub" },
   { name: "Curacao", code: "599", flag: "cuw" },
   { name: "Cyprus", code: "357", flag: "cyp" },
   { name: "Czech Republic", code: "420", flag: "cze" },
   { name: "Democratic Republic of the Congo", code: "243", flag: "cod" },
   { name: "Denmark", code: "45", flag: "dnk" },
   { name: "Djibouti", code: "253", flag: "dji" },
   { name: "Dominica", code: "1-767", flag: "dma" },
   { name: "Dominican Republic", code: "1-809", flag: "dom" },
   /*------- MORE --------*/
];

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点击事件,避免重复触发
  document.addEventListener("click", handleDocumentClick);
   
  function handleDocumentClick(event) {
    if (!formRoot.contains(event.target)) {
      ccContainer.classList.remove("show-cc-list");
    }
  }

  function handleButton() {
    ccContainer.classList.toggle("show-cc-list");
    if (ccContainer.classList.contains("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);
      ccList.innerHTML = filtered.length ? createListHtml(filtered) : createNoDataHtml();
    } else {
      ccList.innerHTML = createListHtml(countryData);
    }
  }
  
  function handleListClick(e) {
    const item = e.target.closest("li");
    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>
    <span class="cc-code" value="+${code}">+${code}</span>
  `;
}

function createNoDataHtml() {
  return '<li class="no-data-found">Sorry, No Data Found</li>';
}

const contactDiv = document.querySelector(".contact-frm-cc");
registerForm(contactDiv);

关键改动说明

  1. CSS动画实现:

    • 移除display: none,改用max-height: 0和opacity: 0隐藏容器
    • 添加transition属性,让max-height和opacity变化时产生平滑过渡
    • 显示时设置max-height: 300px(可根据实际内容调整)和opacity: 1
  2. JS事件修复:

    • 移除handleDocumentClick内重复绑定的document点击事件,避免多次触发关闭逻辑
    • 优化handleButton逻辑,仅在显示时渲染列表,减少不必要的DOM操作
    • 修复createButtonHtml里的标签错误:把<option>改为<span>(原标签语义错误)

内容的提问来源于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.04 04:00:52