プログラミング 中部大学2025年春期

Javascriptによるプログラミング5

今回はJavascriptで簡単な電卓をつくってみる。

電卓のプログラムの構成を考える

まず123 + 456という計算をするプログラムを考えてみよう。 これをJavascriptで実行するには answer = 123 + 456 ; として、answerをdocument.writeで書き出すか、適当なHTMLフォームの値に設定すればよいことはすぐに想像できる。

次に電卓として使用するごとに変わる部分を考えてみよう。 数値は計算ごとに変わるので、これらは変数に入っていることがのぞましい。 計算対象は二つを超えることはないので(例えば 1 + 2 + 3 は 1 + 2 = 3 と 3 + 3 = 6 の2回の計算とみれば計算ごとに使う数値は二つである)、数値をいれる変数は二つあれば足りることがわかる。

演算の種類はプログラム開発を簡単にするために、今回は四則演算に限ることにする。 そうすると4種類の計算ができればよく、電卓にある + や - のボタンは演算方法を変えるためにあると考えることができる。

これらのことを考えて以下のような手順で動作する電卓をHTMLとJavascriptでつくってみる。

  1. ユーザーが一つ目の数値を入力する。 入力には10個の数値ボタンと小数点のボタンを使用する。 入力された値は一つ目の変数 first にいれる。
  2. ユーザーが演算のボタンを押す。 押されたボタンは変数 ope にいれておいて、後の演算のときに使用する関数を変更するのに使う。
  3. ユーザーが二つ目の数値を入力する。値は変数 second にいれる。
  4. ユーザーが = のボタンを押す。 first と second の値を使って ope の演算を行い、結果を変数 answer にいれる。 answer を画面に表示させる。

ボタンを配置する

まずHTMLで電卓のみかけをつくる。 電卓にあるボタンをinputタグで再現する。 これらのボタンもJavascriptの document.write で出力することもできるが、今回はレイアウトやプログラムをシンプルにすることを考え、HTMLを直接作成することにした。

<!DOCTYPE HTML>
<html lang='ja_JP'>
<head>
<style type='text/css'>
	input{
		font-size: large;
		width: 40px;
		height: 40px;
	}
</style>
<script type='text/javascript'><!--

--></script>
</head>
<body>
<table>
<tr>
	<td><input type='button' value='7'></td>
	<td><input type='button' value='8'></td>
	<td><input type='button' value='9'></td>
	<td><input type='button' value='/'></td>
</tr>
<tr>
	<td><input type='button' value='4'></td>
	<td><input type='button' value='5'></td>
	<td><input type='button' value='6'></td>
	<td><input type='button' value='*'></td>
</tr>
<tr>
	<td><input type='button' value='1'></td>
	<td><input type='button' value='2'></td>
	<td><input type='button' value='3'></td>
	<td><input type='button' value='-'></td>
</tr>
<tr>
	<td><input type='button' value='0'></td>
	<td><input type='button' value='.'></td>
	<td><input type='button' value='='></td>
	<td><input type='button' value='+'></td>
</tr>
</table>
</body>
</html>
実行結果

以後の解説では簡単にするために1行目のボタンだけ表示させる。

ボタンが押されたときの動作を設定する

ボタンが押されたときにJavascriptを動作させるために、onclick属性と対応する関数をつくる。 いきなり全部につけるのは面倒なので、とりあえず1番上のボタンに設定する。

数字のボタンが押されたときにclickNumber関数を実行し、演算のボタンが押されたときにclickFunction関数を実行するようにした。

<!DOCTYPE HTML>
<html lang='ja_JP'>
<head>
<style type='text/css'>
	input{
		font-size: large;
		width: 40px;
		height: 40px;
	}
</style>
<script type='text/javascript'><!--
	function clickNumber(){
		alert('数字が押された');
	}

	function clickFunction(){
		alert('演算のボタンが押された');
	}

--></script>
</head>
<body>
<table>
<tr>
	<td><input type='button' value='7' onclick='clickNumber()'></td>
	<td><input type='button' value='8' onclick='clickNumber()'></td>
	<td><input type='button' value='9' onclick='clickNumber()'></td>
	<td><input type='button' value='/' onclick='clickFunction()'></td>
