Javascript with jQuery: Click and double click on

2019-01-03 10:13发布

I have an interesting situation - I have a table row which, currently, shows it's hidden counterpart when I click the "Expand" button. The original (unhidden) row which contains the expand button also has some content in a certain cell which, when clicked, becomes editable. I would like to be rid of the expand button, and enable expanding of the row via doubleclick anywhere in the row itself, including the field that turns editable when you click it. You can smell the trouble here already.

When I double click a row, two click events are fired first, before the dblclick occurs. This means if I double click the field, it will turn into an editable one, and the row will expand. I would like to prevent this. I want the doubleclick to prevent the firing of the single click, and the single click to perform as usual.

Using event.stopPropagation() clearly won't work since they're two different events.

Any ideas?

Edit (some semi-pseudo code):

Original version:

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

Desired version:

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

Cheers

9条回答
在下西门庆
2楼-- · 2019-01-03 10:43

This example in vanilla javascript should work:

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
}
查看更多
beautiful°
3楼-- · 2019-01-03 10:46

Rewrite user822711's answer for reuse:

  $.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);
        }
      }
    })();
  }

Call the function.

$('td.dblclickable').bind('click', $.singleDoubleClick(function(e){
  //single click.
}, function(e){
  //double click.
}));
查看更多
爱情/是我丢掉的垃圾
4楼-- · 2019-01-03 10:48

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

obviously use <td class="dblclickable">

查看更多
唯我独甜
5楼-- · 2019-01-03 10:49

Adding to answer from @puttyshell, this code has space for a visualSingleClick function. This run before setTimeout and is intended to show user some visual action for the action before the timeout, and with that I can use a greater timeout. I'm using this on a Google Drive browsing interface to show user that file or folder that he clicked is selected. Google Drive itself does something similar, but don't saw it code.

/* 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);
      }
    }
  })();
}
查看更多
来,给爷笑一个
6楼-- · 2019-01-03 10:52

There is a more elegant way handling this issue if you're using 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();
    }

查看更多
Viruses.
7楼-- · 2019-01-03 10:54

The issue only occurs when the editable field is clicked, so attach a regular click handler to that element which will cancel propagation of the event (see stopPropagation) and will set a timeout (setTimeout(...)) for, say, 600ms (default time between two clicks to be deemed a dbl-click is 500ms [src]). If, by that time the dblclick has not occurred (you can have a var accessible in both event handlers that acts as a flag to detect this) then you can assume the user wants to expand the row instead and continue with that action...

IMO, you should re-think this. Alas, a single click handler cannot know if the user is about to double-click.

I suggest making both actions single click, and simply don't propagate up from the editable field when it is clicked.

查看更多
登录 后发表回答