87

我在页面上有可以用 jQuery 拖动的元素。这些元素是否具有导航到另一个页面的点击事件(例如普通链接)。

什么是防止单击在放置此类元素时触发的最佳方法,同时允许单击它不是拖放状态?

我对可排序元素有这个问题,但我认为最好有一个通用拖放的解决方案。

我已经为自己解决了这个问题。之后我发现Scriptaculous 存在相同的解决方案,但也许有人有更好的方法来实现这一点。

4

16 回答 16

91

一个对我来说效果很好并且不需要超时的解决方案:(是的,我有点迂腐;-)

我在拖动开始时向元素添加了一个标记类,例如“noclick”。当元素被放下时,点击事件被触发——更准确地说,如果拖动结束,实际上它不必被放置到一个有效的目标上。在单击处理程序中,如果存在标记类,我将删除该标记类,否则单击将正常处理。

$('your selector').draggable({
    start: function(event, ui) {
        $(this).addClass('noclick');
    }
});

$('your selector').click(function(event) {
    if ($(this).hasClass('noclick')) {
        $(this).removeClass('noclick');
    }
    else {
        // actual click event code
    }
});
于 2010-03-26T19:16:30.200 回答
41

解决方案是添加单击处理程序,以防止单击在拖动开始时传播。然后在执行 drop 后删除该处理程序。最后一个动作应该延迟一点,以防止点击工作。

可排序的解决方案:

...
.sortable({
...
        start: function(event, ui) {
            ui.item.bind("click.prevent",
                function(event) { event.preventDefault(); });
        },
        stop: function(event, ui) {
            setTimeout(function(){ui.item.unbind("click.prevent");}, 300);
        }
...
})

可拖动的解决方案:

...
.draggable({
...
        start: function(event, ui) {
            ui.helper.bind("click.prevent",
                function(event) { event.preventDefault(); });
        },
        stop: function(event, ui) {
            setTimeout(function(){ui.helper.unbind("click.prevent");}, 300);
        }
...
})
于 2009-11-20T16:26:36.193 回答
12

我遇到了同样的问题并尝试了多种方法,但没有一个对我有用。

解决方案 1

$('.item').click(function(e)
{            
    if ( $(this).is('.ui-draggable-dragging') ) return false;
});  

对我没有任何帮助。拖动完成后单击该项目。

解决方案 2(由 Tom de Boer 提供)

$('.item').draggable(
{   
    stop: function(event, ui) 
    {
         $( event.originalEvent.target).one('click', function(e){ e.stopImmediatePropagation(); } );
    }
});

这工作得很好,但在一种情况下失败了——当我全屏点击时:

var body = $('body')[0];     
req = body.requestFullScreen || body.webkitRequestFullScreen || body.mozRequestFullScreen;
req.call(body); 

解决方案 3(由 Sasha Yanovets 提供)

 $('.item').draggable({
        start: function(event, ui) {
            ui.helper.bind("click.prevent",
                function(event) { event.preventDefault(); });
        },
        stop: function(event, ui) {
            setTimeout(function(){ui.helper.unbind("click.prevent");}, 300);
        }
})

这对我不起作用。

解决方案 4 - 唯一一个工作得很好

$('.item').draggable(
{   
});
$('.item').click(function(e)
{  
});

是的,就是这样-正确的顺序可以解决问题-首先您需要绑定 draggable() 然后 click() 事件。即使我将全屏切换代码放在 click() 事件中,拖动时它仍然没有进入全屏。适合我!

于 2015-03-28T12:44:20.040 回答
11

我想补充一点,似乎只有在可拖动或可排序事件之后定义了单击事件时,才阻止单击事件。如果首先添加点击,它会在拖动时激活。

于 2010-03-15T13:34:49.550 回答
9

我真的不喜欢使用计时器或预防,所以我所做的是:

var el, dragged

el = $( '#some_element' );

el.on( 'mousedown', onMouseDown );
el.on( 'mouseup', onMouseUp );
el.draggable( { start: onStartDrag } );

onMouseDown = function( ) {
  dragged = false;
}

onMouseUp = function( ) {
  if( !dragged ) {
    console.log('no drag, normal click')
  }
}

onStartDrag = function( ) {
  dragged = true;
}

坚如磐石..

于 2012-11-15T08:48:17.527 回答
3

lex82 的版本,但用于 .sortable()

 start: function(event, ui){
 ui.item.find('.ui-widget-header').addClass('noclick');
 },

你可能只需要:

 start: function(event, ui){
 ui.item.addClass('noclick');
 },

这是我用于切换的内容:

$("#datasign-widgets .ui-widget-header").click(function(){
if ($(this).hasClass('noclick')) {
$(this).removeClass('noclick');

}
else {
$(this).next().slideToggle();
$(this).find('.ui-icon').toggleClass("ui-icon-minusthick").toggleClass("ui-icon-plusthick");
}
});
于 2011-01-10T20:24:09.580 回答
3

