クモのようにコツコツと

フロントエンドエンジニア イイダリョウの技術ブログ。略称「クモコツ」

【JS】Web Speech APIを使ってページのテキスト読み上げボタンを実装してみた

テキスト読み上げのスクリーンリーダーについて調べたところ、なんとJSで「Web Speech API」というのがあることを知った!どんな風に読み上げられるのか、実際にやってみた。それではいきましょう!

【目次】

Web Speech APIとは

これまでTone.jsでブラウザで音を鳴らしたりしいた。「Web Audio API」という機能を使っている。

※参考:Web Audio APIの機はもうすぐ熟しそう!ブラウザ対応状況♪ - クモのようにコツコツと

※参考:【Tone.js】コード切り替えボタンと鍵盤の連携 - クモのようにコツコツと

「Web Speech API」はどんな機能?こちらの解説が詳しい。

※参考:Web Speech APIの実装 - Speech Synthesis API | CodeGrid

サンプルではinputタグに入れた文字が読み上げられる。

※参考:シンプルなデモ | Web Speech API -> speechSynthesis

caniuseでみるとIE以外のブラウザはいけるようだ!

※参考:Can I use... Support tables for HTML5, CSS3, etc

ページ読み上げボタン完成品

inputタグではなく、ページのテキストを読み上げたい実装してみた。

See the Pen Web Speech API 01 by イイダリョウ (@i_ryo) on CodePen.

ヘッダーにある「テキスト読み上ボタン」を押すとテキストが読み上げられる。なんだか「モヤモヤさまぁ~ず」みたいな喋り方wテキストトゥスピーチってやつですな。

