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

模态窗口关闭时触发Uncaught TypeError错误的修复求助

问题修复:模态窗口关闭时的TypeError错误(保留动画效果)

我帮你揪出问题根源啦!这个Uncaught TypeError: Cannot read property 'remove' of undefined错误主要踩了几个JavaScript常见的坑,咱们一步步把它修好,同时保留你需要的动画效果:

问题核心原因

  1. 作用域泄漏:setTimeout是异步执行的,当它触发时,外部的modal变量已经失效,导致访问modal.style报错。
  2. 错误的遍历方式:用for...in遍历modals数组会把数组的原型属性也包含进来,遍历到非DOM元素时自然找不到classList。
  3. 重复的状态修改:关闭逻辑里两次执行open_modals.pop(),还错误地移除所有模态的动画类,导致模态栈状态混乱。

修复后的完整代码

HTML部分

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!-- Trigger/Open The Modal -->
<a href="#myModal1" class="modal-button">• Click Me</a>
<!-- The Modal -->
<div id="myModal1" class="modal">
    <!-- Modal content -->
    <div class="modal-content">
        <div class="modal-header">
            <span class="close">×</span>
            <div class="headertext">
                <p>Modal Header</p>
            </div>
        </div>
        <div class="modal-body">
            <img class="pic" src="https://drive.google.com/thumbnail?id=108ZLeoIfNkKODfRbLuPWpmXRl0gH9qkD">
            <div class="bodytext">
                <h2 class="modal-button" href="#myModal2">Enable Second Modal Window by tapping on this text</h2>
            </div>
        </div>
    </div>
</div>
<div id="myModal2" class="modal">
    <!-- Modal content -->
    <div class="modal-content">
        <div class="modal-header">
            <span class="close">×</span>
            <div class="headertext">
                <p>Modal Header 2</p>
            </div>
        </div>
        <div class="modal-body">
            <img class="pic" src="https://drive.google.com/thumbnail?id=108ZLeoIfNkKODfRbLuPWpmXRl0gH9qkD">
            <div class="bodytext">
                <h2>You have opened modal window no.2</h2>
            </div>
        </div>
    </div>
</div>

CSS部分

@import url('https://fonts.googleapis.com/css?family=Quicksand&display=swap');
/* The Modal (background) */
.modal {
    box-sizing: border-box;
    font-family: 'Quicksand', sans-serif;
    display: none; /* Hidden by default */
    position: fixed; /* Stay in place */
    z-index: 1; /* Sit on top */
    padding-top: 0.1875em; /* Location of the box */
    left: 0;
    top: 0;
    width: 100%; /* Full width */
    height: 100%; /* Full height */
    overflow: auto; /* Enable scroll if needed */
    background-color: rgb(0, 0, 0); /* Fallback color */
    background-color: rgba(0, 0, 0, 0.4); /* Black w/ opacity */
}
/* Modal Content */
.modal-content {
    color: white;
    position: relative;
    background-color: #171B20;
    margin: auto;
    padding: 0;
    border: 0.0625em solid #888;
    width: 97%;
    box-shadow: 0 0.25em 0.5em 0 rgba(0, 0, 0, 0.2), 0 0.375em 1.25em 0 rgba(0, 0, 0, 0.19);
    -webkit-animation-name: animatetop;
    -webkit-animation-duration: 0.4s;
    animation-name: animatetop;
    animation-duration: 0.4s;
}
/* Add Animation */
@-webkit-keyframes animatetop {
    from { top: -300px; opacity: 0; }
    to { top: 0; opacity: 1; }
}
@keyframes animatetop {
    from { top: -300px; opacity: 0; }
    to { top: 0; opacity: 1; }
}
@-webkit-keyframes animateBottom {
    from { top: 0px; opacity: 1; }
    to { top: 500px; opacity: 0; }
}
@keyframes animateBottom {
    from { top: 0px; opacity: 1; }
    to { top: 300px; opacity: 0; }
}
.modal-content-active {
    -webkit-animation-name: animateBottom;
    -webkit-animation-duration: 0.4s;
    animation-name: animateBottom;
    animation-duration: 0.4s;
}
/* The Close Button */
.close {
    color: #F0B823;
    float: right;
    font-size: 9vw;
    font-weight: bold;
    position: absolute;
    right: 0.25em;
    top: -0.25em;
}
.close:hover,
.close:focus {
    color: #fff;
    text-decoration: none;
    cursor: pointer;
}
.modal-header {
    padding: 0.125em 1em;
    background-color: #171B20;
    color: #F0B823;
}
.modal-body {}
.modal-button {
    font-family: 'Quicksand', sans-serif;
    background-color: #171B20;
    border: none;
    color: white;
    padding: 0.248em 0.496em;
    text-align: left;
    text-decoration: none;
    display: inline-block;
    font-size: 7vw;
    margin: 0.124em 0.062em;
    -webkit-transition-duration: 0.4s; /* Safari */
    transition-duration: 0;
    cursor: pointer;
    width: auto;
}
.modal-button:hover {
    background-color: #171B20;
    color: #F0B823;
}
.pic {
    margin: auto;
    display: block;
    height: auto;
    width: 50vh;
}
.headertext {
    font-family: 'Quicksand', sans-serif;
    display: block;
    text-align: center;
    font-size: 6.50vw;
}
.bodytext {
    font-size: 3.90vw;
    font-family: 'Quicksand', sans-serif;
    display: block;
    padding: 0.625em 0.9375em;
}
p {
    display: block;
    margin: 0;
}

