web-dev-qa-db-ja.com

jQueryを使用したJavascript:クリックして同じ要素をダブルクリックすると、異なる効果があり、一方が他方を無効にします

興味深い状況があります-現在、「展開」ボタンをクリックすると非表示になっているテーブル行があります。展開ボタンを含む元の(隠されていない)行の特定のセルには、クリックすると編集可能になるコンテンツもあります。展開ボタンを取り除き、行をクリックすると編集可能になるフィールドなど、行自体の任意の場所でダブルクリックして行を展開できるようにします。あなたはすでにここで問題を嗅ぐことができます。

行をダブルクリックすると、dblclickが発生する前に2つのクリックイベントが最初に発生します。つまり、フィールドをダブルクリックすると、編集可能なフィールドに変わり、行が展開されます。これを防ぎたいです。ダブルクリックでシングルクリックの発生を防ぎ、シングルクリックで通常どおり実行するようにします。

Event.stopPropagation()の使用は、2つの異なるイベントであるため、明らかに機能しません。

何か案は?

編集(いくつかの準擬似コード):

元のバージョン:

<table>
    <tbody>
        <tr>
            <td><a href="javascript:$('#row_to_expand').toggle();" title="Expand the hidden row">Expand Row</a></td>
            <td>Some kind of random data</td>
            <td><?= $editable_cell_which_turns_into_an_input_field_on_single_click[0]->value("First editable value") ?></td>
            <td><?= $editable_cell_which_turns_into_an_input_field_on_single_click[1]->value("Second editable value") ?></td>
            <td><?= $editable_cell_which_turns_into_an_input_field_on_single_click[2]->value("Third editable value") ?></td>
            <!-- ... -->
            <td><?= $editable_cell_which_turns_into_an_input_field_on_single_click[n]->value("Nth editable value") ?></td>
        </tr>
        <tr style="display: none" id="row_to_expand">
            <td colspan="n">Some hidden data</td>
        </tr>
    </tbody>
</table>

望ましいバージョン:

<table>
    <tbody>
        <tr ondblclick="$('#row_to_expand').toggle()">
            <td>Some kind of random data</td>
            <td><?= $editable_cell_which_turns_into_an_input_field_on_single_click[0]->value("First editable value") ?></td>
            <td><?= $editable_cell_which_turns_into_an_input_field_on_single_click[1]->value("Second editable value") ?></td>
            <td><?= $editable_cell_which_turns_into_an_input_field_on_single_click[2]->value("Third editable value") ?></td>
            <!-- ... -->
            <td><?= $editable_cell_which_turns_into_an_input_field_on_single_click[n]->value("Nth editable value") ?></td>
        </tr>
        <tr style="display: none" id="row_to_expand">
            <td colspan="n">Some hidden data</td>
        </tr>
    </tbody>
</table>

乾杯

40
Swader

一般的な考え方:

  1. 最初のクリックでは、関連する関数(single_click_function()など)を呼び出さないでください。むしろ、一定の時間(xなど)のタイマーを設定します。その期間中に別のクリックが発生しない場合は、single_click_function()に進みます。取得した場合は、double_click_function()を呼び出します

  2. 2回目のクリックが受信されると、タイマーはクリアされます。また、xミリ秒が経過するとクリアされます。

ところで、パオロの返事をチェックしてください: ダブルクリックイベントが検出されたときにクリック/マウスアップイベントをキャンセルする必要があります そしてもちろんスレッド全体! :-)

より良い答え: https://stackoverflow.com/a/7845282/260610

23
UltraInstinct

作業デモ

$('#alreadyclicked').val('no click');
$('td.dblclickable').on('click',function(){
    var $button=$(this);
    if ($button.data('alreadyclicked')){
        $button.data('alreadyclicked', false); // reset
        $('#alreadyclicked').val('no click');

        if ($button.data('alreadyclickedTimeout')){
            clearTimeout($button.data('alreadyclickedTimeout')); // prevent this from happening
        }
        // do what needs to happen on double click. 
        $('#action').val('Was double clicked');
    }else{
        $button.data('alreadyclicked', true);
        $('#alreadyclicked').val('clicked');
        var alreadyclickedTimeout=setTimeout(function(){
            $button.data('alreadyclicked', false); // reset when it happens
            $('#alreadyclicked').val('no click');
            $('#action').val('Was single clicked');
            // do what needs to happen on single click. 
            // use $button instead of $(this) because $(this) is 
            // no longer the element
        },300); // <-- dblclick tolerance here
        $button.data('alreadyclickedTimeout', alreadyclickedTimeout); // store this id to clear if necessary
    }
    return false;
});

明らかに<td class="dblclickable">

12
Popnoodles

User822711の回答を書き換えて再利用します。

  $.singleDoubleClick = function(singleClk, doubleClk) {
    return (function() {
      var alreadyclicked = false;
      var alreadyclickedTimeout;

      return function(e) {
        if (alreadyclicked) {
          // double
          alreadyclicked = false;
          alreadyclickedTimeout && clearTimeout(alreadyclickedTimeout);
          doubleClk && doubleClk(e);
        } else {
          // single
          alreadyclicked = true;
          alreadyclickedTimeout = setTimeout(function() {
            alreadyclicked = false;
            singleClk && singleClk(e);
          }, 300);
        }
      }
    })();
  }

