javaScriptを使ってテーブルの行を追加・削除する

何かJavaScriptばかりになっているが、
とりあえず過去の作成物ソースコードを載せておくことにします。
あんま役に立たないかも。

これ作ったときは勉強したてで、色々試したいこととかも混ぜてます。
prototype.jsの存在は知ってましたが、使わず。
まずは自力でやって見るべしって感じでやりました。

任意のテーブルから行を削除するのと追加するためのメソッドです。

行の追加は、ある特定の行をコピーしてその下に同じ物を配置します。
ただし、テキストは空になり、idも異なるものにします。入れ子でも大丈夫。なはず。
行の削除は、ある特定の行を削除します。
ただし、行が1行の場合は削除しません。

出来る限り解説突っ込んでおきます。
tests.js

<!-- 名前空間がぶつからぬよう。javaっぽく -->
var com;
if(!com) {com={};};
if(!com.sample) {com.sample={};};
if(!com.sample.Class) {com.sample.Class={};};

<!-- 上記のショートカット -->
var al;
if(!al){al=com.sample.Class;};

<!-- 使い方。グローバル関数に入れ子して使うと見た目すっきり -->
<!-- テーブル行増メソッド -->
function addTableRow(target) {
	al.addTableRow(target);
}
<!-- テーブル行減メソッド -->
function remTableRow(target) {
	al.remTableRow(target);
}


<!-- テーブルに行を追加する -->
<!-- target:tagオブジェクト  -->
al.addTableRow=function(target) {
	var trtop=this.findParent(target, "tr");
	if(trtop.tagName.toLowerCase() != "tr") {
		alert("親取得に失敗しました");
		return;
	}
	var trtopParent=trtop.parentNode;
	<!--対象行のインデックスを取得し、+1-->
	var newColN=trtop.sectionRowIndex+1;
	var tr=trtopParent.insertRow(newColN);
	<!--行の全体数-->
	var torowN=trtopParent.rows.length;
	var len=trtop.cells.length;
	for(var i=0;i<len;i++) {
		var tdtop=trtop.cells[i];
		var clonechild=this.copyChild(tdtop);
		tr.appendChild(clonechild);
	}
}


<!-- 子供ノードをコピーする.入れ子は全てコピーする. -->
al.copyChild=function(tdNode) {
	var copy=tdNode.cloneNode(false);
	<!-- 属性は全てリセットする. -->
	this.resetAttributes(copy);
	<!-- tdNodeの子をコピーし、全属性をリセット. -->
	if(tdNode.hasChildNodes()) {
		var len=tdNode.childNodes.length;
		for(var i=0;i<len;i++) {
			var cchild=this.copyChild(tdNode.childNodes[i]);
			copy.appendChild(cchild);
		}
	}
	return copy;
}


<!-- 属性のリセットする. テキストは空に設定.idも一意に設定-->
al.resetAttributes=function(node) {
	if(node == null || node == undefined || node.nodeType != 1) {
		return node;
	}
	<!-- typeがテキスト以外のtagはそのまま返す.-->
	<!-- node.attributes.length < 0でないなら次の条件が実行できる.-->
	<!-- node.getAttribute("type")がnullか.つまり、定義されていないか-->
	<!-- node.typeが"text"ではないか.-->
	if(node.attributes.length < 0 || 
                        node.getAttribute("type") == null || node.type != "text") {
		<!-- idを一意に設定する.-->
		if(node.id != null && node.id != undefined && node.id !="") {
			node.id=this.changeValue(node.id);
		}
		<!-- nameを一意に設定する.-->
		if(node.name != null && node.name != undefined && node.name !="") {
			node.name=this.changeValue(node.name);
		}
		return node;
	<!--text-->
	}else {
		<!-- idを一意に設定する.-->
		if(node.id != null && node.id != undefined && node.id !="") {
			node.id=this.changeValue(node.id);
		}
		<!-- nameを一意に設定する.-->
		if(node.name != null && node.name != undefined && node.name !="") {
			node.name=this.changeValue(node.name);
		}
		node.value="";
		return node;
	}
}


<!-- idなどを一意にするためのメソッド -->
<!-- idは基本 form名:テーブル名:カラム番号:タグID といった順序 -->
al.changeValue=function(attr) {
	if(attr == null || attr == undefined) {
		return attr;
	}
	<!-- 他の方法で実装する場合はここへ -->
	
	return attr + ":" + this.createSuffix();
}


