これまで七回に渡って連載を続けてきました。
今回が最終回です。
ここまでが、私の知る JavaScript 開発の手法変化の歴史です。
最近では多くのフレームワークやライブラリが登場し、最近ではこうしたライブラリを利用した開発が一般的になりつつあります。
今回は、ここまでの手法を元にフレームワークを完成させたいと思います。
*
フレームワークといっても今回作るのはごく小規模なものです。
大規模に利用する場合にはここにさまざまな機能を付け加えていく必要がありますが、ここまででも必要最低限の機能を備え、HTML を効率よく構築することに特化したフレームワークとなっています。
前回からの変更点は以下の通り
・クラスの継承を利用
・x-id や x-onclick などの特殊属性を追加
によってさらにコード視認性の向上を図っています。
完成例
var HTMLParts = function(param){
this.param = param;
if(!param.id){
this.id = HTMLParts.lastId;
HTMLParts.lastId++;
}else{
this.id = param.id
}
HTMLParts.instanceMap[this.id] = this;
this.render();
}
HTMLParts.lastId = 0;
HTMLParts.instanceMap = new Object;
HTMLParts.extend = function(subc, superc) {
if(!superc) superc = HTMLParts;
var F = function() {};
F.prototype=superc.prototype;
subc.prototype=new F();
subc.prototype.constructor=subc;
subc.superclass=superc.prototype;
if (superc.prototype.constructor == Object.prototype.constructor) {
superc.prototype.constructor=superc;
}
}
HTMLParts.prototype.ref = function(id){
return document.getElementById("HTMLParts."+this.id+(id ? "." + id : ""));
}
HTMLParts.prototype.bindId = function(id){
return " id=\"HTMLParts."+this.id+(id ? "." + id : "")+"\" ";
}
HTMLParts.prototype.bindEvent = function(type, methodName){
if(!methodName) methodName = type;
return " on"+type+"=\"HTMLParts.instanceMap['"+this.id+"'].on"+methodName+"(event ? event : window.event)\" ";
}
HTMLParts.prototype.getHTML = function(){
thisImpl = this;
return this.createHTML()
.replace( /\$\{([a-zA-Z0-9]+)\}/g,
function($0,$1){ return thisImpl.param[$1]; })
.replace( /x-id(=["']?([a-zA-Z0-9]+)['"]?)?/g,
function($0,$1,$2){ return thisImpl.bindId($2); })
.replace( /x-on([a-zA-Z0-9]+)(=["']?([a-zA-Z0-9]+)['"]?)?/g,
function($0,$1,$2,$3){ return thisImpl.bindEvent($1,$3); })
;
}
HTMLParts.prototype.createHTML = function(){
return "";
}
HTMLParts.prototype.render = function(){
document.write(this.getHTML());
}
ここまでがフレームワーク部分です。
//
// HTMLParts.input 部品
//
HTMLParts.input = function(param){
HTMLParts.input.superclass.constructor.call(this, param);
}
HTMLParts.extend(HTMLParts.input, HTMLParts);
HTMLParts.input.prototype.createHTML = function(){
return '<div style="color:gray;position:absolute;z-index:2;font-size:9pt;padding:6px;" x-id="desc" x-onclick>${description}</div><input name="${name}" type="text" x-id x-onkeydown x-onfocus x-onblur value="" />';
}
HTMLParts.input.prototype.onclick = function(e){
this.ref().focus();
this.onfocus();
}
HTMLParts.input.prototype.onkeydown = function(e){
if(this.ref().value == ""){
this.ref("desc").style.display="none";
}
}
HTMLParts.input.prototype.onfocus = function(e){
if(this.ref().className.indexOf("focused")>=0){
this.ref().className = this.ref().className.replace("focused","");
return;
}
if(this.ref().value == ""){
this.ref("desc").style.display="none";
}
}
HTMLParts.input.prototype.onblur = function(e){
if(this.ref().value == "" ){
this.ref("desc").style.display="";
}
}
これによって HTMLParts.input クラスとして実装がまとまりました。
new HTMLParts.input({
name : "q",
description : "ここで検索!<img src='new.gif'>"
});
いかがでしょうか。
上記はプログラムの分離を行いつつ、高速に動作します。
これでやっと胸を張ってプログラムを提出できるというものです。
*
この短い記事ではよくある万能ライブラリほどの機能を積み込むことは行いませんでしたが、JavaScript 部品開発の際に特に問題になりがちな初回表示速度についてはこうした手法でも改善が見込めそうです。確かに、普段ここまでやることは少ないかもしれません。ことわざでも「蚊を殺すのに大砲を使うな」とあるように状況に応じて最適な選択が出来て初めてフレームワークがいきてくると思います。
灰色の文字入り検索ボックスの開発記事としては随分と遠回りになってしまいましたが、今回の記事で終わりとしたいと思います。
この連載では、プログラム開発の流れや中身を解体して見せることを通じて、その不思議さ、面白さ、機能の裏側にあるアイデアや工夫をお見せできればと思っていました。それは例えば動作速度へのこだわりであり、それはプログラム管理への執念であったと思っています。
七回に渡り、お付き合いいただきありがとうございました。
満足いただけたなら幸いです。
それではまたの機会にお会いしましょう。
(Yahoo!メール開発チーム 田淵純一)
これまでの記事>>第一回へ
>>第二回へ
>>第三回へ
>>第四回へ
>>第五回へ
>>第六回へ
こちらの記事のご感想を聞かせください。
- 学びがある
- わかりやすい
- 新しい視点
ご感想ありがとうございました