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

模态框重复绑定事件监听器致请求倍增问题求助

模态框重复打开后请求次数倍增问题解决

问题描述

我制作了一个用于修改卡片属性(名称、描述和所属公司)的模态框,功能基本正常,但不刷新页面重复打开模态框时,请求次数会成倍增加(两次、三次甚至更多),只有刷新页面能暂时解决,但这并非合理方案。

关键代码片段

JavaScript 代码

function getAllDepartmentHandleButton() {
    const allDeleteButtons = document.querySelectorAll('.departmentContainer__content--card')

    allDeleteButtons.forEach(e => {
        e.addEventListener('click', (event) => {
            if(event.target.classList.value == 'remove') {
                showRemoveDepartmentModal(event.target.id, event.target.alt)
            } else if(event.target.classList.value == 'edit') {
                showEditDepartmentModal(event.target.id)
            }
        })
    })
}

async function showEditDepartmentModal(departmentId) {
    let companyToEditid = 0
    const allDepartments = await getAllDepartments()
    const editModal = document.querySelector('.editDepartmentModal')
    const closeBtn = document.querySelector('.editDepartmentModal__container > img')
    const nameInput = document.querySelector('.editDepartmentModal__container--departmentName')
    const descriptionInput = document.querySelector('.editDepartmentModal__container--departmentDescription')
    const selectedCompanyInput = document.querySelector('.editDepartmentModal__container--company > span')
    const placeToAppend = document.querySelector('.editDepartmentModal__container--list')
    const showCompanyOptionsButton = document.querySelector('.editDepartmentModal__container--company')
    const dialogCompanyOptions = document.querySelector('.editDepartmentModal__container > dialog')
    const allCompanies = await getAllCompanies()
    const buttonSaveAlterations = document.querySelector('.editDepartmentModal__container--button')

    allCompanies.forEach(e => {
        placeToAppend.insertAdjacentHTML('beforeend', `
    <li id="${e.uuid}">${e.name}</li>
    `)        
    })

    editModal.showModal()

    const selectedCompany = allDepartments.find(e => {
        if (e.uuid == departmentId) {
            return e
        }
    })

    closeBtn.addEventListener('click', () => {
        editModal.close()
    })

    nameInput.value = selectedCompany.name
    descriptionInput.value = selectedCompany.description
    selectedCompanyInput.innerText = selectedCompany.companies.name
    selectedCompanyInput.id = departmentId

    showCompanyOptionsButton.addEventListener('click', ()=> {
        if(dialogCompanyOptions.open) {
            dialogCompanyOptions.close()
        } else {
            dialogCompanyOptions.show()
        }
    })

    placeToAppend.addEventListener('click', (event) => {
        companyToEditid = event.target.id
        selectedCompanyInput.innerText = event.target.innerText
        dialogCompanyOptions.close()
    })

    buttonSaveAlterations.addEventListener('click', () => {
        if(nameInput.value == '' || descriptionInput.value == '') {
            alert('vazio')
        } else {
            editDepartmentFunction(nameInput.value, descriptionInput.value, companyToEditid)
            editModal.close()
        }
    })        
}

function editDepartmentFunction(test1, test2, test3) {
    console.log(test1, test2,test3)
}

HTML 代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="../styles/globalStyles.css">
    <link rel="stylesheet" href="../styles/indexStyles.css">
    <link rel="stylesheet" href="../styles/AdminPageStyling copy.css">
    <link rel="stylesheet" href="../styles/adminPagesModal.css">
