ブラウザーで表計算を作りたい？

#ブラウザーでも表計算みたいに入力したい
…とかいう要望が出ることがありますね。
やってみましょう。
テキストボックスをたくさん並べて…、じゃないですよ。

```html:worksheet.html
<style>
	*{
		word-break:break-all;
		word-wrap:break-word;
		overflow-wrap:break-word;
		box-sizing:border-box;
	}
	#worksheet{width:100%;border-collapse:collapse;}
	#worksheet th{background-color:gainsboro;border-right:solid 1px gray;border-bottom:solid 1px gray;}
	#worksheet thead tr th{}
	#tbody tr th{width:64px;}
	#tbody tr td{border:solid 1px gray;font-size:16px;font-family:sans-serif;white-space:pre;}
	#textarea{position:absolute;background-color:lemonchiffon;font-size:16px;font-family:sans-serif;}
</style>

<table id="worksheet">
	<thead id="thead">
		<tr>
			<th></th>
			<th>A</th>
			<th>B</th>
			<th>C</th>
		</tr>
	</thead>
	<tbody id="tbody">
		<tr>
			<th>1</th>
			<td></td>
			<td></td>
			<td></td>
		</tr>
		<tr>
			<th>2</th>
			<td></td>
			<td></td>
			<td></td>
		</tr>
		<tr>
			<th>3</th>
			<td></td>
			<td></td>
			<td></td>
		</tr>
		<tr>
			<th>4</th>
			<td></td>
			<td></td>
			<td></td>
		</tr>
	</tbody>
<table>
<textarea id="textarea"></textarea>
```
テキストボックスはひとつだけです。入力する位置へ動かすのです。
表は、動的に生成しても良いですね。

#機能部分
これで各セルに入力できるようになります。
あとは、ファイルに保存とか、データベースから…とか、計算式を…とか、好きにやってください。
カラム幅の指定とか、行固定のスクロールとか、凝り始めたらキリがないですね。
行の追加・削除くらいは欲しいかな。

```javascript:worksheet.js
	var	pos;
	var	textarea;
	var	table;
	var	thead;
	var	tr;
	var	tbody;

	window.addEventListener('DOMContentLoaded',function(event)
	{
		table=document.getElementById('worksheet');
		thead=document.getElementById('thead');

		// セルサイズの固定化
		tr=document.getElementById('thead').children[0];
		for(i=0;i<tr.children.length;i++){
			tr.children[i].style.width=tr.children[i].offsetWidth+'px';
			tr.children[i].style.height=tr.children[i].offsetHeight+'px';
		}

		// 編集位置の初期設定
		tbody=document.getElementById('tbody');
		td=tbody.children[0].children[1];
		textarea=document.getElementById('textarea');
		pos=setpos(textarea,td,table);

		// クリックした位置に移動
		table.addEventListener('click',
			function(event){
				pos=setpos(textarea,event.srcElement,table);
				td=event.srcElement;
			},
		false);

		// リサイズに合わせてリサイズ
		window.addEventListener('resize',
			function(event){
				pos=setpos(textarea,pos['td'],table);
			},
		false);

		// 矢印キーで移動
		document.addEventListener('keydown',onkeydown,false);
	},false);

	function setpos(textarea,td,table)
	{
		if(textarea && pos){
			if(pos['td']){
				pos['td'].innerHTML=textarea.value;
			}
		}
		if(td && table){
			pos={
				"top"	:td.offsetTop+table.offsetTop+1,
				"left"	:td.offsetLeft+table.offsetLeft+1,
				"width"	:td.offsetWidth-1,
				"height":td.offsetHeight-1,
				"col"	:td.cellIndex,
				"row"	:td.parentNode.rowIndex,
				"td"	:td	};
			if(navigator.userAgent.match(/Firefox/)){
				pos.top-=2;
				pos.left-=1;
			}
		}
		if(textarea){
			if(td){
				textarea.value=td.innerHTML;

				textarea.style.top		=pos['top']+"px";
				textarea.style.left		=pos['left']+"px";
				textarea.style.width	=pos['width']+"px";
				textarea.style.height	=pos['height']+"px";
			}
		}
		return pos;
	}

	function onkeydown(event)
	{
		if(event.target==textarea){
			switch(event.keyCode){
			case 10:	// return
				break;
			case 13:	// enter
				if(event.ctrlKey
				|| event.shiftKey
				|| event.altKey){		// 改行を入力させる
					break;
				}else{
					textarea.blur();
				}
				break;
			}
		}else{
			switch(event.keyCode){
			case 38:
			case 40:
			case 37:
			case 39:
				if(event.keyCode==38){			// ↑
					pos['row']--;
					if(pos['row']<1){
						pos['row']=1;
					}
				}else if(event.keyCode==40){	// ↓
					pos['row']++;
					if(tbody.children.length<pos['row']){
						pos['row']=tbody.children.length;
					}
				}else if(event.keyCode==37){	// ←
					pos['col']--;
					if(pos['col']<=0){
						pos['col']=1;
					}
				}else if(event.keyCode==39){	// →
					pos['col']++
					if(tr.children.length<=pos['col']){
						pos['col']=tr.children.length-1;
					}
				}
				newtd=tbody.children[ pos['row']-1 ].children[ pos['col'] ];
				pos=setpos(textarea,newtd,table);
				break;
			case 10:	// return
				break;
			case 13:	// enter
				textarea.focus();
				event.preventDefault();			// 改行を入力させない
				event.returnValue = false;		// ie
			default:
				break;
			}
		}
	}
```