スマフォ用サイトでclickイベントを使用すると、モサっとした感じになります。
この現象については、clickイベントには待ち時間が存在するからで、スマフォサイトの場合はtouchイベントを使う事で解決する事になると思います。
しかしながら、今のところKnockout.jsにはtouchstart
やtouchend
といったバインディングは存在しません。
touchend
イベントを指定したい場合等は、event
バインディングを使用します。
<div data-bind="with: app">
<button type="button" data-bind="event: {touchend: foo}">TOUCH!!</button>
</div>
<script>
var ViewModel = (function(){
function ViewModel(){}
ViewModel.prototype.foo = function(){
alert('タッチされました。');
};
})();
ko.applyBindings({
app: new ViewModel()
});
</script>
これでtouchend
イベントを紐付ける事が出来ました。
めでたし・めでたし
###・・・ではありません
touchend
は画面をタッチしてスクロールした時も、指を離した瞬間に発生してしまいます。
これを防ぐために、touchstart
とtouchend
の組み合わせで制御したりしますが、上記の様にevent
バインディングで対応するとなると、これはもう大変な作業になってしまいます。
そこで、カスタムバインディングを使用する方法を考えてみます。
<div data-bind="with: app">
<button type="button" data-bind="touchend: foo">TOUCH!!</button>
</div>
<script>
var ViewModel = (function(){
function ViewModel(){}
ViewModel.prototype.foo = function(){
alert('タッチされました。');
};
})();
ko.applyBindings({
app: new ViewModel()
});
// touchendのカスタムバインディング
ko.bindingHandlers['touchend'] = {
'init': function(element, valueAccessor, allBindings, viewModel, bindingContext){
var $el = $(element);
var srcYOffset = null;
// タッチスタート時に、元々のY座標を取得
$el.on("touchstart", function(){
srcYOffset = window.pageYOffset;
});
// タッチエンド
$el.on("touchend", function(){
var destYOffset = window.pageYOffset;
if (srcYOffset === destYOffset) {
var handlerFunction = valueAccessor();
viewModel = bindingContext['$data'];
handlerReturnValue = handlerFunction.apply(viewModel, arguments);
}
});
}
};
</script>
touchend
用のカスタムバインディングの中で、スクロールが発生している場合を判断する様に制御を入れました。
これでdata-bind="touchend: foo"
といった様な記述だけで、スクロール制御がされたtouchendイベントが使用できます。
###追記(答えなし)
上記のコードでちょっと気になっているのが、$elに直接イベントをバインディングしているところです。
これだと、イベントの登録数が増える事が想定されるので、改善案を考えたいところです。