Javascript with jQuery:单击并双击同一元素,效果不同,一个禁用另一个
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5471291/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Javascript with jQuery: Click and double click on same element, different effect, one disables the other
提问by Swader
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.
当我双击一行时,在 dblclick 发生之前,首先触发两个单击事件。这意味着如果我双击该字段,它将变成一个可编辑的字段,并且该行将展开。我想防止这种情况。我希望双击以防止单击触发,并且单击像往常一样执行。
Using event.stopPropagation() clearly won't work since they're two different events.
使用 event.stopPropagation() 显然不起作用,因为它们是两个不同的事件。
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
干杯
采纳答案by UltraInstinct
The general idea:
总体思路:
Upon the first click, dont call the associated function (say single_click_function()). Rather, set a timer for a certain period of time(say x). If we do not get another click during that time span, go for the single_click_function(). If we do get one, call double_click_function()
Timer will be cleared once the second click is received. It will also be cleared once x milliseconds are lapsed.
第一次点击时,不要调用相关的函数(比如 single_click_function())。相反,设置一个特定时间段的计时器(比如 x)。如果我们在这段时间内没有再次点击,请使用 single_click_function()。如果我们得到一个,调用 double_click_function()
一旦收到第二次点击,计时器将被清除。一旦 x 毫秒过去,它也会被清除。
BTW, check Paolo's reply out: Need to cancel click/mouseup events when double-click event detectedand of course the entire thread! :-)
顺便说一句,请查看 Paolo 的回复:需要在检测到双击事件时取消点击/鼠标点击事件,当然还有整个线程!:-)
Better answer: https://stackoverflow.com/a/7845282/260610
回答by Popnoodles
$('#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">
显然使用 <td class="dblclickable">
回答by puttyshell
Rewrite user822711's answer for reuse:
重写 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);
}
}
})();
}
Call the function.
调用函数。
$('td.dblclickable').bind('click', $.singleDoubleClick(function(e){
//single click.
}, function(e){
//double click.
}));
回答by omriyariv
I wrote a simple jQuery plugin that lets you use a custom 'singleclick' event to differentiate a single-click from a double-click. This allows you to build an interface where a single-click makes the input editable while a double-click expands it. Much like Windows/OSX filesystem rename/open UI.
我编写了一个简单的 jQuery 插件,它允许您使用自定义的“singleclick”事件来区分单击和双击。这允许您构建一个界面,其中单击使输入可编辑,而双击将其展开。很像 Windows/OSX 文件系统重命名/打开 UI。
https://github.com/omriyariv/jquery-singleclick
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...
}
回答by James
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...
这个问题只发生在单击编辑字段时,那么附加定期click
处理该元素将取消该事件(的传播见stopPropagation
),并会设置一个超时(setTimeout(...)
,比如说,600毫秒)(两次点击之间的默认时间被视为dbl-click 是 500 毫秒[src])。如果dblclick
到那时还没有发生(您可以在两个事件处理程序中都有一个 var 可访问,作为检测这一点的标志),那么您可以假设用户想要扩展该行并继续执行该操作...
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.
我建议单击这两个操作,并且在单击时不要从可编辑字段向上传播。
回答by Ismayil Niftaliyev
This example in vanilla javascript should work:
这个在 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
}
回答by Philippe Perret
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));
回答by chronossc
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.
添加到@puttyshell 的回答中,此代码具有用于visualSingleClick
函数的空间。此运行之前setTimeout
,旨在向用户显示超时前操作的一些视觉操作,因此我可以使用更大的超时。我在 Google Drive 浏览界面上使用它来向用户显示他单击的文件或文件夹已被选中。Google Drive 本身也做了类似的事情,但没有看到它的代码。
/* 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);
}
}
})();
}
回答by Adam Malchi
There is a more elegant way handling this issue if you're using backbonejs:
如果您使用的是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(); }