

- Share On
目次
ページネーションは、正しいルールや表記で実装していないと、道標としての機能が失われ、ユーザーを混乱させてしまいます。今回ページネーションのアクセシビリティ対応と、よりユーザビリティを向上させる方法についてまとめましたので、参考にしてみてください。
ページネーションのトレンド
コンテンツ一覧ページでは、複数のページに分割し、ページ番号ごとに一定の数を出すページネーションが主流ですが、最近では、アクセシビリティ対応だけではなく、ユーザビリティ向上を目的として、代替手法を使ったサイトも増えてきています。
コンテンツ一覧を1ページにして、ユーザーがページを下にスクロールするにつれて、さらにコンテンツを継続的に読み込んでいく無限スクロールや、「もっと見る」ボタンを置いてユーザーのクリックにより、追加コンテンツを表示させていく手法などです。
この手法では、サイトに再訪した時に、コンテンツを見つけにくいデメリットがある反面、直感的にスムーズにコンテンツを表示させることができ、ページの回遊率を高めることができるといったメリットもあります。
Googleでは、ユーザビリティ向上として、モバイルの検索結果ページで、ページネーションの代わりにコンテンツを1ページにする手法も使い始めています。
3つの手法のメリットデメリットはこちらになります。
横にスクロールできます
| UI パターン | メリット | デメリット | 
|---|---|---|
| ページネーション | • 検索結果の全体の長さと現在の位置がわかる | • 検索結果の間を移動する余分な操作が生じる • 新しいページを読み込む必要がある | 
| さらに読み込む | • すべてのコンテンツに 1 ページで対応している • ユーザーは、直感的な操作が可能 | • 場所をブックマークできず、後で戻ってくることが困難(再訪性へのケアが必要) • キーボードだけで操作するユーザーへの負荷 • フォーカス移動のアクセシビリティ上の問題(ボタンをクリックした後、追加コンテンツを無視してしまう) | 
| 無限スクロール | • すべてのコンテンツに 1 ページで対応している • ユーザーは、直感的な操作が可能 | • すべての検索結果が 1 つのウェブページに含まれているため、検索結果が非常に多い場合に表示速度が遅くなる可能性がある • 場所をブックマークできず、後で戻ってくることが困難(再訪性へのケアが必要) • すべてのコンテンツを開くまで、ページの最後にアクセスできない • キーボードだけで操作するユーザーへの負荷 | 
ページネーションには、移動する回数も増え、操作性の部分では、デメリットもありますが、他の手法に比べて、1ページの読み込むコンテンツ量は少なくなり、表示速度の向上も期待できます。
ページネーションだけの実装ではなく、サイトに合った手法も検討すると良いでしょう。
ページネーションのアクセシビリティ対応の重要性
一般的なページネーションでは、何もアクセシビリティを考慮しないと、視覚的には簡単に理解できる内容も、スクリーンリーダーを使ってウェブサイトを操作している人には、音声に頼っているためわかりにくくなってしまいます。
例えば、記号部分(「«」「‹」)の役割や現在のページ位置などを正しく理解できない状態になっているケースが多いです。
ページネーションの要素が何を示しているのか理解できるページネーションを実装することで、誰もが快適に利用できるウェブサイトとなり、サイトの回遊率やコンバージョン率(CV率)の向上も期待できますので、しっかりとアクセシビリティ対応を考えることをおすすめします。
react-paginateでのアクセシビリティ対応
今回は、Reactのライブラリ「react-paginate」でページネーションを実装して、必要なアクセシビリティ対応について、確認していきます。
react-paginateは、出力される番号の数、各タグへのクラス名の付与によって、とてもカスタマイズ性、自由度も高いのが特徴で、すでに下記でご紹介しているように様々なプロパティが用意されています。
プロパティ一覧
横にスクロールできます
| 名前 | 内容 | 
| pageCount | 総ページ数 | 
| marginPagesDisplayed | 先頭と末尾に表示するページの数 | 
| pageRangeDisplayed | 選択位置の前後で表示する番号の件数 | 
| onPageChange | クリックしたときのイベント | 
| containerClassName | ulタグのclass名 | 
| pageClassName | liタグのclass名 | 
| pageLinkClassName | aタグのclass名 | 
| activeClassName | 現在activeなpageのclass名 | 
| previousLabel | previousのラベル名 | 
| nextLabel | nextのラベル名 | 
| previousClassName | previousのliタグのclass名 | 
| nextLinkClassName | nextのliタグのclass名 | 
| previousLinkClassName | previousのaタグのclass名 | 
| nextLinkClassName | nextのaタグのclass名 | 
| disabledClassName | previous・nextが押せなくなった状態の表示 | 
| breakLabel | 省略表示(通常であれば「…」) | 
| breakClassName | 省略表示のulタグのclass名 | 
| breakLinkClassName | 省略表示のliタグのclass | 
詳細は、公式ページをご確認ください。
https://www.npmjs.com/package/react-paginate
実際の出力方法
今回は、https://jsonplaceholder.typicode.com/posts(post総量:100)からテストデータを取得して、ページネーションを出力する手順をご紹介していきます。
# npm installで「react-paginate」導入
npm install react-paginate詳細は、公式ページをご確認ください。
https://www.npmjs.com/package/react-paginate
コード
# モジュールのimportimport ReactPaginate from 'react-paginate';
export default function Pagination() {
         useEffect(() => {
          const getPosts = async () => {
                  await fetch('https://jsonplaceholder.typicode.com/posts')
                  .then((res) => res.json())
                  .then((posts) => setPosts(posts));
               };
               getPosts();
         }, []);
         const [posts, setPosts] = useState<post[]>([]);
         const itemsPerPage = 10;
         const [itemsOffset, setItemsoffset] = useState(0);
         const endOffset = itemsOffset + itemsPerPage;
         const currentPosts = posts.slice(itemsOffset, endOffset);
         const pageCount = Math.ceil(posts.length / itemsPerPage);
         const handlePageClick = (e: { selected: number }) => {
              const newOffset = (e.selected * itemsPerPage) % posts.length;
              setItemsoffset(newOffset);
          };
       return (
          <ReactPaginate className={styles.pagination}
                pageCount={pageCount}
                onPageChange={handlePageClick}
                 containerClassName='pagination'
                 pageClassName={styles['page-item']}
                 pageLinkClassName={styles['page-link']}
                 activeClassName={styles['active']}
                 previousLabel='<'
                 nextLabel='>'
                 previousClassName={styles['page-item']}
                 nextClassName={styles['page-item']}
                 previousLinkClassName={styles['page-link']}
                 nextLinkClassName={styles['page-link']}
                 disabledClassName={styles['disabled']}
                 breakLabel='...'
                 breakClassName={styles['page-item']}
                 breakLinkClassName={styles['page-link']}
            />
        );
   }