</tr>
</table>
</body>
</html>
実行結果

押されたボタンを関数に渡す

上記の例では7から9までのボタンのいずれにもclickNumber関数を設定した。 この場合、clickNumber関数の中ではいずれのボタンが押されたのかわからない。 そこで、clickNumber関数に押されたボタンを引数として渡すようにしてみる。 具体的には onclick='clickNumber()' となっているところを onclick='clickNumber(7)' のようにしてみる。 onclick属性の値はそのまま関数を実行する形になっていることを思い出そう。

clickNumber関数の定義も変更して、引数を受け取るようにする。

<!DOCTYPE HTML>
<html lang='ja_JP'>
<head>
<style type='text/css'>
	input{
		font-size: large;
		width: 40px;
		height: 40px;
	}
</style>
<script type='text/javascript'><!--
	function clickNumber(value){
		alert(value + 'が押された');
	}

	function clickFunction(){
		alert('演算のボタンが押された');
	}

--></script>
</head>
<body>
<table>
<tr>
	<td><input type='button' value='7' onclick='clickNumber(7)'></td>
	<td><input type='button' value='8' onclick='clickNumber(8)'></td>
	<td><input type='button' value='9' onclick='clickNumber(9)'></td>
	<td><input type='button' value='/' onclick='clickFunction()'></td>
</tr>
</table>
</body>
</html>
実行結果

thisを使う

clickNumber関数に引数を渡すことによって、どのボタンが押されたのかを関数が知ることができるようになった。 ただ、このやり方には問題がある。 数値入力のためののボタンは11個もあり、それらに個別に設定しなければならないことは面倒である (プログラマーは2回以上同じことをしてはいけない)。 そもそもボタンにはボタンを区別するためにすでに数字がvalueの値として割り当てられているので、onclickにも同じ値を設定するのは無駄と考えるようになってほしい。

押されたボタンを判別するにはボタンそのもの(inputタグ)を関数に渡すようにすればよく、そのために使われるのは this というキーワードである。 clickNumber関数を呼び出しているのはユーザーが押したボタンそのもので、thisは関数を呼び出したものを指す変数として使うことができる。

thisを受け取る関数側ではこれを引数にいれる。 引数にthisを使うことができないので、ここでは button とした。 clickNumber 関数内では変数 button が関数を呼び出したHTML要素として扱うことができる。 alert に変えて console.log 関数を使うことで、button の状態をより正確にみることができる(ウェブブラウザーの開発者ツールのConsoleを見る)。

<!DOCTYPE HTML>
<html lang='ja_JP'>
<head>
<style type='text/css'>
	input{
		font-size: large;
		width: 40px;
		height: 40px;
	}
</style>
<script type='text/javascript'><!--
	function clickNumber(button){
		console.log(button);
	}

	function clickFunction(){
		alert('演算のボタンが押された');
	}

--></script>
</head>
<body>
<table>
<tr>
	<td><input type='button' value='7' onclick='clickNumber(this)'></td>
	<td><input type='button' value='8' onclick='clickNumber(this)'></td>
	<td><input type='button' value='9' onclick='clickNumber(this)'></td>
	<td><input type='button' value='/' onclick='clickFunction()'></td>
</tr>
</table>
</body>
</html>
実行結果

Consoleに <input type='button' value='7' onclick='clickNumber(this)'> のように表示されることがわかる。 これはつまり、clickNumber関数の引数buttonがinputタグそのものになっていることを意味する。

入力された数字を表示する

電卓の計算結果を表示する部分をHTMLのフォームで表現する。 これは後でJavascriptから操作できる必要があるので、id属性をつけておく。 また <input type='text'> でつくると数字をいれたくなるので、readonly属性をつけて入力できないようにした。

clickNumber関数内ではbutton変数に押されたボタンが入っている。 ボタンの表示文字列はbutton.valueをすることで取得できる。 (難しい表現をするとHTMLオブジェクトのvalueプロパティということになるが、ここでは変数に.valueをつけることでそれが指すHTML要素のvalueの値を取得できると考えておけばよい)。

