JavaScriptをまともに勉強したのは初めてかもしれん。とりあえず prototype.js はいろいろうんざりさせられた過去があるので jQuery 使ってる。
継承てどう実装するんだろうと思っていろいろ調べてた。JavaScriptだと日本語の情報が結構あるなー。
最初はObject.prototypeを汚染してしまってて、途中で困ったので結局こんな感じで落ち着いた。細かいところは気にしない。
core.js
var Class = new function(){}; Class.extend = function(target, base) { for (var i = 1 ; i < arguments.length ; i++) { var cls = arguments[i] for (property in cls) { if(cls.hasOwnProperty(property)){ target[property] = cls[property]; } }; }; };
最初からjQueryですべて完結するのもいいけれど、まずは基礎から。
早速、アイテムの表示と編集をするためのコンポーネントが必要だったので、割と汎用なものを作った。
listeditor.js
var ListEditor = function(prefix) { this.prefix = prefix; this.items = []; this.is_edit = false; }; Class.extend(ListEditor.prototype, { getTemplate : function(name) { if (name == 'edit') { return '<ul>{for item in items}<li>${item.name} <a href="javascript:void(0);" class="${prefix}_remove_item" id="${prefix}_remove_item_${item_index}">x</a></li>{/for}</ul><form method="post" action="" id="${prefix}_add_form"><div><input type="text" name="name" /> <button type="submit" id="${prefix}_add_item">add</button></div></form><div><a href="javascript:void(0);" id="${prefix}_cancel">cancel</a></div>'; } else { return '<p>{for item in items}${item.name} {/for}</p><div><a href="javascript:void(0);" id="${prefix}_edit">edit</a></div>'; } }, getTarget : function() { return $('#'+this.prefix); }, render : function() { var context = {items : this.items, prefix : this.prefix}; var template_name = ''; if (!this.is_edit) { } else { template_name = 'edit'; } var template = TrimPath.parseTemplate(this.getTemplate(template_name)); this.getTarget().html(template.process(context)); this.bind(); }, removeItem : function(index) { this.items.splice(index, 1); }, addItem : function(item) { this.items.push(item); }, findItem : function(name) { for (var i = 0; i < this.items.length; i++) { if (this.items[i].name == name) { return i; } } return (-1); }, showEditor : function() { this.is_edit = true; this.render(); this.setFocus(); }, hideEditor : function() { this.is_edit = false; this.render(); }, setFocus : function() { $('#' + this.prefix + '_add_form').find('input[name=name]').focus(); }, // actions handleEditClick : function(elem) { this.showEditor(); }, handleCancelClick : function(elem) { this.hideEditor(); }, handleremoveClick : function(elem) { this.removeItem(elem.id.replace(this.prefix + '_remove_item_', '')); this.render(); }, handleAddClick : function(elem) { var val_name = $('#' + this.prefix + '_add_form').find('input[name=name]').val(); if (val_name != '') { var item = { name : val_name }; this.addItem(item); this.render(); } }, // bind actions bind : function() { var th = this; if (this.is_edit) { $('#' + this.prefix + '_cancel').bind('click', function() { th.handleCancelClick(this); }); $('.' + this.prefix + '_remove_item').bind('click', function() { th.handleremoveClick(this); }); $('#' + this.prefix + '_add_form').bind('submit', function() { th.handleAddClick(this); return false; }); } else { $('#' + this.prefix + '_edit').bind('click', function() { th.handleEditClick(this); }); } } });
使う場合はこんな感じ。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <link type="text/css" rel="stylesheet" href="sample.css" /> <script type="text/javascript" src="jquery-1.2.6.min.js"></script> <script type="text/javascript" src="trimpath-template-1.0.38.js"></script> <script type="text/javascript" src="core.js"></script> <script type="text/javascript" src="listeditor.js"></script> <script type="text/javascript"> var listeditor1 = new ListEditor('listeditor1'); listeditor1.addItem({name : 'Test'}); listeditor1.addItem({name : 'テスト'}); listeditor1.addItem({name : 'ほげほげ'}); listeditor1.render(); </script> <title>ListEditor</title> </head> <body> <h1>ListEditor</h1> <div id="listeditor1" class="listeditor"></div> </body> </html>
getTemplateやらrenderをすげ替えれば見た目は割と自由が利く。
listeditor1.getTemplate = function(name) { if (name == 'edit') { return '<ul>{for item in items}<li><span class="item">${item.name}</span> <a href="javascript:void(0);" class="${prefix}_remove_item" id="${prefix}_remove_item_${item_index}">x</a></li>{/for}</ul><form method="post" action="" id="${prefix}_add_form"><div><input type="text" name="name" /> <button type="submit" id="${prefix}_add_item">追加</button></div></form><div><a href="javascript:void(0);" id="${prefix}_cancel">キャンセル</a></div>'; } else { return '<p>{for item in items}<span class="item"><a href="http://www.google.co.jp/search?q=${item.name|encodeURI}" target="_blank">${item.name}</a></span> {/for}</p><div><a href="javascript:void(0);" id="${prefix}_edit">編集</a></div>'; } }; listeditor1.render = function() { var context = {items : this.items, prefix : this.prefix, _MODIFIERS : {encodeURI : encodeURI}}; var template_name = ''; if (!this.is_edit) { } else { template_name = 'edit'; } var template = TrimPath.parseTemplate(this.getTemplate(template_name)); this.getTarget().html(template.process(context)); this.bind(); }
trimpath-templateがいい感じに使いやすいなー。contextのところはgetContextにすべきだったかな。
ちなみに実際に使ってるのは、ListEditorを継承して作ったAjaxListEditorだったりする。