使用jQuery拖放防止点击事件


85

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

什么是防止单击引发未拖放状态同时允许其不处于拖放状态的最佳方法?

我对可排序元素有此问题,但认为最好有一种常规拖放的解决方案。

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


Answers:


88

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

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

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

1
有用!不能一劳永逸地为我工作,可能是因为我在以前的尝试中搞砸了同一件事,并且类最终以与单击的对象不同的元素结束..但是无论如何,我使用了一个全局变量(在这种情况下,简单的页面就可以了),而且效果也很好。
MSpreij 2010年

2
这将我引向解决方案,我只是使用jQuery数据函数而不是类。谢谢。
威廉2010年

3
但是每次放下元素时,浏览器都不会触发点击事件
puchu

6
要保存设置类或数据的设置,还可以在click事件处理程序中使用:if(!$(this).is('。ui-draggable-dragging')){... click action}
ChrisV

1
糟糕,您是否不需要stop: function(event, ui) { $(this).removeClass('noclick'); }可拖动的处理程序?否则,不会每次拖放都会阻碍下次单击“您的选择器”。
鲍勃·斯坦因

41

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

可排序的解决方案:

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

2
看来这不能阻止点击事件对我来说,使用jQuery 1.9.1和jquery.ui 1.10.3即时通讯
阿伦

1
奇怪的是,您回答了自己的问题,而Sasha大笑起来甚至没有用。

:@aaron下面的作品这个答案http://stackoverflow.com/a/4572831/119765

另一个简单的答案-只要发生日的jQuery。点击()处理.draggable()后 stackoverflow.com/questions/18032136/...
JxAxMxIxN

1
如果您将属性帮助器的值设置为'clone',则可以避免将事件触发到拖动排序的项目。{helper:'clone',start,stop ...等},
Luchux

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

这工作正常,但在一种情况下失败-当我进入全屏onclick时:

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()事件中,拖动时仍无法全屏显示。适合我!


1
解决方案4是只有一个对我有用(在IE11 / Edge中)。
西蒙(Simon)

1
#4对我来说是正确,最简单的解决方案,无需使用类或变量。
电子版

11

我想补充一点,似乎只有在可拖动事件或可排序事件之后定义了单击事件的情况下,防止单击事件才起作用。如果单击首先添加,则在拖动时将被激活。


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

坚如磐石..


这对我来说是最好的解决方案。我很难使单击事件在可使用jquery.ui.touch.punch.js在移动Web浏览器中进行排序的jQuery UI上正常工作,但这相当优雅地解决了它。
Godsmith

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

2
看来,jQuery UI将'ui-sortable-helper'类附加到了要排序的项目上。因此,您可以省去'noclick'类,而只需执行($(this).hasClass('ui-sortable-helper'))。这样更简洁
Matt De Leon

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

3

在jQuery UI中,被拖动的元素具有“ ui-draggable-dragging”类。
因此,我们可以使用此类来确定是否单击,只是延迟事件。
您无需使用“开始”或“停止”回调函数,只需执行以下操作:

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

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


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

1

就我而言,它的工作方式如下:

$('#draggable').draggable({
  start: function(event, ui) {
    $(event.toElement).one('click', function(e) { e.stopPropagation(); });
  }
});

0

您是否尝试过使用event.preventDefault()禁用链接;在开始事件中,然后使用取消绑定在拖动停止事件或放置事件中重新启用它?


0

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

显然,这违反了“拖动动作后定义单击动作”规则,因此,作为一种解决方法,我将SalesForce元素的动作重新定义为“ onDblClick”,并将此代码用于容器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');
    }
});

父级的click事件本质上隐藏了双击子元素的需求,使用户体验保持完整。


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

});

0

对我来说,帮助通过选项对象传递了帮助者:

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

似乎克隆被拖动的dom元素可以防止事件冒泡。我无法通过任何eventPropagation,冒泡等方式来避免这种情况。这是我唯一可行的解​​决方案。


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();" />

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


0

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

.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();
        }
    });
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.