JavaScript部分

let open_modals = [];
$(function () {
    // Get all modal buttons
    const btns = document.querySelectorAll(".modal-button");
    // All page modals
    const modals = document.querySelectorAll('.modal');
    // Get all close buttons
    const spans = document.getElementsByClassName("close");

    // Open modal logic
    btns.forEach(btn => {
        btn.onclick = function (e) {
            e.preventDefault();
            const targetModalId = e.target.getAttribute("href");
            const modal = document.querySelector(targetModalId);
            if (modal) {
                modal.style.display = "block";
                // 确保打开时重置动画类
                modal.querySelector('.modal-content').classList.remove('modal-content-active');
                open_modals.push(modal.id);
            }
        }
    });

    // 封装关闭顶层模态框的逻辑
    const closeTopModal = () => {
        if (open_modals.length === 0) return;
        
        const topModalId = open_modals[open_modals.length - 1];
        const topModal = document.getElementById(topModalId);
        if (!topModal) return;

        // 添加关闭动画类
        const modalContent = topModal.querySelector('.modal-content');
        modalContent.classList.add('modal-content-active');

        // 动画结束后隐藏模态框并清理状态
        setTimeout(() => {
            topModal.style.display = "none";
            modalContent.classList.remove('modal-content-active');
            open_modals.pop();
        }, 400);
    };

    // 点击关闭按钮触发关闭
    Array.from(spans).forEach(span => {
        span.onclick = closeTopModal;
    });

    // 点击模态背景触发关闭
    window.onclick = function (event) {
        if (event.target.classList.contains('modal')) {
            closeTopModal();
        }
    };
})

关键修复细节

  1. 作用域优化:用const/let声明变量,避免全局泄漏,同时在封装的closeTopModal函数中保存目标模态框,确保setTimeout能正确访问到元素。
  2. 正确遍历元素:用forEach替代for...in和普通for循环,只遍历DOM元素,避免无效属性干扰。
  3. 动画逻辑统一:把关闭逻辑封装成独立函数,统一处理动画添加、延时隐藏和状态清理,避免重复代码。
  4. 精准DOM操作:只对当前关闭的模态框内容添加/移除动画类,不会影响其他激活的模态框。
  5. 状态正确维护:只在动画结束后执行一次open_modals.pop(),确保模态栈的状态始终准确。

这样修复后,逐层打开/关闭的功能正常,动画效果保留,控制台也不会再报错啦!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 07:26:32