JSコード(SpeechSynthesisUtterance()

var btn = document.querySelector('.btn');
var speech = document.querySelector('#speech');
var text = speech.textContent;
console.log(text);

btn.addEventListener( 'click', function() {
    var msg = new SpeechSynthesisUtterance();
    msg.text = text; 
    msg.lang = 'ja-JP';
    window.speechSynthesis.speak(msg);        
}, false );
  • 変数btnで読み上げボタン(.btn)のDOMを取得
  • 変数speechでテキスト読み上げたい箇所のタグ(.#speech)のDOMを取得
  • 変数textでspeechの中のテキストを取得(textContentプロパティ)
  • console.logはtextが取得されたかのテスト用
  • btnをクリックしたときのイベント
  • 変数msgでSpeechSynthesisUtteranceインスタンスを作成
  • msgのtextプロパティにtextを入れる
  • msgのlangプロパティを日本語に指定
  • windowオブジェクトのspeechSynthesisプロパティのspeak()メソッドを実行。引数はmsg

ポイントになるはSpeechSynthesisUtteranceインスタンス、speechSynthesisプロパティ、speak()メソッド!

ネット上の記事では最初の記事のようにinputタグに入れた中身を喋らせる事例が多いのだが、自分がやりたいのはページ本文の読み上げ。

こちらの記事にその方法が書かれていた!inputタグを読ませるのはvalueプロパティだが、本文を読ませるのはtextContentプロパティ!

※参考:Web Speech API(TTS部分のみ)を試してみた - E-riverstyle Vanguard

HTMLコード(いろんな要素の読み上げ実験)

実際に読み上げられた部分。

読み上げボタン

読み上げボタンはこちら。

<header>
    <input type="button"  value="▶︎" class="btn"> ←テキスト読み上ボタン
</header>

headerタグの中のinputボタン。class名が.btn。

見出し(hタグ)と段落(pタグ)

<section id="speech">
<h1>テキスト読み上げテスト</h1>
<p>うまくいけばここのテキストが再生される。たぶんされると思う。されるんじゃないかな。まちょと覚悟はしておけ。</p>
    <section>
    <!--中略-->
    </section>
</div>

まずは見出しと段落が読み上げられるかテスト。読み上げられた!

ルビ(rubyタグ)

「生ゴミ生ゴメ生タマゴ」と読ませたら「せいごみせいごめせいたまご」と「なま」が「せい」と読まれた。

ルビタグを入れたらそっちが優先されるかと思い、やってみた。

<h2>ルビ</h2>
    <p>生ゴミ生ゴメ生タマゴ</p>
    <p><ruby><rp>(</rp><rt>ナマ</rt><rp>)</rp></ruby>ゴミ<ruby><rp>(</rp><rt>ナマ</rt><rp>)</rp></ruby>ゴメ<ruby><rp>(</rp><rt>ナマ</rt><rp>)</rp></ruby>タマゴ</p>
    <p>※読み間違えたまま二重で読まれる。</p>
    </section>

rubyタグでルビを書く。

※参考:<ruby>-HTML5タグリファレンス

結果「せい なま ごみせい なま ごめせい なま たまご」と二重で読まれるだけだった。声の区切りが変なところに入るw

リスト(liタグ)

リストがどのように読まれるかテスト

   <section>
    <h2>リスト</h2>
    <ul>
        <li>ラオウ</li>
        <li>トキ</li>
        <li>ジャギ</li>
        <li>ケンシロウ</li>
        </ul>
      <p>※リスト間は間髪なく読まれる。</p>
    </section>

結果、リストは句読点がないので間髪なく一気に読まれた。なお、「間髪」は「あいだかみ」と読まれたw

テーブル(tableタグ)

テーブルがどのように読まれるか。

   <section>
    <h2>テーブル</h2>
        <table>
        <tr>
            <th></th><th></th>
        </tr>
        <tr>
            <td>山田</td><td>太郎</td>
        </tr>
        <tr>
            <td>山田</td><td>次郎</td>
        </tr>
        <tr>
            <td>山田</td><td>三郎</td>
        </tr>
    </table>
          <p>※一行ずつ左から右に読まれる。</p>
    </section>

trタグの1行ごとに読まれ、tdタグの列を左から右の順に読まれる。これは意図通りの読まれ方でイイ!

画像(alt属性とfigcaptionタグ)

画像のalt属性を読んでくれるか。

   <section>
    <h2>画像</h2>
    <figure> <img src="http://placehold.jp/300x200.png" alt="画像の説明文">
        <figcaption>画像のキャプション</figcaption>
        </figure>
    <p>※alt属性は読まれない。</p>
    </section>

結果、alt属性は読んでくれない!

alt属性を読ませるにはtextContentプロパティをaltプロパティにする必要がある。しかしそれをすると他の本文テキストが取得できない。(テキストとalt属性が混在したページを書かれた順番に取得する方法があるかもしれないが、よくわからず)

画像の説明はfigcaptionタグで書いた方が良さそう。

ちなみに、ダミー画像の配置は「Placehold.jp」を利用した。楽でいいですね!

※参考:Placehold.jp|ダミー画像生成 モック用画像作成

ウィジェット埋め込み(Twitter)

最後に、SNSのウィジェット埋め込みなどが読みあげられるか。

   <section>
    <h2>ウィジェット埋め込み</h2>
    <a class="twitter-timeline" data-width="300" data-height="500" data-dnt="true" href="https://twitter.com/Twitter?ref_src=twsrc%5Etfw">Tweets by Twitter</a> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
    <p>※アカウント名しか読まれない</p>
    </section>

結果、アカウント名の部分は読み上げられたが投稿部分のテキストは読まれなかった。

その他のスクリーンリーダー

chromeブラウザにはテキスト読み上げ機能がある。

※参考:テキストを読み上げる - Chromebook ヘルプ

Mac、iOsにはテキスト読み上げ機能VoiceOverがある。

※参考:Mac OS X における音声読み上げ (VoiceOver) | Accessible & Usable

※参考:iPhoneでVoiceOverをオンにして練習する - Apple サポート

Windows10にテキスト読み上げナレーター機能「ナレーター」があるようだ。

※参考:https://support.microsoft.com/ja-jp/help/22798/windows-10-complete-guide-to-narrator

Windowsのフリーソフトはいくつかあるようだ。

※参考:無料音声読み上げソフト一覧 - フリーソフト100

全部は試せていないが、VoiceOverはテキストとalt属性が同時に読まれた!ただし「せいごみ」とかの読み違いは同じだった。

日本語は音読み訓読みがあるので、実際に試しながら他の言葉に書き換える(または仮名文字に開く)必要がありそう。

最後に

ということでテキスト読み上げボタンを実装できました!

f:id:idr_zz:20191020225036j:plain

ソフトを入れたりブラウザ機能を使うのは少し手順が増えるので、ボタン1つで簡易的にでも実装したかった。

目が見えない方向けのページを作る時に有能だと思います。これに加えて、文字サイズの大中小ボタンなど、ユニバーサルデザインなサイトを作っていきましょう!

ちなみにWebアクセシビリティの仕様書を「WCAG」。このページ自体が膨大なテキスト量でハードルの高さを感じてます。。

※参考:Web Content Accessibility Guidelines (WCAG) 2.0

それではまた!


※参考:ネイティブJSやってみたシリーズ
qiita.com