如何为国家代码选择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);
关键改动说明
CSS动画实现:
- 移除
display: none,改用max-height: 0和opacity: 0隐藏容器 - 添加
transition属性,让max-height和opacity变化时产生平滑过渡 - 显示时设置
max-height: 300px(可根据实际内容调整)和opacity: 1
- 移除
JS事件修复:
- 移除
handleDocumentClick内重复绑定的document点击事件,避免多次触发关闭逻辑 - 优化
handleButton逻辑,仅在显示时渲染列表,减少不必要的DOM操作 - 修复
createButtonHtml里的标签错误:把<option>改为<span>(原标签语义错误)
- 移除
内容的提问来源于stack exchange,提问作者Gupa Dutta
相关产品推荐
相关产品推荐