今回は、取得したデータの総量(100)を1ページあたり10個の表示とし、itemsPerPageに値を挿入しています。
また取得してきたpostの総量を[itemsPerPage]で割り、pageCount (表示する番号数)のプロパティに代入しています。
const itemsPerPage = 10;
const pageCount = Math.ceil(posts.length / itemsPerPage);ブラウザの表示状態

出力されるDOM
<ul  role="navigation" aria-label="Pagination">
    <li >
        <a
               tabindex="-1" role="button"
               aria-disabled="true"
               aria-label="Previous page"
               rel="prev">
              <
            </a>
    </li>
    <li >
       <a
             rel="canonical"
             role="button"
              tabindex="-1"
              aria-label="Page 1 is your current page"
              aria-current="page">
              1
        </a>
    </li>
    <li >
       <a
            rel="next"
            role="button"
            tabindex="0"
            aria-label="Page 2">
          2
       </a>
     </li>
    <li >
       <a role="button"  tabindex="0" aria-label="Page 3">3</a>
    </li>
    <li >
       <a  role="button" tabindex="0" aria-label="Jump forward">...</a
    </li>
    <li >
       <a role="button"  tabindex="0" aria-label="Page 8">8</a>
    </li>
    <li >
       <a role="button"  tabindex="0" aria-label="Page 9">9</a>
    </li>
    <li >
        <a role="button"  tabindex="0" aria-label="Page 10">10</a>
     </li>
     <li >
        <a
              tabindex="0"
              role="button"
              aria-disabled="false"
              aria-label="Next page"
              rel="next">
            >
         </a>
     </li>
