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

如何在JavaScript中为弹窗实现焦点捕获?

解决弹窗焦点逃逸问题:实现焦点捕获

问题

当前弹窗在键盘导航时,Tab键会将焦点移到弹窗后方的页面元素,需要限制焦点仅在弹窗内的可交互元素(两个操作按钮、关闭按钮)中循环。

实现步骤

  • 收集弹窗内可聚焦元素:将弹窗里的按钮、设置了tabindex="0"的关闭按钮存入数组,用于后续焦点循环处理。
  • 监听键盘事件控制焦点循环:给弹窗添加keydown事件监听,处理Tab/Shift+Tab组合键:
    • 正向按Tab时,若当前焦点是最后一个可聚焦元素,将焦点跳转到第一个元素;
    • 反向按Shift+Tab时,若当前焦点是第一个可聚焦元素,将焦点跳转到最后一个元素;
    • 触发上述情况时阻止默认Tab行为,避免焦点跳出弹窗。
  • 弹窗显示时自动聚焦:弹窗插入DOM后,自动将焦点移到第一个可聚焦元素(推荐关闭按钮,符合可访问性规范)。
  • 弹窗关闭时恢复焦点:记录弹窗打开前的焦点元素,关闭时还原焦点,避免用户失去导航上下文。

修改后的完整代码

// Check if the popup has already been shown during the current session
if (!sessionStorage.getItem('popupShown')) {
    setTimeout(function() {
        var popup = document.createElement("div");
        popup.style.position = "fixed";
        popup.style.top = "0";
        popup.style.left = "0";
        popup.style.zIndex = "999";
        popup.style.backgroundColor = "rgba(0, 0, 0, 0.5)";
        popup.style.width = "100vw";
        popup.style.height = "100vh";
        popup.style.display = "flex";
        popup.style.alignItems = "center";
        popup.style.justifyContent = "center";
        
        // 记录弹窗打开前的焦点元素
        const previousFocus = document.activeElement;

        var innerPopup = document.createElement("div");
        innerPopup.style.backgroundColor = "black";
        innerPopup.style.display = "flex";
        innerPopup.style.flexDirection = "column";
        innerPopup.style.alignItems = "center";
        innerPopup.style.justifyContent = "center";
        innerPopup.style.padding = "40px";
        innerPopup.style.margin = "35px";
        innerPopup.style.borderRadius = "25px";

        var img = document.createElement("img");
        img.src = "https://digitalcloud.co.za/wp-content/uploads/2023/01/kerridge_pop_up_illustration.png ";
        img.alt = "Find out about Kerridge’s core solution ";
        img.style.width = "25%";
        img.style.cursor = "pointer";
        innerPopup.appendChild(img);

        var text = document.createElement("p");
        text.innerHTML = "Interested to find out more about our core solution?";
        text.style.color = "white";
        text.style.marginTop = "50px";
        text.style.textAlign = "center";
        text.style.fontSize = "20px";
        innerPopup.appendChild(text);

        var button = document.createElement("button");
        button.innerHTML = "Book a free demo today";
        button.style.backgroundColor = "#E8017B";
        button.style.color = "white";
        button.style.fontSize = "20px";
        button.style.padding = "15px";
        button.style.borderRadius = "50px"
        button.style.border = "none"
        button.onclick = function() {
            location.href = "https://digitalcloud.co.za/ ";
        }
        innerPopup.appendChild(button);
        
        var secondText = document.createElement("p");
        secondText.innerHTML = "Stay up to date with the latest Kerridge product updates and existing announcements";
        secondText.style.color = "white";
        secondText.style.marginTop = "50px";
        secondText.style.textAlign = "center";
        secondText.style.fontSize = "20px";
        innerPopup.appendChild(secondText);
        
        var secondButton = document.createElement("button");
        secondButton.innerHTML = "Sign up to our Newsletter";
        secondButton.style.color = "#E8017B";
        secondButton.style.background = "transparent";
        secondButton.style.border = "none";
        secondButton.style.fontSize = "20px";
        secondButton.onclick = function() {
            location.href = "https://digitalcloud.co.za/ ";
        }
        
        innerPopup.appendChild(secondButton);

        var closeBtn = document.createElement("div");
        closeBtn.style.position = "absolute";
        closeBtn.style.top = "50px";
        closeBtn.style.right = "50px";
        closeBtn.style.cursor = "pointer";
        closeBtn.style.width = "60px";
        closeBtn.style.height = "60px";
        closeBtn.style.borderRadius = "50%";
        closeBtn.style.backgroundColor = "#E8017B";
        closeBtn.style.display = "flex";
        closeBtn.style.alignItems = "center";
        closeBtn.style.justifyContent = "center";

        closeBtn.setAttribute("tabindex", "0");
        closeBtn.setAttribute("role", "button");
        closeBtn.setAttribute("aria-label", "Close");

        closeBtn.addEventListener("click", function() {
            popup.remove();
            // 恢复之前的焦点
            previousFocus.focus();
        });

        closeBtn.addEventListener("keydown", function(event) {
            if (event.key === "Enter" || event.key === " ") {
                popup.remove();
                // 恢复之前的焦点
                previousFocus.focus();
            }
        });

        var closeX = document.createElement("div");
        closeX.innerHTML = "X";
        closeX.style.color = "white";
        closeX.style.fontWeight = "bold";

        closeBtn.appendChild(closeX);
        closeBtn.onclick = function() {
            popup.remove();
            // 恢复之前的焦点
            previousFocus.focus();
        }
        innerPopup.appendChild(closeBtn);

        popup.appendChild(innerPopup);
        document.body.appendChild(popup);
        
        // 收集弹窗内所有可聚焦元素
        const focusableElements = popup.querySelectorAll('button, [tabindex="0"]');
        const firstFocusable = focusableElements[0];
        const lastFocusable = focusableElements[focusableElements.length - 1];

        // 弹窗显示后自动聚焦到第一个可聚焦元素
        firstFocusable.focus();

        // 监听键盘事件处理焦点循环
        popup.addEventListener('keydown', function(e) {
            if (e.key === 'Tab') {
                // Shift+Tab 反向导航
                if (e.shiftKey) {
                    if (document.activeElement === firstFocusable) {
                        e.preventDefault();
                        lastFocusable.focus();
                    }
                } else {
                    // 正向Tab导航
                    if (document.activeElement === lastFocusable) {
                        e.preventDefault();
                        firstFocusable.focus();
                    }
                }
            }
            // ESC键关闭弹窗(提升可访问性)
            if (e.key === 'Escape') {
                popup.remove();
                previousFocus.focus();
            }
        });

        // Set a value in sessionStorage indicating that the popup has been shown
        sessionStorage.setItem('popupShown', 'true');
    }, 90000 );
}

额外说明

  • 新增ESC键关闭弹窗的逻辑,符合可访问性最佳实践;
  • 焦点循环逻辑仅作用于弹窗内的可交互元素,确保键盘导航不会跳出弹窗范围。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 08:56:10