数学用ソフトウェアキーボードをJavaScriptで作ってみた

#分数の入力ができない
　算数のWeb教材を作成したとき、整数と小数はキーボードから入力できるが、「分数は入力できない」という問題にぶつかりました。このような場合には、「選択肢を用意する」という方法で対処を行うのが普通です。でも、小学生のプリント問題で、分数でしか回答できない場合に「選択肢から選ぶ」というのは、あまりに不自然な感じがします。そこで「分数を入力する」ために、数学用ソフトウェアキーボードをJavaScriptで作ってみました。
　分数に加えて、「ルート √　累乗 2<sup>3</sup>　パイ π」も入力できるようにして、中学校数学のWeb教材に対応するソフトウェアキーボードの作成を目標としました。

#サンプルサイト
　今回のサンプルサイトはこちらです。
http://iwate-manabi-net.sakura.ne.jp/math_software_keyboard/

　ソフトウェアキーボードを利用した教材サンプルはこちらです。
http://www1.iwate-ed.jp/tantou/joho/material/g-tablet-m/index.html

#MathJax を使って数式を表示
　数式の表示にはMathJaxをJavaScriptライブラリとして利用しました。
https://www.mathjax.org/
　最新版のMathJax3が公開になっていますが、今回は古いバージョンを使用します。WARNINGが表示されますが、気にせず使っていきましょう。
　使い方についてはこちらのページを参照願います。
http://www.eng.niigata-u.ac.jp/~nomoto/download/mathjax.pdf

#HTMLソース

```html:index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1">
<script type="text/x-mathjax-config">
  MathJax.Hub.Config({ tex2jax: { inlineMath: [['$','$'], ["\\(","\\)"]] } });
</script>
<script type="text/javascript"
   src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML">
</script>
<script src="js/jquery.min.js"></script>
<script src="js/jquery-ui.min.js"></script>
<script src="js/jquery.ui.touch-punch.js"></script>
<script src="math_software_keyboard.js"></script>
<link rel="stylesheet" href="math_software_keyboard.css">
<title>数学用ソフトウェアキーボード</title>
</head>
<body>
<h1>数学用ソフトウェアキーボード</h1><br>
<div id="setumei">
１　次の計算をしなさい。<br>
　　　　※　例：　<span class='siki'>\(2\sqrt{3}\)</span>　の入力→　「２」「ルート」「３」
</div><br>
<div style='float:left'>
(1)　　<span class='siki'>\(3\sqrt{5}+6\sqrt{5}\)</span><br>
　　　<span id='buttonShow1' class='buttonShowClass'>クリックして答えを入力</span>
答　　<span id='kotae1' class='siki'><font size='3'></font></span>
　　<span id='maru1' class='maru'>○</span>
</div>
　　　　<div id='kaisetu1' class='kaisetu'></div>
<div style='clear: both'></div>
<div id='inputButton1'></div><br><br>
</body>
</html>
```

#CSSソース

