我有一个有趣的情况——我有一个表行,当前,当我单击“展开”按钮时,它显示它是隐藏的对应行。包含展开按钮的原始(未隐藏)行在特定单元格中也有一些内容,单击该单元格后,这些内容将变为可编辑。我想摆脱展开按钮,并通过双击行本身的任意位置来展开行,包括单击它时变为可编辑的字段。你已经可以闻到这里的麻烦了。
当我双击一行时,在 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>
干杯
总体思路:
第一次点击时,不要调用相关函数(比如single_click_function())。相反,为特定时间段(比如 x)设置一个计时器。如果我们在该时间跨度内没有再次点击,请使用 single_click_function()。如果我们确实得到一个,请调用 double_click_function()
收到第二次点击后,计时器将被清除。一旦 x 毫秒过去,它也将被清除。
顺便说一句,查看 Paolo 的回复:检测到双击事件时需要取消点击/鼠标弹起事件 当然还有整个线程! :-)
$('#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">
我写了一个简单的 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...
}
重写 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.
}));
问题仅在单击可编辑字段时发生,因此将常规
click
处理程序附加到该元素,这将取消事件的传播(see stopPropagation
)并设置超时(setTimeout(...)
),比如,600 毫秒(两次点击之间的默认时间被视为 dbl-click 是 500 毫秒[src])。如果到那时 dblclick
还没有发生(您可以在两个事件处理程序中都可以访问一个 var 作为检测此标志的标志),那么您可以假设用户想要扩展该行并继续执行该操作。 ..
IMO,你应该重新考虑一下。 las,单击处理程序无法知道用户是否要双击。
我建议让这两个动作都单击一次,并且在单击时不要从可编辑字段向上传播。
香草 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
}
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));
添加来自@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);
}
}
})();
}
以下是我在 jquery 中的处理方式:
async function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
let clickCount = 0;
$(`#btnDebug`).on(`click`, async function() {
clickCount++;
if (clickCount > 1) {
// handle our double-click event
console.log(`got double click`);
/*
...do stuff
*/
clickCount = -1;
return;
}
await sleep(300);
if (clickCount === -1) {
// we handled a double-click event so set everything back and exit
clickCount = 0;
return;
}
// handle our single-click event. We made it past our 300ms sleep and we didn't get another click so we're assuming a single-click.
clickCount = 0;
console.log(`got single click`);
/*
...do stuff
*/
});
我意识到我可以执行 setTimeout 而不是睡眠,但我经常使用该睡眠功能,所以我只是在这里使用它。
如果您使用的是 backbonejs,则有一种更优雅的方法来处理此问题:
初始化:函数(){ this.addListener_openWidget(); } addListener_openWidget: 函数(){ this.listenToOnce( this, 'openWidgetView', function(e){this.openWidget(e)} ); }, 事件:{ '点击#openWidgetLink' : function(e){this.trigger('openWidgetView',e)}, '点击#closeWidgetLink' : 'closeWidget' }, openWidget: 函数( e ){ //做你的事 }, 关闭小部件:函数(){ this.addListener_openWidget(); }