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

如何在同一网站复用国际区号选择器JavaScript代码

解决国际区号选择器跨表单复用的代码冲突问题

你的代码目前仅在联系表单生效,核心原因是document.querySelector()只会获取第一个匹配的DOM元素,咨询表单的选择器未被绑定事件;同时全局变量(如selectedCountry、ccDiv等)会被多个选择器共享,导致状态混乱。解决方案是将区号选择器封装为独立实例,每个表单的选择器拥有专属元素引用和状态,实现无冲突复用。


修改后的完整代码

HTML

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

<!------------- 联系表单区号选择器 ------------>     
<div class="cc-wrapper 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>

<!------------- 咨询表单区号选择器 ------------>      
<div class="cc-wrapper consult-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;
}
.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; /* 修复原代码拼写错误: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);
}

.cc-wrapper {
    width: 100px; /* 统一容器宽度,减少重复代码 */
}

JavaScript

// 封装区号选择器为类,每个实例独立管理自身元素和状态
class CountryCodeSelector {
    constructor(container) {
        // 缓存当前实例的DOM元素
        this.ccDiv = container;
        this.ccButton = container.querySelector(".cc-telcode");
        this.ccContainer = container.querySelector(".cc-container");
        this.ccSearchInput = container.querySelector(".cc-search-box");
        this.ccList = container.querySelector(".cc-data-list");
        this.selectedCountry = ""; // 每个实例自有状态,不共享

        // 绑定事件(用bind确保this指向当前实例)
        this.ccButton.addEventListener("click", this.handleButton.bind(this));
        this.ccSearchInput.addEventListener("input", this.handleInput.bind(this));
        this.ccList.addEventListener("click", this.handleListClick.bind(this));
        document.addEventListener("click", this.handleDocumentClick.bind(this));
    }

    // 点击外部关闭选择器
    handleDocumentClick(event) {
        if (!this.ccDiv.contains(event.target)) {
            this.ccContainer.classList.remove("show-cc-list");
        }
    }

    // 过滤国家数据
    filterData(value) {
        const data = [
            { name: "Afghanistan", 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" },
            // 更多国家数据可在此添加
        ];
        return data.filter(obj => {
            return obj.name.toLowerCase().startsWith(value.toLowerCase()) ||
                   obj.code.toLowerCase().startsWith(value.toLowerCase());
        });
    }

    // 生成国家列表HTML
    createListHtml(data) {
        return data.map(obj => {
            const { name, code, flag } = obj;
            const isSelected = obj.code === this.selectedCountry ? "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("");
    }

    // 生成无数据提示HTML
    createNoDataHtml() {
        return '<li class="nodata">No data available</li>';
    }

    // 生成按钮HTML
    createButtonHtml(code, flag) {
        return `
            <div class="flag-icon flag-icon-${flag}"></div>
            <span class="cc-code">+${code}</span>
        `;
    }

    // 点击按钮切换选择器显示状态
    handleButton() {
        this.ccContainer.classList.toggle("show-cc-list");
        this.ccList.innerHTML = this.createListHtml(this.filterData(""));
    }

    // 搜索输入处理
    handleInput(e) {
        const value = e.target.value.trim();
        const filteredData = this.filterData(value);
        if (value && filteredData.length === 0) {
            this.ccList.innerHTML = this.createNoDataHtml();
        } else {
            this.ccList.innerHTML = this.createListHtml(filteredData);
        }
    }

    // 选择国家处理
    handleListClick(e) {
        const item = e.target.closest(".cc-list-items");
        if (!item) return;

        const { code, flag } = item.dataset;
        this.selectedCountry = code;
        this.ccButton.innerHTML = this.createButtonHtml(code, flag);
        this.ccContainer.classList.remove("show-cc-list");
    }
}

// 初始化所有区号选择器实例
document.querySelectorAll(".cc-wrapper").forEach(container => {
    new CountryCodeSelector(container);
});

关键修改说明

  1. 封装为类:将选择器逻辑封装成CountryCodeSelector类,每个实例对应一个表单的选择器,元素引用和状态完全独立,避免全局冲突。
  2. 批量初始化:用document.querySelectorAll()获取所有选择器容器,遍历创建实例,实现一次代码复用多个表单。
  3. 修复事件绑定问题:原代码的handleDocumentClick内部重复绑定document事件,会导致多次触发,现在改为每个实例单独处理外部点击关闭逻辑。
  4. 统一CSS类:添加通用类.cc-wrapper,减少重复CSS代码,同时保留原表单类名用于特殊样式定制。
  5. 修复拼写错误:修正CSS中border: 1px soldi darkgray的拼写错误为solid。

内容的提问来源于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.06 07:01:10