JavaScriptへの最初のダイブ②(数字当てゲーム)
MDN( https://developer.mozilla.org/ja/docs/Web/JavaScript )
を参考に勉強するように注意が入ったので、こらを教本にしていきます。
その為、抜粋しながら進めたいと思います。
今回もこちら 1)のページです
下準備
今回は前回の数字当てのコードについて、詳しく見ていこうと思います。
まず初めにHTML,CSS 2)を入れます。
1.データーを保持する変数
//1から100までのランダムな数字が代入
let randomNumber = Math.floor(Math.random() * 100) + 1;
//段落に値を追加するために使用
const guesses = document.querySelector('.guesses');
const lastResult = document.querySelector('.lastResult');
const lowOrHi = document.querySelector('.lowOrHi');
//テキスト入力フォームおよび送信ボタンへの参照が保持され、後で予想の送信をコントロールする際に使用される。
const guessSubmit = document.querySelector('.guessSubmit');
const guessField = document.querySelector('.guessField');
//プレイヤーが予想した回数を記録するため1
let guessCount = 1;
//リセットボタンへの参照を保持
let resetButton;
Math.floor() 関数
小数点以下を切り捨てる
Math.floor(数値)
【今回】
let randomNumber = Math.floor(Math.random() * 100);
-
変数を宣言、再代入が出来るletを使用。(let 変数名 = 初期値;)
-
Math.random()で0以上1未満(0は含むが、1は含まない)の乱数を得る。
(※乱数とは:0から9までの数字が不規則かつ等確率に現れるように配列されたもの。) -
「* 100」で、欲しい値の最大値より1つ上の数を掛ける。
こちらだと変数randomNumberに「0~99」の範囲の整数がランダムに格納されます。
let randomNumber = Math.floor(Math.random() * 100) + 1;
今回は数字が100までなので+1を足して「100」にする必要があります。
document.querySelector()
Document の querySelector() メソッドは、指定されたセレクターまたはセレクター群に一致する、文書内の最初の要素を返します。一致するものが見つからない場合は null を返します。
【今回】
3つの変数(guesses、lastResult、lowOrHi)が
HTMLのこちらを指定しています。
<div class="resultParas">
<p class="guesses"></p>
<p class="lastResult"></p>
<p class="lowOrHi"></p>
</div>
2つの変数(guessSubmit、guessField)が
HTMLのこちらを指定しています。
<label for="guessField">予想を入力してください: </label>
<input type="text" id="guessField" class="guessField">
<input type="submit" value="予想を投稿" class="guessSubmit">
変数の値を変更しない時は『const』を使用。(const 変数名 = 初期値;)
2.予想の確認
function 関数の名前(){}
関数は再利用可能なコードのかたまりです。
何度も実行することができ、同じコードを書く手間を省けます。
今回はfunction 関数の名前(){ 関数を呼ぶたびに実行されるコード }
の書き方を見ていきます。
こちらのコードを先ほどのJavaScriptに追加します。
function checkGuess() {
alert('ここはプレースホルダです');
}
コンソールに下記を入力すると・・・。
checkGuess();
こちらのように警告が表示されました。
呼び出すと、いつでも警告が表示される関数を定義することができました。
先ほどのfunction 関数の名前(){ 関数を呼ぶたびに実行されるコード }
を利用してのコードです。
function checkGuess() {
//現在のテキストフィールドに入力された値をセット。テキストフィールドに入力された値が数値であることを確認。
const userGuess = Number(guessField.value);
//guessCount変数が1(プレイヤーの初回の予想)であるかどうかを判定。
if (guessCount === 1) {
guesses.textContent = '前回の予想: ';
}
//予想が表示されるときにはスペースで区切られて表示するように記載。
guesses.textContent += userGuess + ' ';
//ランダムな数字を格納したrandomNumber変数の値と等しいかどうかを調査。もし等しければ、プレイヤーは正解し勝ち(祝福のメッセージを緑色で表示)。さらに、数字の大小を表示する段落をクリアして、後で説明するsetGameOver()関数を実行。
if (userGuess === randomNumber) {
lastResult.textContent = 'おめでとう!正解です!';
lastResult.style.backgroundColor = 'green';
lowOrHi.textContent = '';
setGameOver();
//else if(){ } という部分で、ひとつ前の条件に続けて条件を記載。この条件はユーザの最後のターンかどうかを調査。最後の回ならば、プログラムはゲームオーバーとする以外は、ひとつ前の部分と同じことを実行。
} else if (guessCount === 10) {
lastResult.textContent = '!!!ゲームオーバー!!!';
lowOrHi.textContent = '';
setGameOver();
//前の二つの条件がどちらも当てはまらなかった場合のみ実行。(プレイヤーは間違えてはいるものの、予想回数が残っている。) この場合、プレイヤーに予想が間違っていることを伝え、入力された数字が大きいか小さいかを伝えるため、さらなる条件の確認を行う。
} else {
lastResult.textContent = '間違いです!';
lastResult.style.backgroundColor = 'red';
if(userGuess < randomNumber) {
lowOrHi.textContent = '今の予想は小さすぎです!' ;
} else if(userGuess > randomNumber) {
lowOrHi.textContent = '今の予想は大きすぎです!';
}
}
//次の予想の入力を受け取るための準備。guessCount変数に1を加算し、プレイヤーの予想回数を数えます。(++はインクリメント演算子で、1だけインクリメント(増加)。) そして、入力フォームのテキストフィールドを空にしてから焦点を当て、プレイヤーの次の入力に備える。
guessCount++;
guessField.value = '';
guessField.focus();
}
Number()
値が数値であるかを検証したり文字列を数値に変換
const 変数名 = Number(対象要素.value);
valueとは:値。
const userGuess = Number(guessField.value);
ここではuserGuessという変数を宣言して、現在のテキストフィールドに入力された値をセットしています。また、組み込みの Number() 関数を呼び出して、テキストフィールドに入力された値が数値であることも確認しています。
if文について
- if:条件分岐を伴うコードブロック
()内の条件式が成り立つ場合に{}内の処理が実行される。
if (条件式) {
条件式が成り立った場合処理を実行
}
if (guessCount === 1) {
guesses.textContent = '前回の予想: ';
ここでは「guessCount === 1」とguessCount変数と1が全く同じかどうか(プレイヤーの初回の予想)の確認・判定が入っています。
- else if:複数条件の分岐に使用
Aの場合、Bの場合、それ以外の場合、というように条件を複数設定するときに使えるのがelse ifです。
if (条件式1) {
条件式1が成り立った場合処理を実行
}else if(条件式2){
条件式2が成り立った場合処理を実行
}
- else:今までの条件に当てはまらない場合に使用
次のようにif文に付け加えることで、条件式が成り立たない場合の処理も書くことができます。
if (条件式) {
条件式が成り立った場合処理を実行
}else{
条件式が成り立たなければ処理を実行
}
上記のif式を使用した今回はこのような意味になります。
(簡易)
if (userGuess === randomNumber) {
正解
}else if(guessCount === 10){
ゲームオーバー(10回失敗)
}else{
間違い(回数はまだ残っている)
if (userGuess < randomNumber) {
予想が小さい
}else if(userGuess > randomNumber){
予想が大きい
}
}
「+= userGuess + ' '」について
guesses.textContent += userGuess + ' ';
「+=」 が元の変数に値を加算という意味。
「userGuess + ' '」を記載することで、予想が表示されるときにはスペースで区切られて表示する。
3."予想を入力"のボタンが押されたときに、イベントを起こす
//guessSubmitボタンに対して、イベントが発生したことを聞き取る構成(イベントリスナー)を追加。これは発生したことを知りたいイベントの種類 (click)と、イベントが発生した場合に実行するコード (checkGuess()) の2つの入力値 (引数) を取る関数です。
guessSubmit.addEventListener('click', checkGuess);
.addEventListener()
対象要素に特定のイベントが配信されるたびに呼び出される関数
対象要素.addEventListener(種類, イベントが発生した場合に実行する処理(関数), オプション)
イベントを検知するための仕組みのことを「イベントリスナ」といい、
イベントリスナを定義するためのJavaScriptのメソッドが「addEventListener」(イベントリスナをadd = 追加する)です。
guessSubmit.addEventListener('click', checkGuess);
今回はguessSubmitボタンに対して、イベントが発生したことを聞き取る構成(イベントリスナー)を追加。
これは発生したことを知りたいイベントの種類 (click)と、イベントが発生した場合に実行するコード (checkGuess()) の2つの入力値 (引数) を取る関数です。
4. ゲームの機能を完成させる(ゲームオーバーになった時)
function setGameOver() {
//入力フォームのテキストフィールドとボタンのdisabledプロパティをtrueに設定することで、入力できないようにしている。
guessField.disabled = true;
guessSubmit.disabled = true;
//新しいbutton要素を生成し、そのラベルに"新しいゲームを始める"という文言を設定し、HTMLページに追加。
resetButton = document.createElement('button');
resetButton.textContent = '新しいゲームを始める';
document.body.appendChild(resetButton);
//上で生成したボタンがクリックされたときに resetGame() という関数が実行されるようにイベントリスナーを設定
resetButton.addEventListener('click', resetGame);
}
disabled,true
- disabled
コントロールに対してユーザーからの入力を受け付けないように設定をするための論理属性。存在する場合、その要素は変更不可、フォーカス不可、フォームへの送信不可となります。
フォームコントロール(ボタンやチェックボックスのようなフォーム内のユーザーインターフェース要素。)にdisabled属性が指定された場合、ブラウザーはそのようなコントロールをグレーアウトし、マウスクリックやフォーカス関連のイベントなどの閲覧イベントをほとんどの場合受け取りません。
- true
数値の0や文字列の空文字、またnullやundefined以外の値(数値、文字列、オブジェクト)
【今回】
この2つを組み合わせてこちらのコードになっています。
guessField.disabled = true;
guessSubmit.disabled = true;
ちなみに、数値の0や空文字、nullやundefinedの時は「false」を使用します。
document.createElement(),appendChild()
要素名で指定されたHTML要素を生成。
document.createElement(生成する要素名)
crreateElement()メソッドで生成した要素を、実際のHTML要素に追加することで HTMLを動的に生成することができます。
HTML要素への追加には appendChild()メソッドなどを使用します。
HTML要素.appendChild(生成した要素)
【今回】
間にテキストの内容表示「textContent」を入れてこちらのようになっています。
resetButton = document.createElement('button');
resetButton.textContent = '新しいゲームを始める';
document.body.appendChild(resetButton);
5. ゲームの機能を完成させる(次のゲームができるようにゲームを初期化)
function resetGame() {
//guessCount に 1 を代入して元に戻す
guessCount = 1;
//情報段落のすべてを消去。
const resetParas = document.querySelectorAll('.resultParas p');
for (let i = 0 ; i < resetParas.length ; i++) {
resetParas[i].textContent = '';
}
//リセットボタンをページから削除。
resetButton.parentNode.removeChild(resetButton);
//入力フォームの要素を使用可能にして、新しい予想が入力できるようにテキストフィールドを空にしてフォーカスを設定します。
guessField.disabled = false;
guessSubmit.disabled = false;
guessField.value = '';
guessField.focus();
//最終結果を表示している lastResult 段落の背景色を消去。
lastResult.style.backgroundColor = 'white';
//同じ数字以外の数字でゲームができるように、新しいランダムな数字を再度生成
randomNumber = Math.floor(Math.random() * 100) + 1;
}
Document.querySelectorAll()
指定したセレクターの条件と一致したHTML要素を全て返します。
返り値はひとつであっても配列として返されます。
【今回】
document.querySelectorAll('.resultParas p');
「.resultParas p」なので、こちらの情報に該当します。
<div class="resultParas">
<p class="guesses"></p>
<p class="lastResult"></p>
<p class="lowOrHi"></p>
</div>
for文
for文によるループは指定された条件がfalseと評価されるまで繰り返されます。
【例】
コンソールにこちらを入力すると・・・。
for (let i = 1 ; i < 11 ; i++) { console.log(i) }
こちらのように表示されます。
for ([初期化式]; [条件式]; [加算式]){
実行する文1;
実行する文2;
...
}
- 初期値: 上の例では、iを1から始めましたが、どんな数字でもかまいません。さらに言えば、iという名前でなくともかまいません。ただし、ループでは短くて覚えやすいため、iという変数の名前がよく使われる。
- 条件: 上の例では「i<11」をループが継続する条件としました。つまりループは i が11未満でなくなるまで継続します。iが11になったらループの実行が終了します。
- 加算式: 上の例では増分をi++と指定しています。つまり「iに1を足し」ます。ループはiが11になるまで、iの取りうる値について、それぞれ一度ずつ実行されます。
- 今回の例では繰り返しのコードが実行される度に iの値をconsole.log()を使用してコンソールに出力しています。
【今回】
ループを使用してその一つ一つの要素の中身を消去しています。
for (let i = 0 ; i < resetParas.length ; i++) {
resetParas[i].textContent = '';
}
初期値:「let i = 0」
条件式:0より「resetParas.length」の方が小さい
加算式:0に1を足す
このfor文の部分、見比べのソースコード 3)と違っていました。
見比べのソースコードの方は
const resetParas = document.querySelectorAll('.resultParas p');
for (const resetPara of resetParas) {
resetPara.textContent = '';
}
どちらでも動くんですが、「for...of文」は、反復可能オブジェクト。
繰り返す回数を指定しなくても良いのが特徴です。
for ( 変数 of 配列 ) {
繰り返しの処理文
}
この記載を使用して情報段落のすべてを消去出来ます。
- 「ResetPara」はtextContentを空白
- 「resultParas」はDocument.querySelectorAll()に出てきた場所を空白。
.removeChild()
削除したい要素の親要素に対して removeChild() メソッドを実行します。
親要素.removeChild(削除要素)
特定のノード(網状構造の構成要素など)を削除したい場合は次のように記述します。
削除要素.parentNode.removeChild(削除要素)
【今回】
リセットボタンを削除したいので下記になります。
resetButton.parentNode.removeChild(resetButton);
false
「disabled,true」の部分でも記載しましたが、
数値の0や空文字、nullやundefinedの時は「false」を使用します。
【今回】
新しい予想が入力できるように空にするので「false」を使用します。
guessField.disabled = false;
guessSubmit.disabled = false;
.focus()
ページが読み込まれるとすぐ、 focus()メソッドを呼び出して、主にinput要素などにフォーカスさせた時に実行できるイベント処理を記述することができます。つまり、ページが開いたと同時に、入力フォームを最初にクリックすることなくプレイヤーが予想を入力できるようにしているのです。
【今回】
guessField.focus();
オブジェクトについて(簡単に)
JavaScript の書き出しの方にあるlet resetButton;
のすぐ下に、以下の行を追記してファイルを保存します。
guessField.focus();
この行はページが読み込まれるとすぐ、 focus()メソッドを呼び出して、入力フォームの <input> 要素に対して自動的にカーソルを設定しています。
個々の部分ですね。
<div class="form">
<label for="guessField">予想を入力してください: </label>
<input type="number" min="1" max="100" required id="guessField" class="guessField">
<input type="submit" value="予想を投稿" class="guessSubmit">
</div>
こちらを加える事で、ページが開いたと同時に、入力フォームを最初にクリックすることなくプレイヤーが予想を入力できるようにしているのです。
【before】
【after】
文献表
-
MDN「JavaScriptへの最初のダイブ」
https://developer.mozilla.org/ja/docs/Learn/JavaScript/First_steps/A_first_splash
(2023/3/20閲覧) -
GitHub「mdn/learning-area」の中の「number-guessing-game-start.html」ファイル https://github.com/mdn/learning-area/blob/main/javascript/introduction-to-js-1/first-splash/number-guessing-game-start.html
(2023/3/20閲覧) -
GitHub「mdn/learning-area」の中の「number-guessing-game.html」ファイル https://github.com/mdn/learning-area/blob/main/javascript/introduction-to-js-1/first-splash/number-guessing-game.html
(2023/3/20閲覧)
Discussion