66

I have a jQuery UI Sortable list. The sortable items also have a click event attached. Is there a way to prevent the click event from firing after I drag an item?

$().ready( function () { 
 $('#my_sortable').sortable({
   update: function() { console.log('update') },
   delay: 30
 });    

 $('#my_sortable li').click(function () {    
   console.log('click');
 });                        

});
#my_sortable li {
          border: 1px solid black;
          display: block;
          width: 100px;
          height: 100px;    
          background-color: gray;
        }
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.1/jquery-ui.min.js"></script>

<ul id="my_sortable">                 
  <li id="item_1">A</li>
  <li id="item_2">B</li>
  <li id="item_3">C</li>
</ul>   
Calvin Nunes
  • 6,376
  • 4
  • 20
  • 48
Patrick McElhaney
  • 57,901
  • 40
  • 134
  • 167
  • 2
    Now that browsers and jqueryui have had some time to evolve (compensate?) this behavior is only evident in Firefox (47.0). Modern Chrome and IE do not fire the `click` event after `sortable`. – veeTrain Jun 10 '16 at 14:28

10 Answers10

143

I had the same problem and since my sortable items contained three or four clickable items (and the number was variable) binding/unbinding them on the fly didn't really seem an option. However, by incident I specified the

helper : 'clone'

option, which behaved identically to the original sortable in terms of interface but apparently does not fire click events on the dragged item and thus solves the problem. It's as much a hack as anything else, but at least it's short and easy..

Elte Hupkes
  • 2,773
  • 2
  • 23
  • 18
  • 1
    [Levi Crain](http://stackoverflow.com/users/577743/levi-crain) says: "the "helper : 'clone'" trick doesn't work in chrome. The onclick event doesn't fire ever." – Patrick McElhaney Jan 16 '11 at 21:24
  • Worked like a charm in Firefox 16! Thank you – pawelglow Nov 18 '12 at 01:18
  • Definitely the best answer! I love simple solutions. The code in the previous answer (selected answer) is clever, but challenging to maintain long term, especially with new programmers trying to figure out clever code. – Nick Johnson Dec 06 '12 at 17:25
  • I had a problem with combining sortable and Magnific Popup. After dragging an element, the popup would show. This was like a gift from God, after I struggled with the problem for a couple of hours. – Marian Nov 20 '13 at 08:51
  • IMHO It's **not** a hack, it's just a smart solution: `helper` defines the DOMElement to show in drag. `clone` uses a clone of the "in-sort-element". Hence, using a clone, actually breaks the "click" check of down+up, then the click event is not fired. So I think this solution is licit and safe. – Niki Romagnoli Feb 20 '17 at 07:57
13

If you have a reference to the click event for your li, you can unbind it in the sortable update method then use Event/one to rebind it. The event propagation can be stopped before you rebind, preventing your original click handler from firing.

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
   "http://www.w3.org/TR/html4/strict.dtd">

<html lang="en">
<head> 


  <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
  <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.1/jquery-ui.min.js"></script>

  <script type="text/javascript" charset="utf-8">
    var myClick = function () {
        console.log('click');
    };

    $().ready( function () { 

       $('#my_sortable').sortable({
         update: function(event, ui) { 
            ui.item.unbind("click");
            ui.item.one("click", function (event) { 
                console.log("one-time-click"); 
                event.stopImmediatePropagation();
                $(this).click(myClick);
            }); 
            console.log('update') },
         delay: 30
       });    


       $('#my_sortable li').click(myClick);                        

     });
  </script>

  <style type="text/css" media="screen">
    #my_sortable li {
      border: 1px solid black;
      display: block;
      width: 100px;
      height: 100px;    
      background-color: gray;
    }
  </style>

</head>
<body>

      <ul id="my_sortable">                 
        <li id="item_1">A</li>
        <li id="item_2">B</li>
        <li id="item_3">C</li>
      </ul>   

</body>
</html>
Patrick McElhaney
  • 57,901
  • 40
  • 134
  • 167
Andrew Champion
  • 549
  • 4
  • 10
3

If you for some reason don't want to use the helper:'clone' trick, this worked for me. It cancels the click event on an item that is dragged. jQuery adds the class ui-sortable-helper to the dragged element.