</ul>
アクセシビリティ対応の考慮ポイント
今回「react-paginate」のアクセシビリティ対応について、ポイントに沿って確認してきます。
スクラッチでの実装や、プラグイン対応した際など、ページネーションがしっかりとアクセシビリティ対応されているかの確認にも役立ててみてください。
※【表記】は、「react-paginate」で実装されている表記となります。
aria-label属性
ページ番号や、次へ前へのaria-label属性は、スクリーンリーダーを利用するユーザーには、非常に重要な情報となります。
【表記】
aria-label="Page 2"
aria-label="Previous page"
aria-label="Next page"
aria-current属性
スクリーンリーダーを利用するユーザーに対して、aria-current属性の付与することで、その要素が「現在表示されているページ」を示していることが伝えられます。
【表記】
aria-current="page"
◾️aria-currentの属性値別の説明
横にスクロールできます
| 属性値 | 説明 | 
|---|---|
| page | 「現在のページ」:ページ間のナビゲーションなどで、現在表示されているページを示す。 | 
| step | 「現在のステップ」:マルチステップフォームや手続きの進行状況表示に使用され、ユーザーに現在の進行状況を示す。 | 
| location | 「現在位置」:ナビゲーションメニューやタブ、アコーディオンなどにおいて、現在の位置を示す。 | 
| date | 「現在の日付」:カレンダーウィジェット内で今日の日付を示す。 | 
| time | 「現在の時刻」:タイムテーブルや日程表内で現在の時間帯を示す。 | 
| true | 「現在の項目」:コンテンツ内で重要な項目やアイテムを示す。 | 
| false(デフォルト値) | 「現在の項目」を示さないことを意味する。 | 
aria-disabled属性
1ページ目での前へボタン、最終ページでの次へボタンや、スクリーンリーダー等での読み上げをスキップさせる配慮が、ユーザーを効率良くページを遷移させることができます。
【表記】
aria-disabled="true"
(aria-disabled="false"は読み上げられる状態)
キーボード操作への配慮
キーボード操作を行う方は、特定の場所まで、クリックして進むしかありません。
現在地(カレントページ)のボタンや、最終ページでの次へボタンなど、操作上、一時的に不要になるボタンへは、tabindex を適宜付与することで、遷移させない配慮が必要となります。
【表記】
tabindex="-1"
最適なページネーションの個数
ページネーションの個数が多すぎると、視覚的にも操作的にも効率の悪いものになってしまいます。
キーボード操作で説明したように、クリック操作を行ってサイトを閲覧するユーザーには、リンクの数を減らして効率良く遷移させることが必要です。
「react-paginate」では、marginPagesDisplayedやpageRangeDisplayedで、表示個数のコントロールができます。
marginPagesDisplayed={1} //先頭と末尾に表示するページの数
pageRangeDisplayed={3} //選択位置の前後で表示する番号の件数またより最適なページネーションのために、PCとスマホで、番号の表示個数を変更して、ユーザビリティの向上を行うことも効果的です。
 
 
まとめ
今回、ページネーションについて、重要な対応ポイントや、現状について、説明してきました。
「react-paginate」では、アクセシビリティ対応がしっかりとされている状態でしたが、何気なく、導入したCMSでのプラグインなど、アクセシビリティへの配慮ができていない場合もあります。
また昨今WEBサイトは、スマホからのアクセスが非常に多くなってきており、このような背景から見ても、ページネーションだと、ボタン数が多く、アクセシビリティだけでなく、ユーザビリティを低下させてしてしまう場面もあります。
サイト規模や、サイトのコンテンツによって、最適な手法は一つには限られませんが、Googleのように、PCとスマホのUIを変更することで、ユーザビリティも向上させる一つの対策になりますので、ぜひ検討してみてください。
Contact
制作のご依頼やサービスに関するお問い合わせ、
まだ案件化していないご相談など、
お気軽にお問い合わせください。
- この記事をシェア






















