uga.dev - A Front-end Engineer's shed

現在のテーマは「ライトモード」です。

Reactのテキストは記述の仕方で読み上げが変わってしまう件について

-

  • #React
  • #VoiceOver
  • #ユーザビリティ

Reactで何かがクリックされた回数を変数countにいれて画面に表示させるような場合、次のように書かれることがほとんどでしょう[1]

jsx
<p>Clicked {count} times!</p>

筆者がこのようなコードを書く場合はテンプレートリテラルを使うか、変数に一度格納してから出力するようにしています。

テンプレートリテラルを用いる例
<p>{`Clicked ${count} times!`}</p>;
変数に格納する例
const content = `Clicked ${count} times!`;

// ...

<p>{content}</p>

React 19時点では、JSX内に書かれた文字列はデータの前後で別々のテキストノードが作成されます。1つの文字列として結合しておかないと、テキストが個別のノードに分かれてしまうためです。

コード

ノード数

テキストノード

Clicked {count} times!

3

「Clicked 」「1」「 times!」

`Clicked ${count} times!`

1

「Clicked 1 times!」

テキストノードが分割されると何が起きるのか

どちらの方法で実装しても表示上の見た目には変化はありませんが、スクリーンリーダーユーザの体験に差を生み出します[2]

テスト環境

結合なしの読み上げ方

結合ありの読み上げ方

Windows 11 + NVDA 2025.1.2jp + Firefox 140

全文

全文

macOS 15 + VoiceOver

全文

全文

iOS 18 + VoiceOver

分割

全文

iPadOS 18 + VoiceOver

分割

全文

iOSのVoiceOverはテキストノードごとに読み上げるため、ユーザはテキストノードの数だけ余計にカーソルを動かす必要があります[3]。 読み上げの最適化に取り組んでいるiOSのVoiceOverのユーザが多いWebサイトならば、ユーザビリティの観点から結合してレンダリングする方法を採用するのも1つの選択肢かもしれません。

とくに見出しなどの一息で読み上げて欲しいコンテンツに関しては、一考の余地があるのではないでしょうか。

ただし、ライブリージョンの観点ではノードが分割されていない場合、aria-atomic="true"のように全文が読み上げられる点には留意が必要です。

どのような読み上げがされてほしいかによって、ライブリージョンの範囲やDOMの構造は変わってくるでしょう。変更部分だけがアナウンスされてほしいようなケースでは、テキストノードが分割された状態のほうがユーザビリティは高いかもしれません。

あくまで豆知識

たしかにVoiceOverのことだけを考えるならば1つの文字列に結合してからテキストは出力した方がいいシーンがあるかもしれません。 一方で、特定の環境のために変わった書き方に統一するというのも少しイマイチに感じます。

筆者としては、iOSおよびiPadOSのVoiceOverが、テキストノード単位ではなく要素単位で読み上げるようにアップデートされることを期待しながら、こういう違いがあるということを頭の片隅に置いておく程度がちょうどよいのかもしれないと思っています。

脚注

† YOUR COOKIE PREFERENCES †

当サイトは「うぇぶ⭐︎ひょーじゅん!」の二次創作を扱う非公式ファンサイトです。

  • 当サイトの画像および内容などの無断転載、加工使用、再配布、直リンクなどは禁止です。
  • 表示がおかしい場合はIE6.0以上、800×600以上の環境でご覧ください。
  • この先、BGMが自動再生されます。不要な方はBGMオフのリンクからご入場ください。

uga.dev

推奨環境:Windows XP IE6.0↑/フォントサイズ:中↑/解像度:1024x768↑

なお、ここまでの内容はすべてうそです。

当サイトでは Google社が提供するアクセス解析ツール「Google Analytics」を利用しています。

Cookieの利用に同意していただける場合はEnterからご入場ください。

もっと詳しく

「Google Analytics」は利用状況の把握およびサービス改善のためにCookieを使用して、利用者のウェブサイト訪問履歴などの情報を収集しています。

収集されたデータは匿名で集計されており、個人を特定する情報は含まれません。

「同意しない」を選択すると、Cookieを利用せずに当サイトが閲覧できます。

同意後もプライバシーポリシーページから再度拒否することができます。

詳細はGoogleのプライバシーポリシーおよびオプトアウト方法をご確認ください。