$('.draggable').click(clickCancelonDrop);
function clickCancelonDrop(event) {
    var cls = $(this).attr('class');
    if (cls.match('ui-sortable-helper'))
         return event.stopImmediatePropagation() || false;
}
Erik Gillespie
  • 3,929
  • 2
  • 31
  • 48
Fredrik Harloff
  • 151
  • 1
  • 6
2
$('.selector').draggable({
    stop: function(event, ui) {
        // event.toElement is the element that was responsible
        // for triggering this event. The handle, in case of a draggable.
        $( event.toElement ).one('click', function(e){ e.stopImmediatePropagation(); } );
    }
});

This works because "one-listeners" are fired before "normal" listeners. So if a one-listener stops propagation, it will never reach your previously set listeners.

Tom de Boer
  • 954
  • 8
  • 8
  • 1
    Problem is if the browser never fires the click event during drag, this will prevent the first 'real' click event – SimplGy Jul 30 '13 at 20:12
  • For Firefox 42.0, I cannot confirm that `on` listeners fired after `one` listeners. In fact, my `on` listener is still invoked priorly to my `one` listener. – theV0ID Nov 17 '15 at 15:20
2

We can also use a flag on the stop event that and check that flag on the click event.

var isSortableCalled = false;

$('#my_sortable').sortable({
         stop: function(event, ui){
              isSortableCalled = true;
         },
         update: function() { console.log('update') },
         delay: 30
});

$('#my_sortable li').click(function () {    
       if(!isSortableCalled){
            console.log('click');
       }
       isSortableCalled = false;

});
Saurabh
  • 137
  • 1
  • 13
1

The answer by mercilor worked for me a couple of caveats. The click event was actually on the handle element rather than the sorted item itself. Unfortunately the ui object, doesn't give you a reference to the handle in the update event (feature request to jquery ui?). So I had to get the handle myself. Also, I had to call preventDefault as well to stop the click action.

update: function(ev, ui) {
    var handle = $(ui.item).find('h3');
    handle.unbind("click");
    handle.one("click", function (event) {
                            event.stopImmediatePropagation();
                            event.preventDefault();
                            $(this).click(clickHandler);
                        });
    // other update code ...
Marco
  • 1,471
  • 11
  • 17
1

Thanks to Elte Hupkus;

helper: 'clone' 

I have implemented the same and a sample is shown below.

$(document).ready(function() {
$("#MenuListStyle").sortable({
 helper:'clone',
 revert:true
}).disableSelection();
});
Community
  • 1
  • 1
Tijo Tom
  • 487
  • 6
  • 13
1

Easier, use a var to know when the element is being sorted...

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
   "http://www.w3.org/TR/html4/strict.dtd">

<html>
<head> 


  <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
  <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.1/jquery-ui.min.js"></script>

  <script type="text/javascript" charset="utf-8">
    $().ready( function () {    
       $('#my_sortable').sortable({
         start: function() {
            sorting = true;
         },
         update: function() {
            console.log('update');
            sorting = false;
         },
         delay: 30
       });    


       $('#my_sortable li').click(function () {
         if (typeof(sorting) == "undefined" || !sorting) {
            console.log('click');
         }
       });                        

     });
  </script>

  <style type="text/css" media="screen">
    #my_sortable li {
      border: 1px solid black;
      display: block;
      width: 100px;
      height: 100px;    
      background-color: gray;
    }
  </style>

</head>
<body>

      <ul id="my_sortable">                 
        <li id="item_1">A</li>
        <li id="item_2">B</li>
        <li id="item_3">C</li>
      </ul>   

</body>
</html>
ragnar
  • 19
  • 2
0

One solution is to use live() instead of normal binding, but Elte Hupkes solution rocks!!!!

Bakaburg
  • 3,165
  • 4
  • 32
  • 64
-1
$('.menu_group tbody a').click(function(){
    link = $(this).attr('href');
    window.location.href = link;
});

This solution seems to be working for me. Now i can click on clickables inside sortable elements.

Note: ".menu_group tbody" is .sortable();

Riki137
  • 2,076
  • 2
  • 23
  • 26