Sasha 的答案的一个可能的替代方案,而不会阻止默认:

var tmp_handler;
.sortable({
        start : function(event,ui){
            tmp_handler = ui.item.data("events").click[0].handler;
            ui.item.off();
        },
        stop : function(event,ui){
            setTimeout(function(){ui.item.on("click", tmp_handler)}, 300);
        },
于 2012-11-09T19:10:46.230 回答
3

在 jQuery UI 中,被拖动的元素被赋予“ui-draggable-dragging”类。
因此我们可以使用这个类来判断是否点击,只是延迟事件。
您不需要使用“开始”或“停止”回调函数,只需执行以下操作:

$('#foo').on('mouseup', function () {
    if (! $(this).hasClass('ui-draggable-dragging')) {
        // your click function
    }
});

这是由“mouseup”触发的,而不是“mousedown”或“click”——所以会有一点延迟,可能并不完美——但它比这里建议的其他解决方案更容易。

于 2017-03-04T18:32:41.803 回答
2

就我而言,它是这样工作的:

$('#draggable').draggable({
  start: function(event, ui) {
    $(event.toElement).one('click', function(e) { e.stopPropagation(); });
  }
});
于 2015-08-03T13:28:58.690 回答
1

在阅读了这个和几个线程之后,这就是我采用的解决方案。

var dragging = false;
$("#sortable").mouseover(function() {
    $(this).parent().sortable({
        start: function(event, ui) {
            dragging = true;
        },
        stop: function(event, ui) {
            // Update Code here
        }
    })
});
$("#sortable").click(function(mouseEvent){
    if (!dragging) {
        alert($(this).attr("id"));
    } else {
        dragging = false;
    }
});
于 2014-09-09T17:11:13.520 回答
1

最简单和最强大的解决方案?只需在您的可拖动对象上创建透明元素。

.click-passthrough {
  position: absolute;
  left: 0;
  top: 0;
  right: 0;
  bottom: 0;
  background: transparent;
}

element.draggable({        
        start: function () {

        },
        drag: function(event, ui) {
            // important! if create the 'cover' in start, then you will not see click events at all
                  if (!element.find('.click-passthrough').length) {
                      element.append("<div class='click-passthrough'></div>");
                  }
        },
        stop: function() {
          // remove the cover
          element.find('.click-passthrough').remove();
        }
    });
于 2019-11-13T12:02:02.430 回答
0

您是否尝试过使用 event.preventDefault(); 禁用链接?在开始事件中并在拖动停止事件或使用解除绑定的放下事件中重新启用它?

于 2009-11-20T16:29:11.987 回答
0

只是有点皱纹添加到上面给出的答案。我必须制作一个包含可拖动 SalesForce 元素的 div,但 SalesForce 元素通过一些 VisualForce gobbledigook 在 html 中定义了一个 onclick 操作。

显然这违反了“在拖动操作之后定义单击操作”规则,因此作为一种解决方法,我重新定义了要触发“onDblClick”的 SalesForce 元素的操作,并将此代码用于容器 div:

$(this).draggable({
        zIndex: 999,
        revert: true,
        revertDuration: 0,
        start: function(event, ui) {
                   $(this).addClass('noclick');
                }
});

$(this).click(function(){
    if( $(this).hasClass('noclick'))
    {
        $(this).removeClass('noclick');
    }
    else
    {
        $(this).children(":first").trigger('dblclick');
    }
});

父元素的点击事件本质上隐藏了双击子元素的需要,保持用户体验不变。

于 2013-05-15T15:01:16.467 回答
0

我试过这样:

var dragging = true; 

$(this).click(function(){
  if(!dragging){
    do str...
  }
});

$(this).draggable({
  start: function(event, ui) {
      dragging = true;
  },

  stop: function(event, ui) {
      setTimeout(function(){dragging = false;}, 300);
  }

});
于 2015-05-17T03:23:41.357 回答
0

对我来说,帮助将选项对象中的助手传递为:

.sortable({
   helper : 'clone', 
   start:function(), 
   stop:function(),
   .....
});

似乎克隆被拖动的 dom 元素阻止了事件的冒泡。我无法通过任何 eventPropagation、冒泡等来避免它。这对我来说是唯一可行的解​​决方案。

于 2019-10-09T00:35:09.290 回答
0

onmousedown 和 onmouseup 事件在我的一个较小的项目中起作用。

var mousePos = [0,0];
function startClick()
{
    mousePos = [event.clientX,event.clientY];
}
        
function endClick()
{
    if ( event.clientX != mousePos[0] && event.clientY != mousePos[1] )
    {
        alert( "DRAG CLICK" );
    }
    else
    {
        alert( "CLICK" );
    }
}
<img src=".." onmousedown="startClick();" onmouseup="endClick();" />

是的,我知道。不是最干净的方法,但你明白了。

于 2019-10-10T10:42:26.647 回答