表示用のフォームに押されたボタンの値を追記させる。

<!DOCTYPE HTML>
<html lang='ja_JP'>
<head>
<style type='text/css'>
	input{
		font-size: large;
		width: 40px;
		height: 40px;
	}

	input#output {
		width: 160px;
		text-align: right;
	}

</style>
<script type='text/javascript'><!--
	function clickNumber(button){
		// button.value に input タグの value 属性の値が入っている。
		// 以下のようにすると押されたボタンの値をテキストボックスにいれることができる。
		// document.getElementById('output').value = button.value;

		// 数字は上位の桁からいれていくので、前の値に追加するようにする(ここの+は文字列をつなぐ演算子であることを思い出すこと。
		document.getElementById('output').value = document.getElementById('output').value + button.value;
	}

	function clickFunction(){
		alert('演算のボタンが押された');
	}

--></script>
</head>
<body>
<table>
<tr>
	<td colspan=4><input id='output' type='text' readonly='readonly'></td>
</tr>
<tr>
	<td><input type='button' value='7' onclick='clickNumber(this)'></td>
	<td><input type='button' value='8' onclick='clickNumber(this)'></td>
	<td><input type='button' value='9' onclick='clickNumber(this)'></td>
	<td><input type='button' value='/' onclick='clickFunction()'></td>
</tr>
</table>
</body>
</html>
実行結果

演算ボタンが押されたときの動作

演算ボタンが押されたときの動作は次のようになる。

  1. 表示されている数値を変数 first にいれる。
  2. 二つ目の数値を入力するために output を空にする。
  3. 演算ボタンの種類を変数 ope にいれる。
<!DOCTYPE HTML>
<html lang='ja_JP'>
<head>
<style type='text/css'>
	input{
		font-size: large;
		width: 40px;
		height: 40px;
	}

	input#output {
		width: 160px;
		text-align: right;
	}

</style>
<script type='text/javascript'><!--
	var first, second, ope;	//	これらの変数はHTML全体で共有される。

	function clickNumber(button){
		document.getElementById('output').value = document.getElementById('output').value + button.value;
	}

	function clickFunction(button){
		first = document.getElementById('output').value;	//	変数 first は関数の外で定義済み。もしここで var first としてしまうと関数内でしか使うことができず、関数が呼び出されるごとに消えてしまうことに注意する。
		document.getElementById('output').value = '';	//	二つ目の数値入力のために空にする(長さ0の文字をいれる)。
		ope = button.value;
		console.log(first, ope);	//	変数の状態が変わっていることを確認する
	}

--></script>
</head>
<body>
<table>
<tr>
	<td colspan=4><input id='output' type='text' readonly='readonly'></td>
</tr>
<tr>
	<td><input type='button' value='7' onclick='clickNumber(this)'></td>
	<td><input type='button' value='8' onclick='clickNumber(this)'></td>
	<td><input type='button' value='9' onclick='clickNumber(this)'></td>
	<td><input type='button' value='/' onclick='clickFunction(this)'></td>
</tr>
</table>
</body>
</html>
実行結果

=ボタンが押されたときの動作

=が押されたときにもclickFunction関数が呼ばれるようにする。 他の演算ボタンと違う動作をさせるために if で分岐させる。

  1. 表示されている数値を変数 second にいれる。
  2. 変数 ope によって演算を行う。
  3. 結果を 変数 answer にいれ、画面に表示させる。
<!DOCTYPE HTML>
<html lang='ja_JP'>
<head>
<style type='text/css'>
	input{
		font-size: large;
		width: 40px;
		height: 40px;
	}

	input#output {
		width: 160px;
		text-align: right;
	}

