jQuery で クリック と ダブルクリック を判別

0 件のコメント

(2013/05/14 jQuery プラグインを作成しました→こちら

クリックとダブルクリックに別機能を載せて実行したいと、個人的に思ったので、試しに実装してみることにした。
どうしてもclickイベントが先に発生してしまうので setTimeout 使うしかないよなぁ…と思いつつ
setTimeout の使いどころによってはうまく実行されないよなぁ…とも思う。。

このソースコードはテスト環境でとりあえず動くよう単純化したものです。
過度な期待はしないでください。

というわけで、setTimeout を大量に利用するスクリプト中では正しく動作しない可能性があります。 まぁ、そんなページはそもそも動作が鈍くなり始めてると思われますが…。

サンプルデモ

IE9、Chromeのみ確認済み。他は…よう知らん(^_^;
try me

実装 & 利用方法

index.js

var targetElement, onclick, ondblclick, instance;

// ターゲットDOM要素
targetElement = window.document.getElementById('targetElementId');

// クリック時のコールバック関数
onclick = function (event) {
    // クリック時の処理を記述…
};

// ダブルクリック時のコールバック関数
ondblclick = function (event) {
    // ダブルクリック時の処理を記述…
};

// インスタンスの生成、実行準備。
instance = new g.fx.CompositeEvent(targetElement, onclick, ondblclick);
※このスクリプトをHTMLの先頭へもってきても動かないので、HTMLの末尾またはonload処理中で実行してください…。。
※jQueryと以下にあるcompositeevent.jsより後に実行してください…。。

compositeevent.js
クリックとダブルクリックを判別する機能を提供するクラス。基本的には以下のコードをコピペして使えばOK。

var g = g || {};
g.fx = g.fx || {};


/**
* @class        クリックとダブルクリックを判別して実行する機能を提供する
*
* @constructor
* @param    {DOMElement}    監視したいDOM要素
* @param    {function}      クリック時に呼び出すコールバック関数
* @param    {function}      ダブルクリック時に呼び出すコールバック関数
*/
g.fx.CompositeEvent = function (target, onclick_handler, ondblclick_handler) {
    $(target).on('mouseup', function (event) {
        var DBLCLK_TIME = g.fx.CompositeEvent.DBLCLK_TIME;
        var currentTime = (new Date()).getTime();
        var clickCount = g.fx.CompositeEvent.count;
        var clickTime = g.fx.CompositeEvent.time;
        var clickEvent = g.fx.CompositeEvent.event;
        var timerId = g.fx.CompositeEvent.timerId;
        
        clickCount += 1;
        
        if (clickCount === 1) {
            // 指定された時間までクリックイベントの実行を待ちます
            timerId = window.setTimeout(function () {
                // クリックイベントの実行
                onclick_handler(event);
                
                // フラグ類のリセット
                timerId = clickEvent = null;
                clickCount = clickTime = 0;
                g.fx.CompositeEvent.count = clickCount;
                g.fx.CompositeEvent.time = clickTime;
                g.fx.CompositeEvent.event = clickEvent;
                g.fx.CompositeEvent.timerId = timerId;
            }, DBLCLK_TIME);
            
            // フラグ類のセット
            clickTime = currentTime;
            clickEvent = event;
        } else {
            if (clickEvent && clickEvent.which === event.which &&
                currentTime - clickTime < DBLCLK_TIME) {
                // ダブルクリックイベントの実行。
                ondblclick_handler(event);
            } else {
                // クリックイベントの実行。
                onclick_handler(clickEvent);
                onclick_handler(event);
            }
            // フラグ類のリセット。
            timerId && window.clearTimeout(timerId);
            timerId = clickEvent = null;
            clickCount = clickTime = 0;
        }
        
        // セットされているフラグ類をグローバルへ移す。
        g.fx.CompositeEvent.count = clickCount;
        g.fx.CompositeEvent.time = clickTime;
        g.fx.CompositeEvent.event = clickEvent;
        g.fx.CompositeEvent.timerId = timerId;
    });
};


/**
* ダブルクリックを監視する時間[msec]
* @private
* @type     number
*/
g.fx.CompositeEvent.DBLCLK_TIME = 300;


/**
* ダブルクリックを監視する時間[msec]
* @private
* @type     number
*/
g.fx.CompositeEvent.count = 0;


/**
* 直前のイベントが発生した時刻
* @private
* @type     number
*/
g.fx.CompositeEvent.time = 0;


/**
* 直前のイベントオブジェクト
* @private
* @type     jQuery.Event
*/
g.fx.CompositeEvent.event = null;


/**
* タイマーID
* @private
* @type     number
*/
g.fx.CompositeEvent.timerId = 0;

難点はやはり setTimeout を利用していることによる以下の点。

  • クリックイベントが即時でないこと
  • setTimeout が多数発行された場合に反応しづらいこと(こちらは一応対策済み)
  • 実装がクロージャなのでIEだとメモリーリークの可能性があること
メモリーリークを注意しつつ、速度向上も行いつつ、要望をかなえなければならない難しさ… というより、その難しさを理解されないことがとてもつらい。。