使用jQuery拖放防止单击事件

时间:2009-11-20 16:25:11

标签: javascript jquery events drag-and-drop jquery-ui-sortable

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

什么是阻止点击触发放弃此类元素同时允许点击它的最佳方法是不是拖放状态?

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

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

17 个答案:

答案 0 :(得分:86)

一个对我有用的解决方案,并且不需要超时:(是的,我有点迂腐; - )

我在拖动开始时向元素添加标记类,例如'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 :(得分:38)

解决方案是添加点击处理程序,以防止点击在拖动开始时传播。然后在执行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);
        }
...
})

答案 2 :(得分:11)

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

答案 3 :(得分:11)

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

解决方案1 ​​

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

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

解决方案2 (汤姆·德波尔)

$('.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()事件中放入全屏切换代码,拖动时仍然没有进入全屏模式。对我来说很完美!

答案 4 :(得分:9)

我发现使用标准的jQuery点击功能:

$("#element").click(function(mouseEvent){ // click event });

在jQuery UI中运行得很好。拖放时不会执行click事件。 :)

答案 5 :(得分:8)

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

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

<强> Rocksolid ..

答案 6 :(得分:3)

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

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

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

答案 7 :(得分: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);
        },

答案 8 :(得分: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");
}
});

答案 9 :(得分: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;
    }
});

答案 10 :(得分: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);
  }

});

答案 11 :(得分:0)

对我来说,帮助将helper在options对象中传递为:

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

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

答案 12 :(得分:0)

就我而言,它的工作原理如下:

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

答案 13 :(得分:0)

您是否尝试使用event.preventDefault()禁用链接;在start事件中,使用unbind?

在drag stopped事件或drop事件中重新启用它

答案 14 :(得分: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();" />

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

答案 15 :(得分:0)

添加上面给出的答案只是有点皱纹。我必须创建一个包含可拖动的SalesForce元素的div,但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');
    }
});

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

答案 16 :(得分: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();
        }
    });