使用jQuery的Javascript:单击并双击相同的元素,不同的效果,一个禁用另一个

时间:2011-03-29 10:46:17

标签: javascript jquery events event-bubbling propagation

我有一个有趣的情况 - 我有一个表格行,当前,当我点击“展开”按钮时,它显示它是隐藏的对应物。包含展开按钮的原始(未隐藏)行也包含某个单元格中的某些内容,单击该单元格后,该内容将变为可编辑状态。我想摆脱展开按钮,并通过双击行本身的任何位置来启用行的扩展,包括单击它时变为可编辑的字段。你可以在这里闻到麻烦。

当我双击一行时,在dblclick发生之前首先触发两个单击事件。这意味着如果我双击该字段,它将变为可编辑的字段,并且该行将展开。我想阻止这个。我希望双击可以防止单击的触发,并且单击可以照常执行。

使用event.stopPropagation()显然不会起作用,因为它们是两个不同的事件。

有什么想法吗?

编辑(一些半伪代码):

原始版本:

<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>

干杯

10 个答案:

答案 0 :(得分:23)

总体思路:

  1. 第一次点击时,不要调用相关的函数(比如single_click_function())。而是将计时器设置为特定时间段(比如x)。如果我们在该时间段内没有再次点击,请转到single_click_function()。如果我们得到一个,请调用double_click_function()

  2. 收到第二次点击后,计时器将被清除。它也会在x毫秒过后被清除。

  3. 顺便说一句,检查Paolo的回复:Need to cancel click/mouseup events when double-click event detected 当然还有整个线程! : - )

      

    更好的回答:https://stackoverflow.com/a/7845282/260610

答案 1 :(得分:12)

Working demo

$('#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">

答案 2 :(得分:3)

重写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 :(得分:2)

只有在单击可编辑字段时才会出现此问题,因此请将常规click处理程序附加到该元素,这将取消事件的传播(see stopPropagation)并设置超时({{1例如,600毫秒(两次点击之间的默认时间被视为dbl-click是500ms [src] )。如果那时setTimeout(...)没有发生(你可以在两个事件处理程序中都可以访问var作为检测这个的标志)那么你可以假设用户想要扩展行而是继续行动...

IMO,你应该重新思考一下。唉,单击处理程序无法知道用户是否要双击。

我建议单击两个操作,只需点击它就不会从可编辑字段向上传播。

答案 4 :(得分:2)

我写了一个简单的jQuery插件,它允许你使用自定义'singleclick'事件来区分单击和双击。这允许您构建一个界面,单击此选项可使输入可编辑,同时双击展开它。很像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...
}

答案 5 :(得分:1)

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));

答案 6 :(得分:1)

这个示例在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
}

答案 7 :(得分:0)

从@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);
      }
    }
  })();
}

答案 8 :(得分:0)

我知道这篇文章是 10 年前写的,但我想贡献我的解决方案。

主要思想是忽略 dbleClick 事件并使用 onClick 事件处理它。

所以,这是您必须在点击事件上绑定到元素上的 javascript 代码。

var d = new Date();
lastClickTime = d.getTime();

if (lastClickTime - gc1 < 500)
 {
      //  2 clicks closer in time less than 0.5 sec  = dbleclick 
    gc1 = 0;
   
    //your code for dble click
}
else {
   //1 clic
     gc1 = lastClickTime;
   //your code for simple click
}

// gc1 is a global variable in your document initialized to 0

答案 9 :(得分:-2)

如果你正在使用backbonejs,有一个更优雅的方法来处理这个问题:


    initialize: function(){
        this.addListener_openWidget();
    }

    addListener_openWidget: function(){
        this.listenToOnce( this, 'openWidgetView', function(e){this.openWidget(e)} );
    },

    events: {
        'click #openWidgetLink' : function(e){this.trigger('openWidgetView',e)},
        'click #closeWidgetLink' : 'closeWidget'
    },

    openWidget: function( e ){
       //Do your stuff
    },

    closeWidget: function(){
       this.addListener_openWidget();
    }