~ホームページに多彩な機能を追加する~
CSSとJSを別のファイルに移す
教材①の最後になると、だんだんCSSとJSのコードが増えてきて、読みづらくなってきていますね(これを可読性が低いと言います)。実際のホームページには更に大量のHTMLタグが記述されていますし、CSSもJSも何倍ものコードが書かれています。それを一つのファイルに書いておくのは良くないですよね。本格的にJSを学ぶ前に、HTMLのファイルから、CSSとJSを別のファイルに移して読みやすくし、変更もしやすくする方法を学びます。
前に出てきたsample03.htmlのCSSとJSを他のファイルに出してみましょう。
- jsworkフォルダの中に、cssとjsというフォルダを作成してください。
- jsworkの上で右クリックし、sample03a.htmlを作成し以下のコードを貼り付けて保存してください。
<!--
sample01のCSSとJSを別のファイルに出すサンプルです
このページの(DOCTYPE = document type)ドキュメントタイプはHTMLです
つまり、このファイルはHTMLで書かれていますよ、という宣言です
-->
<!DOCTYPE html>
<!-- ここからHTMLを書きますよ、という意味です -->
<html>
<!--
headタグの中には、ホームページを表示する前提条件を書きます
ここに書かれたことは画面に表示はされませんが、文字化けを防ぐなど大事な役割があります
-->
<head>
<!-- 日本語を表示する方法を指定しています -->
<meta charset="UTF-8">
<!-- ブラウザーのタブに表示されるタイトルです -->
<title>サンプル02</title>
<!--
指定ファイルをCSSとして読み込みます
HTMLがある階層のcssフォルダの中のsample03a.cssを読み込みます
-->
<link href="css/sample03a.css" rel="stylesheet" />
</head><!-- headタグここまで -->
<!-- bodyタグの中に書かれたものが画面に表示されます -->
<body>
<!-- 大きな文字で見出しを書くタグです -->
<h1>これは見出しです</h1>
<!-- この段落には、ptag1というidがセレクタとして定義されています -->
<p id="ptag1">一つ目の段落です</p>
<!-- ここから下の三つ段落には、ptagsというclassがセレクタとして定義されています -->
<p class="ptags">二つ目の段落です</p>
<p class="ptags">三つ目の段落です</p>
<p class="ptags">四つ目の段落です</p>
<!--
指定ファイルをJSとして読み込みます
HTMLがある階層のjsフォルダの中のsample03a.jsを読み込みます
-->
<script src="js/sample03a.js"></script>
</body><!-- bodyタグここまで -->
</html><!-- htmlタグここまで -->
- 同様に、cssフォルダの中にsanitize.cssを作成し、githubからコードをコピーして貼り付けてください
リンク先: sanitize.css
sanitize.cssは、ブラウザの画面表示をリセットするためのcssです。色々なホームページをサーフィンしていると、ごくたまに前のページのCSS設定が残ってしまい、正常に画面が表示されなくなってしまう、という現象が起こるので、それを回避するために一旦CSSの設定を初期値に戻すために使われます。
- 同様に、cssフォルダの中にsample03a.cssを作成し、以下のコードを貼り付けて保存してください。
/*
CSSのコメント(説明)はこう書きます
これは1.HTMLタグをそのままセレクタとして利用した例です
この書き方だと、h1タグの文字は全て暗い赤色で表示されます
*/
h1 {
/* 文字色を変更 */
color: darkred;
/* 文字を画面中央に表示 */
text-align: center;
}
/*
idをセレクタとして利用する例です
#abcと書くことで、
idセレクタがptag1の部品に{}内のデザインをあてるという意味になります
*/
#ptag1 {
/* 文字色を変更 */
color: rgb(0, 0, 255);
font-size: 24px;
}
/*
classをセレクタとして利用する例です
.xyzと書くことで、
classセレクタがptagsの部品に{}内のデザインをあてるという意味になります
*/
.ptags {
/* 文字色を変更 */
color: rgb(0, 64, 0);
font-size: 20px;
}
- 同様に、jsフォルダの中にsample03a.jsを作成し、以下のコードを貼り付けて保存してください。
// h1タグのエレメントを取ってくる
const elH1 = document.querySelector("h1");
elH1.innerText = "JSでh1の文章を変更しました";
// ptag1というidを持つエレメントを取ってくる
const elP1= document.querySelector("#ptag1");
elP1.innerText = "JSでptag1の文章を変更しました";
// ptagsというclassを持つエレメントのうち最初に見つかったものを一つだけ取ってくる
const elP2= document.querySelector(".ptags");
elP2.innerText = "JSでptagsの文章を変更しました";
sample03a.htmlをLive Serverで表示し、CSSとJSが正常に動作しているか確認してみましょう。
HTMLファイルが短くなって、読みやすくなりましたね。また、CSSとJSを別ファイルにしたことで不要なインデントが無くなり、こちらも読みやすくなっています。
最終サンプル学習① ~講師紹介の吹き出し~
それでは最終演習に移る前に、サンプルページのJS処理について一つずつ学習していきましょう。比較的に簡単なものから順に解説していきますので、ページに表示された順番とは違う順序になることをご了承ください。
まずは、吹き出しの表示機能です。弊社講師のサムネイルにマウスを合わせると吹き出しが表示され、マウスが外れると消えるようになっています。この機能はこれまでに学んだことの応用で実装されていますが、いくつかポイントがあります。HTMLでは以下のように記述されています。
<div class="container mb-4">
<div class="row">
<div class="col-lg-4" style="text-align: center; margin-top: 10px;">
<div class="tooltipBlock">
<p class="toolTip fukidashi" id="toolTip1">受講者に再会して「ありがとうございました」と言って頂けるのが嬉しいです。</p>
<img src="img/yamazaki320.png" id="toggle1" alt="山崎講師" height="200px" style="border-radius:50%;"
onmouseover="mOver(1)" onmouseleave="mLeave(1)">
<br>
</div>
<h6>山崎講師</h6>
</div>
<div class="col-lg-4" style="text-align: center; margin-top: 10px;">
<div class="tooltipBlock">
<p class="toolTip fukidashi" id="toolTip2">研修後に笑顔でお帰りになる様子を見られるときが一番嬉しいです。</p>
<img src="img/tabuchi318.png" id="toggle2" alt="田渕講師" height="200px" style="border-radius:50%;"
onmouseover="mOver(2)" onmouseleave="mLeave(2)">
<br>
</div>
<h6>田渕講師</h6>
</div>
<div class="col-lg-4" style="text-align: center; margin-top: 10px;">
<div class="tooltipBlock">
<p class="toolTip fukidashi" id="toolTip3">積極的にいろいろ質問や相談をしてもらえると何故か嬉しくなっちゃいますね。</p>
<img src="img/imai320.png" id="toggle3" alt="今井講師" height="200px" style="border-radius:50%;"
onmouseover="mOver(3)" onmouseleave="mLeave(3)">
<br>
</div>
<h6>今井講師</h6>
</div>
</div>
</div>
<p class="toolTip">~と書かれた部分が三つありますね。ここには各講師の吹き出しに入る文章が書かれています。それぞれにIDが付いていますが、各IDの最後に1~3の連番が付与されていることを覚えておいてください。また、imgタグのonmouseoverとonmouseleaveという記述にも注目です。これは、「この画像にマウスが重なった」「この画像からマウスが外れた」というイベントを契機に指定したJS関数を発火させるためのコードです。続く関数の呼び出し部分に、1~3の番号を引数として渡しています。
ここで、CSSを見てみましょう。以下は、このpタグに適用されているtoolTipというクラスの記述です。display属性がnoneになっていますね。これはつまり、ページが表示された時点では、この吹き出しは表示されない設定になっている、ということです。
p.toolTip {
display: none;
position: absolute;
top: -110px;
left: 50%;
transform: translateX(-50%);
}
次にJS部分を見てみましょう。mOver関数がマウスが重なった時に、mLeave関数がマウスが外れた時に呼び出されます。引数の番号がどう使われているかが、コードから伝わるでしょうか。"toolTip"という文字列に番号を連結することでIDを都度生成し、どの吹き出し部品を取ってくるかを切り替えているのです。このように、同じ処理を行う部品操作を一つの関数で行う為に連番を用いる、というやり方は昔からよく使われています。実はもっと効率の良いやり方もあるのですが、まずはこちらを理解しましょう。
mOver関数でdisplay属性をblockに設定することで、吹き出しが表示されているのですね。逆にmLeave関数ではdisplay属性をnoneにしているので、マウスが外れると吹き出しが消える、という実装になるわけです。このように、画面部品のオブジェクトは自身のデザインをstyleというオブジェクトとして持っています。styleオブジェクトの属性を操作してやることで、画面部品のデザインを場合に応じて変更することが可能になります。
/*
①講師紹介の吹き出し表示
★学習のポイント
・マウスのイベント(マウスオーバー、マウスリーブ)
・同じ操作を行う複数の画面部品に対するIDの扱い方
・JSでCSSを操作する方法の復習
*/
function mOver(num) {
let popup = document.querySelector('#toolTip' + num);
popup.style.display = 'block';
}
function mLeave(num) {
let popup = document.querySelector('#toolTip' + num);
popup.style.display = 'none';
}
最終サンプル学習② ~二重送信防止と入力内容の取得~
次は、入力した内容を送信するボタンの操作です。問い合わせや記事投稿などを行うページでよく実装されるのが、二重送信防止という機能です。これは、ボタンをカチカチと連続で押しても、一度だけ送信するようにするためのものです。こうしておかないと、何度も同じ内容の情報が送られてきてしまい、色々と不都合が起きてしまうわけですね。まずはHTMLです。
formは送信する情報を定義するためのタグで、<form>と</form>に挟まれた画面部品が持つ情報が送信される仕組みです。
<form onsubmit="send(event)">
<table class="table">
<tr>
<th>お名前</th>
<td><input type="text" name="name" id="name" placeholder="お名前"></td>
</tr>
formタグの中に、onsubmit=という記述がありますね。これは、「このフォームが送信された」というイベントを契機にsendというJS関数を実行してください、という意味です。
では、JSのコードを見てみましょう。
/*
②送信ボタン(二重送信防止と入力内容表示)
★学習のポイント
・二重送信防止処理
・入力内容をJSで取得する
*/
function send(event) {
// フォーム送信を防止
event.preventDefault();
// 各画面部品のオブジェクトを変数に入れる
let nameInput = document.querySelector('#name');
let mailInput = document.querySelector('#email');
let zipcodeInput = document.querySelector('#zipcode');
let addressInput = document.querySelector('#address');
let speakInput = document.querySelector('#speakTarget');
// 各部品オブジェクトから入力内容を取得し、メッセージを編集する
let msg = "名前:" + nameInput.value
+ "\nメール:" + mailInput.value
+ "\n〒:" + zipcodeInput.value
+ "\n住所:" + addressInput.value
+ "\n評価:" + app.rating
+ "\n内容:" + speakInput.value
+ "\nこのメッセージを閉じたら、送信ボタンが無効になっていることを確認してください。";
// メッセージの表示
alert(msg);
console.log(msg);
// 送信ボタンを無効に設定
let btn = document.querySelector('#sendButton');
btn.setAttribute('disabled', true);
}
send関数の引数であるeventというのは、formの送信を行うオブジェクトです。preventDefaultメソッドを呼び出すことで、送信処理自体をキャンセルしています。続く処理は、情報入力用の各部品から入力内容を取ってきてメッセージを作り、アラートのウィンドウとコンソールに表示しています。最後に、ボタンのオブジェクトを取ってきて、ボタンが押せないようにボタンを無効化しています。これにより、一度このボタンが押されるとそれ以上クリックが出来ないようになって情報が一度だけ送信される、という実装になるわけです。
便利なJSライブラリ
次の学習の前に、JSのライブラリについて概要をお伝えしておきましょう。JSは長い歴史の中で進化を繰り返してきました。その過程で「ライブラリ」と呼ばれる便利なものが多数開発されています。ライブラリとは、よく使う機能や一から自分で作るのに手間がかかる機能をまとめて手軽に使えるようにしたもので、初学者にとっても経験を積んだ技術者にとっても非常に便利です。ゼロからすべてをコーディングする必要がなくなり、時間を節約しつつ、高品質なコードを書くことができるからです。
ライブラリの利点
- 時間の節約: ライブラリを使うことで、一般的な機能や操作を簡単に実装できます。例えば、アニメーションの作成やDOM操作(HTML要素の操作)など、複雑なコードを書く必要がありません。
- コードの簡潔化: ライブラリは、頻繁に使われる機能をシンプルなAPI(アプリケーションプログラミングインターフェース)として提供します。これにより、コードが短く読みやすくなります。
- 信頼性: 多くのライブラリはコミュニティや企業によって開発・メンテナンスされており、バグが少なく安心して利用できます。
以下、色々なライブラリを使った実装のサンプルです。興味のある方はご覧になってみてください。
- AOS.jsを使いスクロール時に要素にアニメーション効果をつけたい (Sample)
- GASPを使いアニメーションしたい (Sample)
- Luminous.jsを使いコンテンツを拡大表示したい (Sample)
- Slickを使いスライドショーを表示したい (Sample)
- Vanta.jsを使いマウスに反応する鳥の群れの効果を追加したい (Sample)
- darkmode.jsを使ってWebサイトにダークモードを実装したい (Sample)
ライブラリはバージョンを確かめて使いましょう
JSライブラリは大変便利なものですが、ネットで調べた記事を参考に導入するときは、ライブラリのバージョンに注意してください。同じライブラリでもバージョンによって導入方法や使い方が変わります。参考記事にあるコードが、別のバージョンでは全く動作しなかったりすることも日常茶飯事です。全く同じバージョンのライブラリを導入するのが一番確実ですが、他のライブラリとの相性問題等で違うバージョンを利用する場合は、そのバージョンに合った参考記事を探すなどして問題を避けるようにしてください。
最終サンプル学習③ ~Vue.jsによる星評価~
では、サンプルの学習に戻りましょう。次は、記事の評価を簡単に入力してもらうためによく使われる★の実装です。Vueというライブラリを使っています。HTMLの記述はたったこれだけです。
<tr>
<th>評価</th>
<td><star-rating v-bind:star-size="30" v-model="rating"></star-rating></td>
</tr>
star-ratingなんて見慣れないタグがありますね。これは、Vueによって解釈されるHTMLには無いタグで、これで星の大きさを30に設定して五段階の評価入力の画面部品を表示しています。Vueが星評価の画面部品をオブジェクトとして持っていて、それをwindow.documentオブジェクトに登録して表示している、というのが内部的な処理になります。Vueの導入部分はこうなります。
<!-- Vue.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
<script src="https://unpkg.com/vue-star-rating@1.6.3/dist/star-rating.min.js"></script>
これは、ネット上に共有されているVueと星評価のライブラリを読み込んで利用しますよ、という記述です。このライブラリの利用方法を、CDNと呼びます。他にも利用法はあるのですが、まずは一番手軽なCDNを覚えましょう。
また、ご覧のようにVueは2.6.10というバージョンを使っています。Vueは2系と3系のバージョンで使い方がかなり違いますので注意してください。次に、JSを見てみましょう。
/*
③Vue.jsによる星評価
★学習のポイント
・JSライブラリVueの利用
・★評価の実装
*/
const StarRating = window.VueStarRating.default;
Vue.component('star-rating', StarRating);
let app = new Vue({
el: '#app',
data: {
rating: 3
}
});
component(コンポーネント)という見慣れない言葉がありますね。難解な概念なので技術的な説明は省きますが、要するにこれで星評価の画面部品オブジェクトを生成してwindow.documentに登録し、star-ratingというタグに紐付けているのです。画面表示時の評価は3に設定されています。
便利なWeb API
さて、次の学習の前に、Web API(ウェブ エーピーアイ)の概要についてお伝えします。APIとは、(Application Programming Interface)の略で、インターネット上に存在する関数だと思ってください。近年はAPIの種類も豊富になり、機能も大変充実してきました。リアルタイムで東証の株価を取ってきたり、目的地の緯度と経度から予約可能な宿泊先リストをもらったりと、多種多様な用途で、しかも無料で利用できるAPIがたくさんあります(もちろん有料のAPIもあります)。皆さんがよく見かける地図も、APIを利用して表示されているのです。
使い方ですが、JSの関数を使う時は、()の中に引数を入れて渡し、その戻り値を利用して何か処理をするのでしたね。APIも同じです。ただし、引数をインターネットのURLに追記して渡すことと、戻り値がJSONというちょっと変わった形式になっていること、がJSの関数と違うところです。それでは、郵便番号から住所を検索する実例を見ながら、APIの使い方を学んでいきましょう。
最終サンプル学習④ ~郵便番号で住所検索~
では、JSとWeb APIを使って、郵便番号から住所を検索する機能を実装します。まずはHTMLの部分から見ていきましょう。
<tr>
<th>郵便番号</th>
<td>
<input type="text" name="zipcode" id="zipcode" placeholder="郵便番号を入力"/>
</td>
</tr>
<tr>
<th></th>
<td><button type="button" class="btn btn-primary" onClick="searchAddress()">住所検索</button></td>
</tr>
<tr>
<th>住所</th>
<td><textarea rows="3" cols="24" name="address" id="address" placeholder="ここに住所が表示されます"></textarea></td>
</tr>
HTMLでは郵便番号の入力欄、検索ボタン、住所を表示する入力欄の三つが定義されており、二つの入力欄にはIDが、ボタンにはonClickで発火するJS関数が書いてあります。では、JSの部分を見てみましょう。
/*
④郵便番号で住所検索
★学習のポイント
・JSによるWeb APIの利用
・fetch構文
*/
function searchAddress() {
let zipcodeInput = document.querySelector('#zipcode');
let zipcode = zipcodeInput.value;
let el = document.querySelector('#address');
fetch(`https://api.zipaddress.net/?zipcode=${zipcode}`)
.then(response => response.json())
.then(data => {
if (data.code === 200) {
el.value = data.data.fullAddress;
console.log(data.data.fullAddress);
} else {
el.value = '住所が見つかりませんでした。';
}
})
.catch(error => {
console.error('エラーが発生しました:', error);
el.value = 'エラーが発生しました。';
});
}
ボタン押下で発火する関数のコードです。まずは郵便番号の入力欄から番号を取得します。次に住所を表示する画面部品のオブジェクトを取ってきます。その次のfetchと書いてある部分がAPIの呼び出し処理です。引数にURLと、それに続いて/?zipcode=${zipcode}`という記述がありますね。これは、APIに、zipcodeという名前でzipcode変数の中身を渡しますよ、という意味です。
例えば、910-1111という郵便番号の住所を検索したい場合には、fetch関数の引数はこうなります。
fetch(`https://api.zipaddress.net/?zipcode=910-1111`)
緯度と経度を渡す等、複数の引数が必要な場合は、&でつなげて以下のようにURLを書きます。
APIのURLの最後に/?と書いて名前=内容、更に引数が必要な場合は&名前=内容...と書いていくのですね。では、このfetch構文についてもう少しお伝えしていきましょう。.thenという記述が二つと、.catchという記述が続いていますね。最初のthenでは、APIの戻り値をdataという変数名のオブジェクトに変換しています。APIが返した情報をdataというオブジェクトに詰めた状態になった、ということです。
data.codeがAPIの実行の成功失敗を表す番号です。正常に処理が行われた場合はこの番号が200になります。if文は正常終了ならAPIが返した住所を入力欄に表示し、そうでなければ住所が見つからない、という表示をしているわけです。続く.catchの部分は、どこかでエラーが発生した場合の処理を書きます。エラーとは例えばネット障害でAPIが呼び出せなかった、又はAPIがオブジェクトに変換できないデータを返してきた(通信エラーなどが原因)、というような場合です。
大まかな処理の流れを復習すると、fetchでまずAPIに引数を渡して実行し、その結果をオブジェクトに変換、正常終了時とそれ以外で表示を変更、その過程のどこかでエラーが発生したらその旨を表示する、という処理を行っていることになります。演算子の右辺と左辺は右辺が先に実行されましたが、ドットでつないだ処理は、左から実行されるのだと覚えてください。左の処理結果を前提にして次の処理を行うので、演算子の場合とは処理順が逆になるのですね。
最終サンプル学習⑤ ~jQueryによる画面部品の初期設定~
jQueryは、ちょっと前まで最も一般的に利用されてきた巨大なJSライブラリです。一部の追加ライブラリの開発が終了したことでjQueryの時代は終わったなど、様々な論議を醸していますが、全世界のWebページの六割以上がjQueryを利用して書かれているという統計も上がっており、また、jQuery本体は現在も開発が続いています。今後も使われていく公算が大きいと判断してこの講義でも取り上げることにいたしました。
jQueryの導入
Vue.jsと同様に、CDNでjQueryを導入している部分のHTMLを見てみましょう。
<!-- jQuery -->
<script src="https://code.jquery.com/jquery-3.6.3.min.js"></script>
<!-- autokana.js -->
<script src="js/jquery.autoKana.js"></script>
jQueryの特徴は$マーク
jQueryのコードは、素のJSとはずいぶん違った書き方になります。まずはサンプルページのJS部分を見てみましょう。
/*
⑤jQueryによる画面部品の機能初期設定
★学習のポイント
・無名関数
・画面部品のフェードアウト
・slickスライダー
・jQueryの追加ライブラリによる漢字<->かな変換
*/
$(document).ready(function(){
// ウェルカムメッセージをフェードアウト
$('#fadeOut').fadeOut(3000);
// Slickによる画像スライダー
$('.slick-slider').slick({
infinite: true,
slidesToShow: 1,
slidesToScroll: 1,
dots: true,
arrows: true,
autoplay: true,
autoplaySpeed: 2000,
});
// 漢字->ひらがな自動変換
$.fn.autoKana("#name", "#kana", {
katakana: false, // true に設定するとカタカナに変換されます
});
});
最初の行の$(document)という記述を見て、なんだこれはと思われた方も多いと思いますが、このドルマークこそがjQueryの特徴です。jQueryは、$を使って簡単にJS処理を実装するライブラリなのです。$(document).ready(function(){~という記述は、この画面が表示されたときにfunction(){以下に書かれた処理を実行してください、という意味のコードです。
このfunction(){~を、無名関数(又はコールバック関数)と言います。関数は機能を部品化して再利用できるようにする、というのが本来の使い方なのですが、ここでしか使わない処理なら別に関数に名前を付けなくてもいいじゃないか、名無しの関数を直接書いてしまえばいいだろうという発想で生まれたものです。()丸括弧の中に{}波括弧があって少し煩雑に見えますが、分解して考えればそんなに難しくはありません。Webで見つかる参考文献にも無名関数を用いたサンプルも多く存在しますので、この機会に慣れておいてくださいね。
$('#fadeOut').fadeOut(3000)は、IDがfadeOutの画面部品を3000ミリ秒かけて徐々に消してください、という意味になります。実際、背景が黄色のウェルカムメッセージが三秒ほどで徐々に消えて行き、最終的には見えなくなっていますね。ここで気付かれた方もいらっしゃると思いますが、jQueryを利用した画面部品の取得は、ただ$('#ID')や、$('.class')と書くだけで良いのです。querySelectorを使わずとも画面部品が取ってこれるわけです。
$('.slick-slider').slick({~は、スライドショーを実装するコードですが、どんなスライドショーにするかの設定をするだけで、対象のクラスに自動スライドを実装できる、簡単なサンプルになっています。
最後の$.fn.autoKana~は、追加ライブラリの自動仮名変換機能を各入力欄(名前入力と仮名出力)に紐付けている処理です。
本来はjQueryを使わずに、素のJSでまず実装し、それをjQueryを使ったらどうなるのかをやってみる、というのが正攻法の学習だと思いますが、ネット上で散見するJSのサンプルコードには、jQuery導入が前提になっているモノも多数見受けられます。$を見かけたら、ああこれはjQueryのコードなのだと判断して、柔軟な頭脳でサンプルを解釈できるようになりましょう。
最終サンプル学習⑥ ~音声読み上げ機能~
サンプル学習の最後は、目の不自由な方向けのアシスト機能として近年注目を浴びている音声読み上げの実装です。この例では、ブラウザが持っている各言語の音声を利用する方法を用いています。読み上げ用音声を持っているブラウザは、edgeやchromeが代表で、firefoxなどは今のところ音声を持っていませんので、その点は留意してください。それでは、JSのコードを見てみましょう。
/*
⑥音声読み上げ機能(Web Speech API)
★学習のポイント
・イベントリスナーの使い方
・ブラウザが持っている音声の取得
・言語設定と読み上げ機能の利用
*/
const lang = 'ja-JP';
let voices = [];
// ブラウザが他言語音声を持っていればイベントリスナーに処理を登録
if (window.speechSynthesis) {
speechSynthesis.addEventListener('voiceschanged', setVoices);
}
// ブラウザから他言語音声を取ってきて配列に代入
function setVoices() {
if (voices.length) return;
voices = speechSynthesis.getVoices();
}
// 音声読み上げ処理本体
function speak() {
if (window.speechSynthesis) {
setVoices();
// 発話リクエストを生成
const uttr = new SpeechSynthesisUtterance();
// 読み上げ対象のテキストを設定
let el = document.querySelector('#speakTarget');
uttr.text = el.value;
// 言語を設定
uttr.lang = lang;
// 言語に対応しているvoiceを設定
// ただし、firefoxやoperaは独自の音声を持たない
let found = false;
for (let i = 0; i < voices.length; i++) {
if (voices[i].lang === lang) {
uttr.voice = voices[i];
found = true;
break;
}
}
// 再生
if (found) {
window.speechSynthesis.speak(uttr);
}
}
}
最初のlangという変数には、日本語を表す文字列を代入しています。次のvoicesという変数には、空の配列を入れておきます。ブラウザが持っている多言語音声から日本語の音声を選んで使う準備をしているわけですね。
次のif (window.speechSynthesis) {というif文は、画面を表示しているブラウザが読み上げ用音声を持っていれば処理を行う、という条件分岐構文です。音声を持っていないブラウザでこの画面を表示しても、何も処理は行われません。speechSynthesis.addEventListener('voiceschanged', setVoices)は、音声が変更されたときにこの関数を呼んでください、とイベントで発火する関数を設定しています。「音声の変更」?と思われた方も多いと思いますが、ブラウザの音声を取得する定型文と思ってください。この書き方であれば、音声を持っていないブラウザでは何も処理は行われません。
次のsetVoices関数では、voicesが空の時だけブラウザから音声を取ってきています。一度音声を取得した後は何もしない、という書き方です。
最後のspeak()関数が、読み上げ処理の本体になります。uttrが読み上げ機能を持つオブジェクトで、日本語の音声を選んでuttrのvoiceに代入し、読み上げ対象の文章を画面の入力欄から取ってきて読み上げ実行、という処理になります。コードを簡略化してわかりやすくするため、このサンプルには必要最小限の処理しか書かれていませんが、実際にはボリューム調整や、同じ日本語でも男性、女性、読み方の違いなどによる複数の音声が利用可能で、音声を切り替えたり音読速度を調整したりと、高機能な実装も可能です。
最終演習
それではいよいよ、最後の演習です。皆さん、それぞれ自分のレベルに合わせて目標を決めてください。サンプルページの機能を変更したり追加しても結構ですし、材料になるホームページを既にお持ちの方はそれに機能追加してもいいです。自由な発想で、やりたいことに挑戦してみましょう!