やりたいこと
- あるコンポーネントにボタンが複数含まれているものをつくりたい
- 汎用化してボタンの数は可変にしたい
- だがしかしCSSやイベントリスナーの記述をシンプルにしたい
デモ
実装ポイント
CSSの命名規則と属性セレクタで要素をグルーピングする
増えうる複数のアクション系のボタンに対して.btn-action_1
,.btn-action_2
,.btn-action_3
……という具合に連番にしておき*[class^=btn-action]
という指定をするとbtn-actionで開始する要素にマッチすることができます。
こうすることでCSS的に特定の名前をつけつつも似たような連番のクラスが増えたり、class="btn-actoin btn-action_1"
というふうに冗長な記述をする必要がなくなります。
(nth-child(N)
使えよ的な話もあるにはあるのですが……)
CSSの指定をJS側のイベントのセレクターにも利用する
CSSのマッチができるということは、jQueryのセレクターでも利用できるということです。
つまりon(type, delegate, listener)
メソッドの第二引数にセレクターを指定することで、コンポーネント以下の増えうるボタン群のイベントを一括で管理できます。またボタンが増えても変更する必要がありません。
// ボタンが増えると厄介
function listener1(event) { /* ロジック*/ }
function listener2(event) { /* ロジック*/ }
function listener3(event) { /* ロジック*/ }
$('.action-sheet .btn-action_1').on('click', listener1);
$('.action-sheet .btn-action_2').on('click', listener2);
$('.action-sheet .btn-action_3').on('click', listener3);
// 一つの指定で済むし、ボタンが増えても変更がいらない
function listener(event) { /* 分岐ロジック */ }
$('.action-sheet').on('click', '*[class^=btn-action]', listener);
クリックされたボタンを見分ける
押されたボタンのクラスから正規表現で末尾の数字を取り出します。
function listener(event) {
var $btn = $(event.currentTarget);// jQueryイベント
// CSSからアクションボタンの番号を取り出す
var matchedClass = $btn.attr('class').match(/btn\-action_(\d*)/);
if(!matchedClass) return false;
var actionType = parseInt(matchedClass[1]);
if(isNaN(actionType)) return false;
// actionTypeによって処理を変える
}
こんな感じでコンポーネント化できそうだなーということでBackbone.jsのViewで部品化してみました。
Backbone.jsで作ってみた。
CSS ActionSheet Demo using Backbone.js
コンポーネント化する場合、ボタンにアクションを割り当てるインターフェイスが必要になるので、ここではボタンの番号に対応した関数を実行するような仕組みにしました。
var actionSheet = new ActionSheet({ el: '.action-sheet'});
actionSheet.open();
actionSheet.attachAction(function() {
alert('Action!0');
}, 0);
actionSheet.attachAction(function() {
alert('Action!1');
}, 1);
actionSheet.attachAction(function() {
alert('Action!2');
}, 2);
またView#initialize
で複雑なイベント処理をしなくてもView.events
にセレクタを書くだけでシンプルに記述できます。(これがやりたかった)
var ActionSheet = Backbone.View.extend({
events: {
'click [class^="btn-action"]': 'action',
// 中略...
},
initialize: function(options) {
// 中略...
this._actions = [];
// 中略...
},
// ボタンの番号でアクションを設定する
attachAction: function(func, atIndex) {
this._actions[atIndex] = func;
},
// 押されたボタンによって登録されたアクションを実行する
action: function(event) {
var $btn = $(event.currentTarget);
var matchedClass = $btn.attr('class').match(/btn\-action_(\d*)/);
if(!matchedClass) return false;
var actionType = matchedClass[1];
if(isNaN(actionType)) return false;
if(this._actions[actionType]) {
this._actions[actionType]();
}
return false;
},
// 中略...
});
その他のアイディア
- JS利用が前提ならclass名は素直に
btn-action
とし、data-action-type="0"
とかそれ用の属性で判定するほうがよいのかも。 - ボタンの処理の対応はactionイベントを発火させてイベント駆動でもいいかも(分岐自体も外出しする形になるけど。)