```css:math_software_keyboard.css
/* math_software_keyboard.css */
#setumei{
	font-weight: bold;
	color: #060;
}

.buttonShowClass{
	background-color: #cff;
	padding: 10px 10px 10px 10px;
	border-radius:4px;
	border:1px solid #bbd;
	box-shadow:2px 2px 2px 2px rgba(200, 200, 200, 0.4);
	margin: 0px 5px;
	cursor:pointer;
}

.waku{
	user-select: none; /* CSS3 単語を選択させない */
	-moz-user-select: none; /* Firefox */
	-webkit-user-select: none; /* Safari、Chromeなど */
	-ms-user-select: none; /* IE10かららしい */
	-webkit-tap-highlight-color:#fff;
	background-color: #eef;
	padding: 10px 5px 5px 5px;
	border-radius:4px;
	border:1px solid #888;
	box-shadow:2px 2px 2px 2px rgba(200, 200, 200, 0.4);
	margin: 0px 5px;
	width:800px;
}

.bs{
	user-select: none; /* CSS3 単語を選択させない */
	-moz-user-select: none; /* Firefox */
	-webkit-user-select: none; /* Safari、Chromeなど */
	-ms-user-select: none; /* IE10かららしい */
	-webkit-tap-highlight-color:#fff;
	background-color: #ccf;
	padding: 10px 10px 10px 10px;
	border-radius:4px;
	border:1px solid #999;
	box-shadow:2px 2px 2px 2px rgba(200, 200, 200, 0.6);
	margin: 0px 5px;
	cursor:pointer;
	font-weight:bold;
}

.bc{
	user-select: none; /* CSS3 単語を選択させない */
	-moz-user-select: none; /* Firefox */
	-webkit-user-select: none; /* Safari、Chromeなど */
	-ms-user-select: none; /* IE10かららしい */
	-webkit-tap-highlight-color:#fff;
	background-color: #ffa;
	padding: 10px 5px 10px 5px;
	border-radius:4px;
	border:1px solid #aaa;
	box-shadow:2px 2px 2px 2px rgba(200, 200, 200, 0.6);
	margin: 0px 5px;
	cursor:pointer;
}
.maru{
	user-select: none; /* CSS3 単語を選択させない */
	-moz-user-select: none; /* Firefox */
	-webkit-user-select: none; /* Safari、Chromeなど */
	-ms-user-select: none; /* IE10かららしい */
	-webkit-tap-highlight-color:#fff;
	vertical-align: middle;
	color: red;
	opacity:0;
	font-size: 500%;
}
.siki{
	font-size: 130%;
}

.kaisetu{
	user-select: none; /* CSS3 単語を選択させない */
	-moz-user-select: none; /* Firefox */
	-webkit-user-select: none; /* Safari、Chromeなど */
	-ms-user-select: none; /* IE10かららしい */
	-webkit-tap-highlight-color:#fff;
	color: blue;
	font-size: 120%;
}
```

#JavaScriptソース