</head>
<body>
    <header>
        <div class="titleAndMenuIcon">
            <span><span class="titleAndMenuIcon__bulelt">• </span>Kenzie Empresas</span>
            <button>Logout</button>
        </div>
    </header>

    <main>
        <div class="departmentContainer">
            <div class="departmentContainer__header">
                <span class="departmentContainer__header--title">Departamentos</span>
                <div class="departmentContainer__header--companiesSelect">
                    <span class="">Selecionar Empresa</span>
                    <img src="../files/down-arrow-svgrepo-com.svg" alt="">
                </div>
                <dialog class="departmentContainer__header--companiesModal">
                    <ul>
                    </ul>
                </dialog>
                <div class="departmentContainer__header--create"><img src="../files/plusIcon.svg" alt=""><span>Criar</span></div>
            </div>
            <div class="departmentContainer__content">
            </div>
        </div>

        <div class="userContainer">
            <div class="userContainer__header">
                <span class="userContainer__header--title">Usuários Cadastrados</span>                
            </div>
            <div class="userContainer__content">           
            </div>
        </div>
    </main>

    <dialog class="createDepartmentModal">
        <div class="createDepartmentModal__container">
            <img src="../files/closebtnsvg.svg" alt="">
            <span class="createDepartmentModal__container--title">Criar Departemento</span>
            <input type="text" class="createDepartmentModal__container--departmentName" placeholder="Nome do Departamento">
            <input type="text" class="createDepartmentModal__container--departmentDescription" placeholder="Descrição">
            <div class="createDepartmentModal__container--company">
                <span>Selecionar Empresa</span>
                <img src="../files/down-arrow-svgrepo-com.svg" alt="">
            </div>
            <dialog class="createDepartmentModal__container--company__modal">
                <ul class="createDepartmentModal__container--company__modal--container">                    
                </ul>
            </dialog>
            <span class="createDepartmentModal__container--button">Criar Departamento</span>
        </div>
    </dialog>
    
    <dialog class="removeDepartmentModal">
        <div class="removeDepartmentModal__container">
            <span>mensagem aqui</span>
            <div>Continuar</div>
            <img src="../files/closebtnsvg.svg" alt="">
        </div>
    </dialog>

    <dialog class="editDepartmentModal">
        <div class="editDepartmentModal__container">
            <dialog><ul class="editDepartmentModal__container--list"></ul></dialog>
            <img src="../files/closebtnsvg.svg" alt="">
            <span class="editDepartmentModal__container--title">Editar Departamento</span>
            <input type="text" class="editDepartmentModal__container--departmentName" placeholder="Novo Nome do Departamento">
            <input type="text" class="editDepartmentModal__container--departmentDescription" placeholder="Nova Descrição">
            <div class="editDepartmentModal__container--company">
                <span>Selecionar Empresa</span>
                <img src="../files/down-arrow-svgrepo-com.svg" alt="">
            </div>
            <dialog class="editDepartmentModal__container--company__modal">
                <ul class="editDepartmentModal__container--company__modal--container">                    
                </ul>
            </dialog>
            <span class="editDepartmentModal__container--button">Salvar Alterações</span>
        </div>
    </dialog>
</body>
<script src="../scripts/AdminPageScript.js"></script>
</html>

问题原因

  1. 重复绑定事件监听器:每次调用showEditDepartmentModal时,都会给关闭按钮、保存按钮、公司选择按钮等DOM元素绑定新的事件监听器。第一次打开绑定1个,第二次打开再绑定1个,点击时就会触发多次请求/操作。
  2. 重复插入DOM元素:每次打开模态框都会往公司列表中追加新的<li>选项,导致列表越来越长,同时也会累积点击事件。

解决方案

1. 重构事件绑定逻辑,只初始化一次

将模态框的所有事件绑定移到页面初始化阶段,只执行一次,避免重复绑定。打开模态框时仅负责填充数据,而非绑定事件。

2. 清空旧数据再填充

每次打开模态框前,清空公司列表和输入框的旧数据,避免残留内容。

3. 使用事件委托优化卡片点击监听

将卡片的点击事件绑定到父容器上,而非每个卡片单独绑定,适合动态生成的卡片,同时避免重复绑定。

修改后的代码示例

