NITS TIPS

~微塵集合知巧~

swiper/reactで実装したカルーセルをTAB操作した時、画面に描画されていないスライドにフォーカスが当たってしまう

最終更新日: 2024-07-27記事投稿日: 2024-07-27

nobumitsu-1995のgithubトップ

Frontend developer

nobumitsu-1995


概要

カルーセルのスライドは多くの場合、リンクになっているためTABキーでフォーカスが当てられるようになっています。

しかし、何も対策しないとTABキー操作をした時に画面に描画されていないスライドにもフォーカスが当たってしまいます。これはWCAG2.0の達成基準2.4.3「フォーカスの順序」達成基準2.4.7「フォーカスの可視化」の基準が満たせなくなるため好ましくない状態です。

カルーセルには全部で1~9のスライドがあります。画面には1~3のスライドしか描画されていませんが、4~9のスライドにもフォーカスが当たるようになってしまっています。

修正方法

IntersectionObserverを利用して画面に描画されているかを検知し、tabindexaria-hiddenを制御することで修正対応しました。

tabindex-1を指定すればfocusしないようになり、0を付与すればページのソース内の順序でfocusされるようになります。

参考コードは以下になります。

import React, { useRef, useEffect } from 'react';

export const SlideItem = ({ index }) => {
  const ref = useRef(null)

  useEffect(() => {
    const observer = new IntersectionObserver(([entry]) => {
      ref.current?.setAttribute('aria-hidden', entry.isIntersecting ? false : true)
      ref.current?.setAttribute('tabindex', entry.isIntersecting ? 0 : -1)
    });

    observer.observe(ref.current);

    return () => observer.disconnect();
  }, [])

  return (
    <a href="/" ref={ref}>
      <img src={`https://placehold.jp/150x150.png?text=${index}`} alt={index} />
    </a>
  )
}

上記コンポーネントを以下のようにswiperから提供されているコンポーネントでラップしてあげるとフォーカスの制御がうまくいきます。

import React, { useRef, useState } from 'react';
import { Swiper, SwiperSlide } from 'swiper/react';

import 'swiper/css';
import 'swiper/css/pagination';
import 'swiper/css/navigation';

import './styles.css';

import { Pagination, Navigation } from 'swiper/modules';
import { SlideItem } from './SlideItem';

export default function App() {

  return (
    <>
      <Swiper
        slidesPerView={3}
        navigation={true}
        pagination={true}
        loop={true}
        modules={[Pagination, Navigation]}
        className="mySwiper"
      >
        {[1,2,3,4,5,6,7,8,9].map(index => (
          <SwiperSlide key={index}>
            <SlideItem  index={index}/>
          </SwiperSlide>
        ))}
      </Swiper>
    </>
  );
}
フォーカスは画面に描画されている3スライドのみ当たるようになりました。次スライドに移動することでaria-hiddenやtabindexが変わっていることも確認できます。

参考文献


関連記事

  • 最終更新日: 2024-05-06記事投稿日: 2024-05-06

    初回レンダリング時のみ、useEffect内の処理を実行しないようにする方法

    コンポーネントの初回レンダリング時のみ、useEffect内の処理を実行したくないという場面で、どのような処理を加えれば実現できるかを紹介します。

  • 最終更新日: 2024-08-06記事投稿日: 2024-08-06

    ARIA属性を活用したアクセシブルなモーダル実装

    アクセシブルなmodalを実装するために必要なaria属性が意外と多い。参考となるコードをメモとして残しておきたい。

  • 最終更新日: 2024-07-09記事投稿日: 2024-07-09

    PayloadとArgumentの使い分け方

    typescriptで型を宣言するとき〇〇Payloadとか××Argumentとか名付けることが多いがその違いについて真面目に考えたことがなかったので調べてみた。

  • 最終更新日: 2024-04-08記事投稿日: 2024-04-08

    StorybookのInteractionテストでcreatePortalした要素にアクセスできない時の対処法

    Storybookでテストを実装した時、createPortalして描画しているモーダルにアクセスできないことに気がつきました。このような場合の解決方法を紹介します。