<!-- 接頭辞を作成。100%一意である時間を使うmath.randumは一意にならない. -->
al.createSuffix=function() {
	return new Date().getTime();
}


<!-- テーブルの行を削除する -->
al.remTableRow=function(target) {
	var trtop=this.findParent(target, "tr");
	<!-- テーブルに存在する行が1つなら削除不可 -->
	if(!this.searchRemainTr(trtop)) {;
		alert("can not delete any more");
		return false;
	}
	<!-- 行を削除 -->
	trtop.parentNode.deleteRow(trtop.sectionRowIndex);
	return true;
}


<!-- 指定した親がいたらそれを返す -->
<!-- target:親を捜す対象タグオブジェクト pname:親のタグ名 -->
al.findParent=function(target, pname) {
	if(target.tagName.toLowerCase() == "html" || target == top) {
 		return target;
 	}
	var parent=target.parentNode;
	if(parent.tagName.toLowerCase() != pname.toLowerCase()) {
		parent=this.findParent(parent, pname);
	}
 	return parent;
}


<!-- 行を削除可能か確認 -->
al.searchRemainTr=function(node) {
	var par=node.parentNode;
	var relen=par.childNodes.length;
	var eleCount=0;
	
	for(var i=0;i<relen;i++) {
		<!-- nodeがテキスト要素か確認。trueならカウントしない -->
		if(par.childNodes[i].nodeType == 3) {
			continue;
		}
		eleCount++;
	}
	if(eleCount == 1 ) {
		return false;
	}
	return true;
}

htmlソース

<html xmlns="http://www.w3.org/1999/xhtml" lang="ja" xml:lang="ja" >
  <head>
    <meta http-equiv="Content-Type" content="text/html; charaset=UTF-8" />
    <meta http-equiv="Content-Script-Type" content="text/javascript" />
    <link href="tests.js" rel="Javascript" type="text/javascript" media="all" />
    <script src="tests.js" type="text/javascript"></script>
    <title>sample</title>
  </head>

  <body>
    <form id="form1">
      <table border="1" id="tdata">
        <thead></thead>
        <tbody id="tbody1">
          <tr>
            <td>
	       <div>
	          <input type="text" id="sendt1" name="sendt1" value="xx" size="30"/>
	          <input type="button" value=" add " onClick="addTableRow(this);"/>
                  <input type="button" value=" rem " onClick="remTableRow(this);"/>
	       </div>
            </td>
           </tr>
           <tr>
	     <td>
                <div>
                   <input type="text" id="sendt2" name="sendt2" value="" size="30"/>
		   <input type="button" value=" add " onClick="addTableRow(this);"/>
		   <input type="button" value=" rem " onClick="remTableRow(this);"/>	
		</div>
	     </td>
           </tr>
         </tbody>
         <tfoot></tfoot>
      </table>
    </form>
  </body>
</html>

サンプルを起動するとこうなります。

fig.1 起動時

fig.2 「add」ボタン押下

fig.3 「remove」ボタン押下

fig.4 「remove」ボタン押下するも、行が1つしかないため警告表示される

テーブルに行を追加・削除するのにエラいコード書いているような気がしつつも、
そろそろPythonとかもソース書いてみようかなと思う今日この頃でした。
Erlangも。。

ちなみに最近「グラフデータベース」という言葉を知った。
ちょっと気になってます。

DOMのnodeType

DOMにはnodeTypeというものがあり、
JavaScriptでhtmlを操作する際によく使用される。
JavaScirptは見て分かるように型定義が緩いため、
時々オブジェクトに何が入っているのか分からなくなる時がある。
そんなとき、オブジェクト.nodeTypeを確認すると以下の番号が返されるので
便利である。

ELEMENT_NODE                   = 1;
ATTRIBUTE_NODE                 = 2;
TEXT_NODE                      = 3;
CDATA_SECTION_NODE             = 4;
ENTITY_REFERENCE_NODE          = 5;
ENTITY_NODE                    = 6;
PROCESSING_INSTRUCTION_NODE    = 7;
COMMENT_NODE                   = 8;
DOCUMENT_NODE                  = 9;
DOCUMENT_TYPE_NODE             = 10;
DOCUMENT_FRAGMENT_NODE         = 11;
NOTATION_NODE                  = 12;

詳細はw3cに行ってくださいませ

JavaScriptのスコープチェーンやCallオブジェクトやらの覚え書き