```js:math_software_keyboard.js
var mondaiMax= 1;  // 問題数
var kaitouNumber=0;
var kaitouText = new Array();
var buttonText = new Array();
for(var i=0; i<= mondaiMax ; i++){           //問題数
	buttonText[i] = [];
	for(var j=0; j<20; j++){
		buttonText[i][j] = '';
	}
}
var buttonCheckText ="";
var buttonMax  = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]; // 初期値 0
var kaisetuText = "";

//    入力用のボタン表示
var bData ="";
	bData = bData + "<div id='inputNow' class='waku'><br>";
	//bData = bData + "<br>";
	bData = bData + "<span id='bbb' class='bs'>分母入力</span><span id='bbe' class='bs'>分数おわり</span>";
	bData = bData + "<span id='brs' class='bs'>ルート</span><span id='bre' class='bs'>ルートおわり</span>";
	bData = bData + "<span id='bpi' class='bs'>π</span><span id='bpw' class='bs'>累乗</span>";
	bData = bData + "<span id='bpm' class='bs'>±</span>";
	bData = bData + "　<span id='bal' class='bc'>すべて消す</span><br><br><br>";
	bData = bData + "<span id='bt' class='bs'>＋</span><span id='bh' class='bs'>－</span><span id='bk' class='bs'>×</span>";
	bData = bData + "<span id='bw' class='bs'>÷</span><span id='bi' class='bs'>＝</span>";
	bData = bData + "<span id='bks' class='bs'>（</span><span id='bke' class='bs'>）</span>";
	bData = bData + "<span id='ma' class='bs'>ａ</span><span id='mb' class='bs'>ｂ</span><span id='mc' class='bs'>ｃ</span>";
	// bData = bData + "<span id='md' class='bs'>d</span>";
	bData = bData + "<span id='mx' class='bs'>χ</span><span id='my' class='bs'>ｙ</span><span id='mz' class='bs'>ｚ</span>";
	bData = bData + "<span id='bba' class='bc'>←１つもどる</span><br><br><br>";
	bData = bData + "<span id='b1' class='bs'>１</span><span id='b2' class='bs'>２</span><span id='b3' class='bs'>３</span>";
	bData = bData + "<span id='b4' class='bs'>４</span><span id='b5' class='bs'>５</span><span id='b6' class='bs'>６</span>";
	bData = bData + "<span id='b7' class='bs'>７</span><span id='b8' class='bs'>８</span><span id='b9' class='bs'>９</span>";
	bData = bData + "<span id='b0' class='bs'>０</span><span id='bd' class='bs'>．</span>";
	bData = bData + "<span id='bhide' class='bc'>閉じる</span>　<span id='bcheck' class='bc'>解答する</span><br><br><br>";
	bData = bData + "</div>";

$(function() {  // jQuery を実行	
	$('#inputButton1').hide();  // キーボードの表示を消しておく
	
	$('#buttonShow1').on('click', function () {
		funcButtonHide();
		kaitouNumber = 1;                              // 問題番号
		$('#inputButton' +kaitouNumber).html(bData);   // キーボードの表示
		$('#inputButton' +kaitouNumber).show('normal');// アニメーション表示
		buttonReload();                                // ボタンを作動させる
		$('#buttonShow' +kaitouNumber).hide();         //「クリックして答えを入力」ボタンを隠す
	});

	function funcKaitou(){
		switch (kaitouNumber){
			case 1:  //問題番号1 の場合
				// 計算式の表示
				kaisetuText =                   '　\\(3\\sqrt{5}+6\\sqrt{5}\\)　';
				kaisetuText = kaisetuText+      '=\\((3+6)\\sqrt{5}\\)　';
				kaisetuText = kaisetuText+      '=\\(9\\sqrt{5}\\)<br>';
				$('#kaisetu' + kaitouNumber).html(kaisetuText);
				// 答え合わせ
				if((kaitouText[kaitouNumber] == '\\({9}\\sqrt{5}\\)')  //9ルート5
				 ||(kaitouText[kaitouNumber] == '\\({9}\\sqrt{5}\\)\\({}\\)')){ //9ルート5 ルートおわり
					$('#maru' + kaitouNumber ).css('opacity',1);  //正解の場合
				}else{
					funcBatu();  //不正解の場合
				}
				break;
		};
		MathJax.Hub.Queue(["Typeset",MathJax.Hub,('kaisetu' + kaitouNumber)]);  //数式を表示
		$('#buttonShow'+ kaitouNumber ).hide();         //キーボード表示ボタンを消します
		$('#inputNow').remove();
		$('#inputNow').html("<div id='inputButton" + kaitouNumber + "'></div>");
		$('#inputButton' + kaitouNumber).hide();
		$(('#buttonShow'+ kaitouNumber)).remove();
		
	};
	
	function funcBatu(){       //  不正解の場合、バッテンを表示
		$('#maru' + kaitouNumber).text('×');
		$('#maru' + kaitouNumber).css('opacity',1);
		$('#maru' + kaitouNumber).css('color','blue');
	};
	
	function funcButtonHide() {         //キーボードを消す
		$('#inputNow').remove();
		$('#inputNow').html("<div id='inputButton" + kaitouNumber + "'></div>");
		$('#inputButton' + kaitouNumber).hide();
		$(('#buttonShow'+ kaitouNumber)).show();
	}

	function buttonReload(){ // 	数学用ソフトウェアキーボードが押されたとき
		$('#b1' ).on('click', function () { buttonText[kaitouNumber][buttonMax[kaitouNumber]]="1"; buttonMax[kaitouNumber]++ ; display(); });
		$('#b2' ).on('click', function () { buttonText[kaitouNumber][buttonMax[kaitouNumber]]="2"; buttonMax[kaitouNumber]++ ; display(); });
		$('#b3' ).on('click', function () { buttonText[kaitouNumber][buttonMax[kaitouNumber]]="3"; buttonMax[kaitouNumber]++ ; display(); });
		$('#b4' ).on('click', function () { buttonText[kaitouNumber][buttonMax[kaitouNumber]]="4"; buttonMax[kaitouNumber]++ ; display(); });
		$('#b5' ).on('click', function () { buttonText[kaitouNumber][buttonMax[kaitouNumber]]="5"; buttonMax[kaitouNumber]++ ; display(); });
		$('#b6' ).on('click', function () { buttonText[kaitouNumber][buttonMax[kaitouNumber]]="6"; buttonMax[kaitouNumber]++ ; display(); });
		$('#b7' ).on('click', function () { buttonText[kaitouNumber][buttonMax[kaitouNumber]]="7"; buttonMax[kaitouNumber]++ ; display(); });
		$('#b8' ).on('click', function () { buttonText[kaitouNumber][buttonMax[kaitouNumber]]="8"; buttonMax[kaitouNumber]++ ; display(); });
		$('#b9' ).on('click', function () { buttonText[kaitouNumber][buttonMax[kaitouNumber]]="9"; buttonMax[kaitouNumber]++ ; display(); });
		$('#b0' ).on('click', function () { buttonText[kaitouNumber][buttonMax[kaitouNumber]]="0"; buttonMax[kaitouNumber]++ ; display(); });
		$('#bd' ).on('click', function () { buttonText[kaitouNumber][buttonMax[kaitouNumber]]="."; buttonMax[kaitouNumber]++ ; display(); });
		$('#ma' ).on('click', function () { buttonText[kaitouNumber][buttonMax[kaitouNumber]]="a"; buttonMax[kaitouNumber]++ ; display(); });
		$('#mb' ).on('click', function () { buttonText[kaitouNumber][buttonMax[kaitouNumber]]="b"; buttonMax[kaitouNumber]++ ; display(); });
		$('#mc' ).on('click', function () { buttonText[kaitouNumber][buttonMax[kaitouNumber]]="c"; buttonMax[kaitouNumber]++ ; display(); });
		$('#md' ).on('click', function () { buttonText[kaitouNumber][buttonMax[kaitouNumber]]="d"; buttonMax[kaitouNumber]++ ; display(); });
		$('#mx' ).on('click', function () { buttonText[kaitouNumber][buttonMax[kaitouNumber]]="x"; buttonMax[kaitouNumber]++ ; display(); });
		$('#my' ).on('click', function () { buttonText[kaitouNumber][buttonMax[kaitouNumber]]="y"; buttonMax[kaitouNumber]++ ; display(); });
		$('#mz' ).on('click', function () { buttonText[kaitouNumber][buttonMax[kaitouNumber]]="z"; buttonMax[kaitouNumber]++ ; display(); });
		$('#bt' ).on('click', function () { buttonText[kaitouNumber][buttonMax[kaitouNumber]]="＋"; buttonMax[kaitouNumber]++ ; display(); });
		$('#bh' ).on('click', function () { buttonText[kaitouNumber][buttonMax[kaitouNumber]]="－"; buttonMax[kaitouNumber]++ ; display(); });
		$('#bk' ).on('click', function () { buttonText[kaitouNumber][buttonMax[kaitouNumber]]="×"; buttonMax[kaitouNumber]++ ; display(); });
		$('#bw' ).on('click', function () { buttonText[kaitouNumber][buttonMax[kaitouNumber]]="÷"; buttonMax[kaitouNumber]++ ; display(); });
		$('#bi' ).on('click', function () { buttonText[kaitouNumber][buttonMax[kaitouNumber]]="＝"; buttonMax[kaitouNumber]++ ; display(); });
		$('#bks').on('click', function () { buttonText[kaitouNumber][buttonMax[kaitouNumber]]="（"; buttonMax[kaitouNumber]++ ; display(); });
		$('#bke').on('click', function () { buttonText[kaitouNumber][buttonMax[kaitouNumber]]="）"; buttonMax[kaitouNumber]++ ; display(); });
		$('#bpi').on('click', function () { buttonText[kaitouNumber][buttonMax[kaitouNumber]]="π"; buttonMax[kaitouNumber]++ ; display(); });
		$('#bpm').on('click', function () { buttonText[kaitouNumber][buttonMax[kaitouNumber]]="±"; buttonMax[kaitouNumber]++ ; display(); });
		$('#bbb').on('click', function () { buttonText[kaitouNumber][buttonMax[kaitouNumber]]="分母入力"; buttonMax[kaitouNumber]++ ; display(); });
		$('#bbe').on('click', function () { buttonText[kaitouNumber][buttonMax[kaitouNumber]]="分数おわり"; buttonMax[kaitouNumber]++ ; display(); });
		$('#brs').on('click', function () { buttonText[kaitouNumber][buttonMax[kaitouNumber]]="ルートはじめ"; buttonMax[kaitouNumber]++ ; display(); });
		$('#bre').on('click', function () { buttonText[kaitouNumber][buttonMax[kaitouNumber]]="ルートおわり"; buttonMax[kaitouNumber]++ ; display(); });
		$('#bpw').on('click', function () { buttonText[kaitouNumber][buttonMax[kaitouNumber]]="累乗"; buttonMax[kaitouNumber]++ ; display(); });
		$('#bba').on('click', function () { buttonMax[kaitouNumber]-- ; if( buttonMax[kaitouNumber] <0 ){ buttonMax[kaitouNumber] =0 }display(); });
		$('#bal').on('click', function () { buttonMax[kaitouNumber] =0; display(); });
		$('#bhide').on('click', function () {funcButtonHide();});
		$('#bcheck').on('click', function () {funcKaitou();});
	};
	buttonReload();    //  ボタンを使えるようにする
	
	function display() {
		buttonCheckText ="";
		var myText="\\({";           //\を表示するには２回\\
		for ( var k=0; k <buttonMax[kaitouNumber] ;k++){
			switch (buttonText[kaitouNumber][k]){
				case "1": myText = myText + "1" ;break;
				case "2": myText = myText + "2" ;break;
				case "3": myText = myText + "3" ;break;
				case "4": myText = myText + "4" ;break;
				case "5": myText = myText + "5" ;break;
				case "6": myText = myText + "6" ;break;
				case "7": myText = myText + "7" ;break;
				case "8": myText = myText + "8" ;break;
				case "9": myText = myText + "9" ;break;
				case "0": myText = myText + "0" ;break;
				case ".": myText = myText + "." ;break;
				case "a": myText = myText + "a" ;break;
				case "b": myText = myText + "b" ;break;
				case "c": myText = myText + "c" ;break;
				case "d": myText = myText + "d" ;break;
				case "x": myText = myText + "x" ;break;
				case "y": myText = myText + "y" ;break;
				case "z": myText = myText + "z" ;break;
				case "＋": myText = myText + "+" ;break;
				case "－": myText = myText + "-" ;break;
				case "×": myText = myText + "×" ;break;
				case "÷": myText = myText + "÷" ;break;
				case "＝": myText = myText + "=" ;break;
				case "（": myText = myText + "(" ;break;
				case "）": myText = myText + ")" ;break;
				case "π": myText = myText + "\\pi" ;break;
				case "±": myText = myText + "\\pm" ;break;
				case "分母入力": myText = myText + "}\\over{" ;break;
				case "分数おわり": myText = myText + "}\\)\\({" ;break;
				case "ルートはじめ": myText = myText + "}\\sqrt{" ;break;
				case "ルートおわり": myText = myText + "}\\)\\({" ;break;
				case "累乗": myText = myText + "\^" ;break;
			};
			buttonCheckText = buttonCheckText +buttonText[kaitouNumber][k];
		};
		if(buttonMax[kaitouNumber] > 0){
			if(buttonText[kaitouNumber][buttonMax[kaitouNumber] -1] == "累乗"){    //累乗入力の時に表示が変になるを防ぐ
				myText = myText + "□";
			};
		};
		myText = myText + "}\\)";
		//MathJax表示
		$(("#kotae" + kaitouNumber)).html(myText);
		MathJax.Hub.Queue(["Typeset",MathJax.Hub,("kotae" + kaitouNumber)]);
		kaitouText[kaitouNumber]= myText;
	};
	 $('body').bind('updated',function(){display()});
});
```

非表示にしたいボタン（キー）があるときには、
// 入力用のボタン表示
の下の部分で
 // bData = bData + ＜span id='brs' class='bs'＞ルート・・・
　↑このように//を使ってコメントアウトすると、「ルート」ボタンが非表示になります。