Preventing click event with jQuery drag and drop
Asked Answered
A

16

87

I have elements on the page which are draggable with jQuery. Do these elements have click event which navigates to another page (ordinary links for example).

What is the best way to prevent click from firing on dropping such element while allowing clicking it is not dragged and drop state?

I have this problem with sortable elements but think it is good to have a solution for general drag and drop.

I've solved the problem for myself. After that I found that same solution exists for Scriptaculous, but maybe someone has a better way to achieve that.

Arium answered 20/11, 2009 at 16:25 Comment(1)
Similar/duplicate: #3487260Bibby
A
41

Solution is to add click handler that will prevent click to propagate on start of drag. And then remove that handler after drop is performed. The last action should be delayed a bit for click prevention to work.

Solution for sortable:

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

Solution for draggable:

...
.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);
        }
...
})
Arium answered 20/11, 2009 at 16:26 Comment(5)
seems this could not prevent the click event for me, im using jquery 1.9.1 and jquery.ui 1.10.3Respire
weird that you answered your own question, and it is not even working, Sasha lol.Expend
@Respire this answer below works: https://mcmap.net/q/237743/-preventing-click-event-with-jquery-drag-and-dropPretension
Another simple answer - just place th jQuery .click() handler after .draggable() #18032636Ovoviviparous
if you pass the attribute helper with value 'clone', it avoids triggering the event to the dragged-sorted item.. {helper: 'clone', start, stop... etc},Zonate
W
91

A solution that worked well for me and that doesn't require a timeout: (yes I'm a bit pedantic ;-)

I add a marker class to the element when dragging starts, e.g. 'noclick'. When the element is dropped, the click event is triggered -- more precisely if dragging ends, actually it doesn't have to be dropped onto a valid target. In the click handler, I remove the marker class if present, otherwise the click is handled normally.

$('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
    }
});
Wallacewallach answered 26/3, 2010 at 19:16 Comment(10)
Useful! Didn't working for me in one go, probably because I was messing in previous attempts at the same thing and the classes ended up on a different element than the one clicked.. but anyway, I used a global variable (which on this extremely simple page was ok), and that worked quite well also.Pileous
This lead me to my solution, I just used the jQuery data function instead of a class. Thanks.Nga
but browser wont trigger click event each time you drop elementClimb
drag n drop won't interfere with click event if only a portion of the draggable responds to click events (child anchor elements for example) and the user drags from the portion that doesn't respond to click events (not on one of the anchors).Bibby
To save setting a class or data, in the click event handler you can also use: if (!$(this).is('.ui-draggable-dragging')) { ... click action }Hide
Oops, don't you want stop: function(event, ui) { $(this).removeClass('noclick'); } in the draggable handler? Otherwise won't every drag-n-drop somewhere else thwart the next click on 'your selector'.Forsworn
This works fine in Chrome and IE, but not in Firefox. Do you have any idea why?Naquin
No idea. It's some time ago, but I'm pretty sure it worked in Firefox when I wrote this answer.Wallacewallach
it might be better that instead of using an else clause you just have return void; after the removeClass. so it just prepends and does not have to wrap all the code currently built by peopleScheffler
Another solution that seems to work for me as well as others is simply placing the click event listener after the draggable() - though it might not work in every case... Source: #18032636Ovoviviparous
A
41

Solution is to add click handler that will prevent click to propagate on start of drag. And then remove that handler after drop is performed. The last action should be delayed a bit for click prevention to work.

Solution for sortable:

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

Solution for draggable:

...
.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);
        }
...
})
Arium answered 20/11, 2009 at 16:26 Comment(5)
seems this could not prevent the click event for me, im using jquery 1.9.1 and jquery.ui 1.10.3Respire
weird that you answered your own question, and it is not even working, Sasha lol.Expend
@Respire this answer below works: https://mcmap.net/q/237743/-preventing-click-event-with-jquery-drag-and-dropPretension
Another simple answer - just place th jQuery .click() handler after .draggable() #18032636Ovoviviparous
if you pass the attribute helper with value 'clone', it avoids triggering the event to the dragged-sorted item.. {helper: 'clone', start, stop... etc},Zonate
J
12