関数が実行されるとCallオブジェクトというオブジェクトが作成される。
ここには引数の値(Argumentプロパティ)や関数内で使用するローカル変数の値が格納される。
ローカル変数のスコープ(ある変数が有効となる範囲)はこの関数のみで、もし、この関数内に
定義されていない変数を呼び出した場合、この関数が属するグローバルオブジェクトに
変数が定義されていないか探しに行く。これをスコープチェーンという。
グローバルオブジェクトに変数が定義されていなければUndefienedとなり、探索は終了する。
Callオブジェクトは直接操作することは不可能。


Callオブジェクトは関数実行が終了すると同時にガーベッジコレクションの対象となり、
消える。が、先ほどのスコープチェーンを応用するとCallオブジェクトをガベコレの
対象から外すことができる。
これにより、特定のメソッドを通じてのみアクセスが可能な変数を定義することもでき、名前空間の衝突や
予期しないアクセスによる変数の変更ということも避けることが出来る。

ex.

	var y={};
	(function() {
 		var num=1;
 		var str="hello";
 		var getNum=function() {
 			return num;
 		};
 		var setNum=function(nnum) {
 			if(typeof nnum=="number")
	 			num=nnum
 		};
 		var getStr=function() {
 			return str;
 		};
 		var setStr=function(nstr) {
 			if(typeof nstr=="string")
	 			str=nstr;
 		};
 		y.getNum = getNum;
 		y.setNum = setNum;
 		y.getStr = getStr;
 		y.setStr = setStr;
 	})();

alert(y.getNum());//1
y.setNum(35);
alert(y.getNum());//35

ちなみに関数内の変数にthisをつけてはならない。(this.numとか)
もし、ここでthisキーワードを付けた場合、
thisキーワードが何を指すかと言えば、yオブジェクトである。

java threadの走り書きメモ

最近、PythonやらErlangをちょこちょこやっていたので、
久しぶりにjavaをいじろうと思ったものの、すごい忘れている。。
スレッドに関しても好きだったのに、忘れていてちょっとショックだった。
ちなみにPythonは数学系の人が開発し、Perlは言語系の人が開発したため、
ある処理を書くのにPythonではおおよその人が同じ書き方をするのに対し、
Perlでは人それぞれの書き方になるらしい。
Pythonは可読性が非常に良い、ということになるそうで。

閑話休題
さて、synchronizedブロックとwait,notifyのところが少々曖昧になっているため、ショットしたサンプルを作成して実験。これはそのメモ。

Runnableのrunメソッド内に以下を書く。

Runnable r = null;
while(true) {
    synchronized(queue) {
        while(queue.isEmpty()) {
	    try {
                   queue.wait();
	    }catch (InterruptedException e) {
		//ignore
            }
        }
	r = queue.removeFirst();
    }
    try { 
       r.run();
    }catch(RuntimeException e) {
       //rの処理の後始末
    }finally {
       r = null;
    }
}

簡単に言うと、キューに何か入れたらnotifyで睡眠中のスレッド達を起こして処理させる、ということをするもの。
忘れていた部分は、この辺りで、
notifyが呼ばれたら、waitのところから処理が再開されるのかsynchronizedブロックの初めに戻るのか忘れてしまい、疑問だった。
実施したところ、前者の通りになった。
となると、一見waitでロック解放しているのに、synchronizedブロック内のループ処理が作動していると思ってしまうが、System.out.print入れてチェックしたところ、裏できちんとロックを取っているようだった。

やってくると思い出してくるし、確認も出来る。

html5のWeb Workers

最近html5の参考書を購入しました。
これまでのhtmlと互換性はあるものの、劇的に進化している気が。。
単なるwebドキュメントレベルを超えて一つのプログラミング言語となったような。
今後業務系だろうがオープン系だろうが、設計レベルから根本的に変わりそう。
ちょっと怖い。

なーんとhtml5から並列処理が出来てしまうとのこと。
他にも色々とありますが、勉強したら書いていきます。
で、今回は並列処理の元である、WebWorkerについて。
・・・・・・書こうとしましたが、サンプル動かしていたら、
ブラウザによってまだ未実装部分があることが判明。
google chromeは動きました。

とりあえず動く物を。