</style>
<script type='text/javascript'><!--
	var first, second, ope;	//	これらの変数はHTML全体で共有される。

	function clickNumber(button){
		document.getElementById('output').value = document.getElementById('output').value + button.value;
	}

	function clickFunction(button){
		if('=' == button.value){
			first = parseFloat(first);	//	この時点でfirstに入っているのは文字列。計算に使えるようにするために数値に変換する。
			second = parseFloat(document.getElementById('output').value);
			var answer;
			if('+' == ope){
				answer = first + second;
			}else if('-' == ope){
				answer = first - second;
			}else if('*' == ope){
				answer = first * second;
			}else if('/' == ope){
				answer = first / second;
			}
			console.log(answer, button);
			document.getElementById('output').value = answer;
		}else{
			first = document.getElementById('output').value;
			document.getElementById('output').value = '';
			ope = button.value;
			console.log(first, ope);
		}
	}
--></script>
</head>
<body>
<table>
<tr>
	<td colspan=4><input id='output' type='text' readonly='readonly'></td>
</tr>
<tr>
	<td><input type='button' value='7' onclick='clickNumber(this)'></td>
	<td><input type='button' value='8' onclick='clickNumber(this)'></td>
	<td><input type='button' value='9' onclick='clickNumber(this)'></td>
	<td><input type='button' value='/' onclick='clickFunction(this)'></td>
</tr>
<tr>
	<td><input type='button' value='4' onclick='clickNumber(this)'></td>
	<td><input type='button' value='5' onclick='clickNumber(this)'></td>
	<td><input type='button' value='6' onclick='clickNumber(this)'></td>
	<td><input type='button' value='*' onclick='clickFunction(this)'></td>
</tr>
<tr>
	<td><input type='button' value='1' onclick='clickNumber(this)'></td>
	<td><input type='button' value='2' onclick='clickNumber(this)'></td>
	<td><input type='button' value='3' onclick='clickNumber(this)'></td>
	<td><input type='button' value='-' onclick='clickFunction(this)'></td>
</tr>
<tr>
	<td><input type='button' value='0' onclick='clickNumber(this)'></td>
	<td><input type='button' value='.' onclick='clickNumber(this)'></td>
	<td><input type='button' value='=' onclick='clickFunction(this)'></td>
	<td><input type='button' value='+' onclick='clickFunction(this)'></td>
</tr>
</table>
</body>
</html>
実行結果

課題

以下の問題を解決して電卓を完成させ、そのJavascriptを含むHTMLファイルを提出せよ。

もっと上手になりたい人へ

ここまでプログラムを書いた時点で、document.getElementById('output')だらけになっていると思います。 これを変数にいれてみようと思います。

<script type='text/javascript'><!--
	var first, second, ope;
	var ten = false, second_input = false;
	var output = document.getElementById('output');
--></script>

ところがこれではうまく動きません。 HTMLファイルは先頭から順に処理されていくので、Javascriptを実行する時点ではoutputのHTML要素が無いからです。 document.getElementById('output')を上手く働かせるためには<input id='output'>より後ろにJavascriptが出てこないといけません。 そのためにscriptタグ全体を後ろにもっていくと、今度はonclick属性に設定した関数の定義がinputタグの後に行ってしまい、inputタグを表示させるときに関数が無いという状態になります。

これの問題を解消するにはHTMLファイル全体がブラウザーに読み込まれた後にJavascriptを動かす必要があります。 そのために使うのが window.onload プロパティです。 このプロパティに設定するのは関数です。 設定された関数はHTMLファイルがブラウザーに読み込まれたあと(ロード後)に実行されます。 (ゲームを始めるときなどに Loading... と表示されることがあります。 これはプログラムをコンピュータのメモリに読み込んでいることを示しています。 ブラウザーもHTMLファイルをメモリにロードしているのです。) 以下のように使います。

// 初期化処理 (ACボタンのときにも使える)
function reset(){
	output = document.getElementById('output');
}
window.onload = reset;
--></script>

今回は演算ボタンに*とか/を使っていますが、電卓の表示としては × と ÷ を使ったほうがよいでしょう。 これらの文字はHTMLのエンティティ文字と呼ばれ、特殊な表現を使うことで表記できます。 エンティティ文字一覧 ただこれらを使うとopeにいれてそのまま使うということができなくなるので、一工夫必要となります。

<input type='button' value='&times;'>
<input type='button' value='&divide;'>
参考