I had the same problem and tried multiple approaches and none worked for me.

Solution 1

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

does nothing for me. The item is being clicked after the dragging is done.

Solution 2 (by Tom de Boer)

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

This works just fine but fails in one case- when I was going fullscreen onclick:

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

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

This does not work for me.

Solution 4- the only one that worked just fine

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

Yep, that's it- the correct order does the trick- first you need to bind draggable() then click() event. Even when I put fullscreen toggling code in click() event it still didn't go to fullscreen when dragging. Perfect for me!

Jaella answered 28/3, 2015 at 12:44 Comment(2)
Only one that worked for me (in IE11/Edge) was solution 4. Thanks!Pincers
#4 is the correct and simplest solution for me, without using classes or variables.Spiracle
F
11

I'd like to add to this that it seems preventing the click event only works if the click event is defined AFTER the draggable or sortable event. If the click is added first, it gets activated on drag.

Fachini answered 15/3, 2010 at 13:34 Comment(0)
S
9

I don't really like to use timers or preventing, so what I did is this:

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..

Saxophone answered 15/11, 2012 at 8:48 Comment(1)
This was the best solution for me. I had trouble getting click events to work on a jquery UI sortable with in a mobile web browser using jquery.ui.touch.punch.js, but this solved it fairly elegantly.Armijo
A
4

lex82's version but for .sortable()

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

and you may only need:

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

and here's what I'm using for the toggle:

$("#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");
}
});
Anthemion answered 10/1, 2011 at 20:24 Comment(1)
It appears that jquery UI appends 'ui-sortable-helper' class to the item that is being sorted. So, you can leave out the 'noclick' class and just do if ($(this).hasClass('ui-sortable-helper')). More terse that wayCervin
F
3

A possible alternative for Sasha's answer without preventing default:

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);
        },
Foskett answered 9/11, 2012 at 19:10 Comment(0)
S
3

In jQuery UI, elements being dragged are given the class "ui-draggable-dragging".
We can therefore use this class to determine whether to click or not, just delay the event.
You don't need to use the "start" or "stop" callback functions, simply do:

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

This is triggered from "mouseup", rather than "mousedown" or "click" - so there's a slight delay, might not be perfect - but it's easier than other solutions suggested here.

Shellieshellproof answered 4/3, 2017 at 18:32 Comment(0)
F
2

In my case it worked like this:

$('#draggable').draggable({
  start: function(event, ui) {
    $(event.toElement).one('click', function(e) { e.stopPropagation(); });
  }
});
Flats answered 3/8, 2015 at 13:28 Comment(0)
H
1

After reading through this and a few threads this was the solution I went with.

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;
    }
});
Hindquarter answered 9/9, 2014 at 17:11 Comment(0)
J
1

the most easy and robust solution? just create transparent element over your draggable.

.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();
        }
    });
Ji answered 13/11, 2019 at 12:2 Comment(0)
S
0

Have you tried disabling the link using event.preventDefault(); in the start event and re-enabling it in the drag stopped event or drop event using unbind?

Showbread answered 20/11, 2009 at 16:29 Comment(0)
G
0

Just a little wrinkle to add to the answers given above. I had to make a div that contains a SalesForce element draggable, but the SalesForce element has an onclick action defined in the html through some VisualForce gobbledigook.

Obviously this violates the "define click action after the drag action" rule, so as a workaround I redefined the SalesForce element's action to be triggered "onDblClick", and used this code for the container 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');
    }
});

The parent's click event essentially hides the need to double-click the child element, leaving the user experience intact.

Gallice answered 15/5, 2013 at 15:1 Comment(0)
O
0

I tried like this:

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

});
Orthoscope answered 17/5, 2015 at 3:23 Comment(0)
Z
0

for me helped passing the helper in options object as:

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

Seems cloning dom element that is dragged prevented the bubbling of the event. I couldn´t avoid it with any eventPropagation, bubbling, etc. This was the only working solution for me.

Zonate answered 9/10, 2019 at 0:35 Comment(0)
C
0

The onmousedown and onmouseup events worked in one of my smaller projects.

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

Yes, I know. Not the cleanest way, but you get the idea.

Chummy answered 10/10, 2019 at 10:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.