<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<script type="text/javascript">
		var worker;
		function start() {
			if(worker) {
                                //workerの実行を停止
				worker.terminate();
			};
			var num = document.getElementById("num").value;
			var ans = document.getElementById("ans");
			worker = new Worker("webworks.js");
            //workerが処理を終えたときに実行される
			worker.onmessage = function(event) {
				ans.value = event.data;
			};
                        //workerへデータを送るためのメソッド
			worker.postMessage(num);
		}
		function stop() {
			if(worker) 
				worker.terminate();
			alert("stop");
			
		}
	</script>
</head>
<body>
number:<input type="text" id="num"> ====> <input type="text" id="ans">
<button onclick="start()">START</button>
<button onclick="stop()">STOP</button>
<br>
<textarea rows="4" cols="40" placeholder="計算中にカキコできます"></textarea>
</body>
</html>

webworks.jsファイル

//workerの処理内容
self.onmessage = function(e) {
	var data = e.data;
	var result = 0;
	var num = 10000000;
	for(var i=0;i<=num;i++) {
		result += i*data;
		
	}
        //実行元へデータを送る
	postMessage(result);
};

id=numのところに適当な数字を入れるとworkersが裏で数値計算を行います。
本来ならば、長い数値計算をやっている間、画面上の操作ができなくなるのですが、
このWorkerを用いると別スレッドで計算を行うため、画面上の操作が可能となります。
上記例だと、数値を適当に入れ、startボタンを押下後、下にあるテキストエリア内に
書き込みが出来てしまいます。
すげ。。
動かしてみたイメージ。ブラウザはgoogle chromeを使用。
半角で適当に数字を入れてstart押下。

計算結果が出るまでテキストエリアにカキコ。

カキコ中に結果が表示。


ただ書いていて混乱したことは、、
workerのハンドラ、つまり、workerが処理を終え、値を返したときに実行される関数を指定する
プロパティ名はonmessageで、
workerが処理を実行するためのプロパティ名もonmessage。
また、
workerが値を返すメソッド名はpostMessageで、
workerへ値を送信するメソッド名もpostMessage
と同じ名前となっている点である。
幸い、worker用のjavascriptは独立して書くので良かった。


ちなみに共有Workerも作成できるらしく、複数画面:Worker=多:1の関係が作れます。
ん?、workerから全画面に対してブロードキャストも出来るのか?
・・・・・・意味があるのか分からんが非常に疑問が出てきた。

まだ頭の中が整理できていないので、後ほどきちんと書きます。
今回はメモ程度。

JavaScriptのthisキーワード

javaから入った自分にとって、thisキーワードと言えばインスタンスを指すものだ。
まあ、言語によって何を指すかは変わってくるだろうが、javascriptでもthisがあると分かった。
しかもjavascriptでは実行時によってthisのさすものが異なるという非常にやっかいなものだ。
なので私は混乱した場合、この関数(メソッド)を実行しているのは何のオブジェクトかという風に考える。
例えば、

var a = "global val a";
var ob = {
	"a":"local val a"
};
ob.func1 = function() {
	alert(this.a);
}
ob.func2 = function() {
	return function() {
	       alert(this.a);
	       }
}
ob.func1();
ob.func2()();

このとき
ob.func1()の場合、thisキーワードはobオブジェクトを指すため、結果として"local val a"を返す。
ob.func2()()の場合、のthisキーワードはグローバルオブジェクトを指すため、結果として"global val a"を返す。
例えが悪いかもしれないが、このようにthisでも使うところによって指している対象物が異なってくるから
ソースをきちんと読む必要がある。



ちなみにjavascriptでは関数=オブジェクトであり、オブジェクト内で関数が定義されるとメソッドという名に変わる。
関数もオブジェクトであるため、関数にプロパティを作成することも出来るし、関数にメソッドを作成するという事も出来る。
まあ、関数が出来たらprototypeプロパティは自動的に作成されるのだが。

白い嵐

白い嵐という映画を見た。
久しぶりに良い映画に出会った。
感動して泣いてしまった。
男同士の友情とかすごいかっこいい。
こういう出会いって本当、素敵で正直うらやましいとさえ思ってしまった。
出会いって偶然なのだろうけど、自らどんどん道を進んでいけば
そういった仲間のような人たちと出会えることは可能なのだろうかと
妙にしんみりと考えてしまった。
この映画は実話なので、私もどんどん自分の人生を攻めて行けたら
いいな、と思いました。
会社がこういう感じだったらきっと毎日が凄いんだろうな。

いやー、本当久々に泣きました。

白い嵐 アドバンスト・コレクターズ・エディション [DVD]

白い嵐 アドバンスト・コレクターズ・エディション [DVD]