関数を呼び出します。

$('td.dblclickable').bind('click', $.singleDoubleClick(function(e){
  //single click.
}, function(e){
  //double click.
}));
3
puttyshell

カスタムの「シングルクリック」イベントを使用して、シングルクリックとダブルクリックを区別できるシンプルなjQueryプラグインを作成しました。これにより、シングルクリックで入力を編集可能にし、ダブルクリックで入力を展開できるインターフェイスを構築できます。 Windows/OSXファイルシステムの名前変更/オープンUIとよく似ています。

https://github.com/omriyariv/jquery-singleclick

$('#someInput').on('singleclick', function(e) {
    // The event will be fired with a small delay but will not fire upon a double-click.
    makeEditable(e.target);
}

$('#container').on('dblclick', function(e) {
    // Expand the row...
}
2
omriyariv

この問題は、編集可能なフィールドがクリックされたときにのみ発生するため、イベントの伝播をキャンセルする通常のclickハンドラーをその要素にアタッチします( stopPropagation を参照)。たとえば、dbl-clickが500msであると見なされる2つのクリック間のデフォルト時間(600ms)にタイムアウト(setTimeout(...))を設定します [src])。その時点までにdblclickが発生していない場合(これを検出するフラグとして機能する両方のイベントハンドラーで変数にアクセスできます)、ユーザーが代わりに行を展開し、続行することを想定できますそのアクション...

IMO、これを再考する必要があります。残念ながら、シングルクリックハンドラーは、ユーザーがダブルクリックしようとしているかどうかを知ることができません。

両方のアクションをシングルクリックにすることをお勧めしますが、クリックされたときに編集可能なフィールドから上に伝播しないようにします。

2
James
window.UI = {
  MIN_LAPS_FOR_DBL_CLICK: 200, // msecs

  evt_down:null,
  timer_down:null,
  mousedown:function(evt){
    if( this.evt_down && evt.timeStamp < this.evt_down.timeStamp + this.MIN_LAPS_FOR_DBL_CLICK ){
      clearTimeout(this.timer_down);
      this.double_click(evt); // => double click
    } else {
      this.evt_down   = evt;
      this.timer_down = setTimeout("UI.do_on_mousedown()", this.MIN_LAPS_FOR_DBL_CLICK);
    }
  },
  evt_up:null,
  timer_up:null,
  mouseup:function(evt){
    if( this.evt_up && evt.timeStamp < this.evt_up.timeStamp + this.MIN_LAPS_FOR_DBL_CLICK ){
      clearTimeout(this.timer_up);
    } else {
      this.evt_up   = evt;
      this.timer_up = setTimeout("UI.do_on_mouseup()", this.MIN_LAPS_FOR_DBL_CLICK);
    }
  },
  do_on_mousedown:function(){
    var evt = this.evt_down;
    // Treatment MOUSE-DOWN here
  },
  do_on_mouseup:function(){
    var evt = this.evt_up;
    // Treatment MOUSE-UP here
  },
  double_click:function(evt){
    // Treatment DBL-CLICK here
  }

}

// Then the observers

$('div#myfoo').bind('mousedown',  $.proxy(UI.mousedown, UI));
$('div#myfoo').bind('mouseup',    $.proxy(UI.mouseup, UI));
1
Philippe Perret

Vanilla javascriptのこの例は動作するはずです。

var timer = 0
var delay = 200
var prevent = false

node.onclick = (evnt) => {
    timer = setTimeout(() => {
         if(!prevent){
             //Your code
         }
         prevent = false
    }, delay)
}
node.ondblclick = (evnt) => {
    clearTimeout(timer)
    prevent=true
    //Your code
}
1

@puttyshellからの回答に加えて、このコードにはvisualSingleClick関数用のスペースがあります。これはsetTimeoutの前に実行され、タイムアウト前にアクションの視覚的アクションをユーザーに示すことを目的としており、それにより、より大きなタイムアウトを使用できます。 Googleドライブのブラウジングインターフェイスでこれを使用して、クリックしたファイルまたはフォルダーが選択されていることをユーザーに示しています。 Googleドライブ自体も同様のことを行いますが、コードは表示されません。

/* plugin to enable single and double click on same object */
$.singleDoubleClick = function(visualSingleClk, singleClk, doubleClk) {
  return (function() {
    var alreadyclicked = false;
    var alreadyclickedTimeout;

    return function(e) {
      if (alreadyclicked) {
        // double
        alreadyclicked = false;
        alreadyclickedTimeout && clearTimeout(alreadyclickedTimeout);
        doubleClk && doubleClk(e);
      } else {
        // single
        alreadyclicked = true;
        visualSingleClk && visualSingleClk(e);
        alreadyclickedTimeout = setTimeout(function() {
          alreadyclicked = false;
          singleClk && singleClk(e);
        }, 500);
      }
    }
  })();
}
0
chronossc