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

SwiperJS配置:仅在激活幻灯片中实现键盘Tab导航

SwiperJS 滑块无障碍键盘导航配置方案

需求背景

当前使用SwiperJS滑块(通常含3张幻灯片)时,用户用键盘导航到滑块区域后,焦点会落在不可见的幻灯片上,导致位置迷失。需要实现:仅允许在**激活(可见)**的幻灯片内进行Tab切换,同时遵循以下理想交互流程:

  • 用户焦点落在激活幻灯片的首个可聚焦链接「查看详情」上
  • 按Tab键切换到「立即购买」CTA按钮
  • 按Tab键切换到幻灯片左侧的「上一页」按钮
  • 按Tab键切换到幻灯片右侧的「下一页」按钮
  • 按Tab键切换到对应此幻灯片的底部分页圆点,继续按Tab可遍历所有分页圆点
  • 最后一次按Tab时,焦点离开滑块区域,继续导航页面其余部分

现有配置

options = {
    init: false,
    a11y: true,
    watchOverflow: true,
    navigation: {
        nextEl: null,
        prevEl: null,
        hideOnClick: true
    },
    pagination: {
        el: null,
        clickable: true
    },
    loop: true,
    keyboard: true,
    mousewheel: true,
},

解决方案

1. 调整Swiper配置项

修改基础配置,关闭循环模式(避免克隆幻灯片干扰焦点),完善导航和分页的DOM选择器,增强无障碍属性:

options = {
    init: false,
    a11y: true,
    watchOverflow: true,
    navigation: {
        nextEl: '.swiper-button-next', // 替换为你的下一页按钮选择器
        prevEl: '.swiper-button-prev', // 替换为你的上一页按钮选择器
        hideOnClick: false, // 保持导航按钮可见,方便键盘用户识别
        disabledClass: 'swiper-button-disabled' // 禁用状态添加类,便于样式区分
    },
    pagination: {
        el: '.swiper-pagination', // 替换为你的分页容器选择器
        clickable: true,
        bulletClass: 'swiper-pagination-bullet',
        bulletActiveClass: 'swiper-pagination-bullet-active'
    },
    loop: false, // 关闭循环模式,避免克隆幻灯片干扰焦点
    keyboard: {
        enabled: true,
        onlyInViewport: true // 仅当滑块在视口中时响应键盘操作
    },
    mousewheel: true,
    slidesPerView: 1, // 确保一次只显示一张幻灯片
    preventClicks: false, // 避免阻止可点击元素的默认行为
    preventClicksPropagation: false
};

2. 焦点管理与不可见幻灯片处理

仅靠配置无法完全实现需求,需添加额外JS逻辑,控制不可见幻灯片内可聚焦元素的tabindex,并在幻灯片切换时更新焦点状态:

// 初始化Swiper
const swiper = new Swiper('.swiper-container', options);

// 切换幻灯片时更新可聚焦元素的tabindex
function updateSlideTabIndex() {
    swiper.slides.forEach((slide, index) => {
        const focusableElements = slide.querySelectorAll('a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])');
        if (index === swiper.activeIndex) {
            // 激活幻灯片:恢复tabindex,允许聚焦
            focusableElements.forEach(el => {
                el.removeAttribute('tabindex');
                el.removeAttribute('aria-hidden');
            });
            // 自动聚焦到激活幻灯片的第一个可聚焦元素(可选)
            if (focusableElements.length > 0) {
                focusableElements[0].focus();
            }
        } else {
            // 不可见幻灯片:设置tabindex="-1",禁止Tab访问
            focusableElements.forEach(el => {
                el.setAttribute('tabindex', '-1');
                el.setAttribute('aria-hidden', 'true');
            });
        }
    });
}

// 初始化后执行一次
swiper.on('init', updateSlideTabIndex);

// 幻灯片切换时更新
swiper.on('slideChange', updateSlideTabIndex);

// 自定义Tab焦点顺序,匹配需求流程
swiper.on('init', () => {
    const navigationPrev = swiper.navigation.prevEl;
    const navigationNext = swiper.navigation.nextEl;
    const paginationBullets = swiper.pagination.bullets;
    
    // 激活幻灯片最后一个可聚焦元素Tab后跳转到上一页按钮
    const activeSlideLastFocusable = swiper.slides[swiper.activeIndex].querySelectorAll('[tabindex]:not([tabindex="-1"])').pop();
    if (activeSlideLastFocusable && navigationPrev) {
        activeSlideLastFocusable.addEventListener('keydown', (e) => {
            if (e.key === 'Tab' && !e.shiftKey) {
                e.preventDefault();
                navigationPrev.focus();
            }
        });
    }
    
    // 上一页按钮Tab后跳转到下一页按钮
    if (navigationPrev && navigationNext) {
        navigationPrev.addEventListener('keydown', (e) => {
            if (e.key === 'Tab' && !e.shiftKey) {
                e.preventDefault();
                navigationNext.focus();
            }
        });
    }
    
    // 下一页按钮Tab后跳转到第一个分页圆点
    if (navigationNext && paginationBullets.length > 0) {
        navigationNext.addEventListener('keydown', (e) => {
            if (e.key === 'Tab' && !e.shiftKey) {
                e.preventDefault();
                paginationBullets[0].focus();
            }
        });
    }
    
    // 最后一个分页圆点Tab后离开滑块区域
    if (paginationBullets.length > 0) {
        const lastBullet = paginationBullets[paginationBullets.length - 1];
        lastBullet.addEventListener('keydown', (e) => {
            if (e.key === 'Tab' && !e.shiftKey) {
                // 可自定义跳转至滑块后的首个可聚焦元素,或保留浏览器默认行为
                const nextFocusable = document.querySelector('.swiper-container').nextElementSibling?.querySelector('[tabindex]:not([tabindex="-1"])');
                if (nextFocusable) {
                    e.preventDefault();
                    nextFocusable.focus();
                }
            }
        });
    }
});

// 初始化Swiper
swiper.init();

关键说明

  • 关闭loop模式是核心:循环模式会自动克隆幻灯片,这些克隆元素会出现在DOM中,导致键盘焦点意外落到不可见的克隆幻灯片上。
  • tabindex控制:通过动态设置不可见幻灯片内可聚焦元素的tabindex="-1",阻止Tab键访问这些元素,同时添加aria-hidden="true"告知屏幕阅读器忽略这些元素。
  • 焦点顺序自定义:通过监听keydown事件,手动控制Tab键的焦点跳转路径,确保符合需求的交互流程。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 07:53:10