// 初始化模态框事件,仅执行一次
function initEditModalEvents() {
    const editModal = document.querySelector('.editDepartmentModal')
    const closeBtn = document.querySelector('.editDepartmentModal__container > img')
    const nameInput = document.querySelector('.editDepartmentModal__container--departmentName')
    const descriptionInput = document.querySelector('.editDepartmentModal__container--departmentDescription')
    const selectedCompanyInput = document.querySelector('.editDepartmentModal__container--company > span')
    const placeToAppend = document.querySelector('.editDepartmentModal__container--list')
    const showCompanyOptionsButton = document.querySelector('.editDepartmentModal__container--company')
    const dialogCompanyOptions = document.querySelector('.editDepartmentModal__container > dialog')
    const buttonSaveAlterations = document.querySelector('.editDepartmentModal__container--button')
    
    let currentDepartmentId = ''
    let companyToEditid = 0

    // 关闭按钮事件
    closeBtn.addEventListener('click', () => {
        editModal.close()
        resetEditModalState()
    })

    // 显示/隐藏公司选项
    showCompanyOptionsButton.addEventListener('click', () => {
        dialogCompanyOptions.open ? dialogCompanyOptions.close() : dialogCompanyOptions.show()
    })

    // 选择公司
    placeToAppend.addEventListener('click', (event) => {
        if(event.target.tagName === 'LI') {
            companyToEditid = event.target.id
            selectedCompanyInput.innerText = event.target.innerText
            dialogCompanyOptions.close()
        }
    })

    // 保存修改
    buttonSaveAlterations.addEventListener('click', async () => {
        if(!nameInput.value || !descriptionInput.value) {
            alert('vazio')
            return
        }
        editDepartmentFunction(nameInput.value, descriptionInput.value, companyToEditid, currentDepartmentId)
        editModal.close()
        resetEditModalState()
    })

    // 重置模态框状态
    function resetEditModalState() {
        nameInput.value = ''
        descriptionInput.value = ''
        selectedCompanyInput.innerText = 'Selecionar Empresa'
        selectedCompanyInput.id = ''
        companyToEditid = 0
        placeToAppend.innerHTML = ''
    }

    // 填充模态框数据的方法
    return {
        fillData: async (departmentId) => {
            currentDepartmentId = departmentId
            const allDepartments = await getAllDepartments()
            const allCompanies = await getAllCompanies()
            const selectedDepartment = allDepartments.find(e => e.uuid === departmentId)
            
            // 清空列表再插入新选项
            placeToAppend.innerHTML = ''
            allCompanies.forEach(e => {
                placeToAppend.insertAdjacentHTML('beforeend', `<li id="${e.uuid}">${e.name}</li>`)
            })

            // 填充现有数据
            nameInput.value = selectedDepartment.name
            descriptionInput.value = selectedDepartment.description
            selectedCompanyInput.innerText = selectedDepartment.companies.name
            companyToEditid = selectedDepartment.companies.uuid
            selectedCompanyInput.id = selectedDepartment.companies.uuid

            editModal.showModal()
        }
    }
}

// 初始化模态框管理对象
const editModalManager = initEditModalEvents()

// 修改显示模态框的函数,仅调用填充数据方法
async function showEditDepartmentModal(departmentId) {
    await editModalManager.fillData(departmentId)
}

// 改用事件委托监听卡片点击,避免重复绑定
function getAllDepartmentHandleButton() {
    document.querySelector('.departmentContainer__content').addEventListener('click', (event) => {
        const target = event.target
        if(target.classList.contains('remove')) {
            showRemoveDepartmentModal(target.id, target.alt)
        } else if(target.classList.contains('edit')) {
            showEditDepartmentModal(target.id)
        }
    })
}

// 页面加载完成后初始化事件
document.addEventListener('DOMContentLoaded', () => {
    getAllDepartmentHandleButton()
})

function editDepartmentFunction(name, desc, companyId, departmentId) {
    console.log(name, desc, companyId, departmentId)
    // 这里执行实际的请求逻辑
}

内容的提问来源于stack exchange,提问作者okimstartingtocode

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 22:45:33