Can you detect "dragging" in jQuery?
Asked Answered
O

15

118

I have a throbber that is to appear when a user clicks a link.

The problem is is that that same link can be clicked and dragged to be rearranged. In this case, I wouldn't need the throbber to appear. It only needs to appear if its actually waiting to go somewhere.

How can I, with jQuery, create an event listener that would only allow a throbber to appear if its a click through to a link, and not a click and drag?

Overflow answered 8/11, 2010 at 19:11 Comment(2)
Perhaps OOT? I've tried several approaches for drag and drop with jQuery incluing jQuery UI, my own code with mousedown, mouseup, mousemove events and the one that has worked best is github.com/haberman/jdragdrop.Dorwin
Here's another simple solution without any heavy framework. blog.blakesimpson.co.uk/read/…Bubal
C
239

On mousedown, start set the state, if the mousemove event is fired record it, finally on mouseup, check if the mouse moved. If it moved, we've been dragging. If we've not moved, it's a click.

var isDragging = false;
$("a")
.mousedown(function() {
    isDragging = false;
})
.mousemove(function() {
    isDragging = true;
 })
.mouseup(function() {
    var wasDragging = isDragging;
    isDragging = false;
    if (!wasDragging) {
        $("#throbble").toggle();
    }
});

Here's a demo: http://jsfiddle.net/W7tvD/1399/

Chirrup answered 9/11, 2010 at 23:39 Comment(4)
hey..can you help me with this #19704117Hentrich
You might want to add a drag threshold so your clicks aren't interpreted as drags on the "a" element. Just look for a certain amount of change in an x and y mousemove event.Concentre
Awesome, right after the wind unbind mousemove i put my code to act as soon as the start of dragging was detected. thanks you!Foot
I've found that in some cases and/or browsers, mousemove will still be fired at least once even without any movement, so I changed it to increment the variable on movement and then check for a certain threshold on mouseup jsfiddle.net/W7tvD/1649 Works like a charm for me now, thanks!Lossa
T
28

For some reason, the above solutions were not working for me. I went with the following:

$('#container').on('mousedown', function(e) {
    $(this).data('p0', { x: e.pageX, y: e.pageY });
}).on('mouseup', function(e) {
    var p0 = $(this).data('p0'),
        p1 = { x: e.pageX, y: e.pageY },
        d = Math.sqrt(Math.pow(p1.x - p0.x, 2) + Math.pow(p1.y - p0.y, 2));

    if (d < 4) {
        alert('clicked');
    }
})

You can tweak the distance limit to whatever you please, or even take it all the way to zero.

Tetreault answered 5/2, 2013 at 15:46 Comment(3)
Best answer, helped me a lot. Just a thought: Math.sqrt isn't really necessary, better working with square distances (sqrt is slow, could affect UX).Aynat
sqrt is not slow and will not affect UX, not even if you did it hundreds of times.Nebula
Yeah, worry about unnecessary square root in the inner loop of your physics engine, not your jquery click handler...Rolland
L
26

With jQuery UI just do this!

$( "#draggable" ).draggable({
  start: function() {

  },
  drag: function() {

  },
  stop: function() {

  }
});
Laurettelauri answered 7/4, 2016 at 19:43 Comment(1)
You need jquery-ui, but it's the easiest way.Turfman
B
5
$(".draggable")
.mousedown(function(e){
    $(this).on("mousemove",function(e){
        var p1 = { x: e.pageX, y: e.pageY };
        var p0 = $(this).data("p0") || p1;
        console.log("dragging from x:" + p0.x + " y:" + p0.y + "to x:" + p1.x + " y:" + p1.y);
        $(this).data("p0", p1);
    });
})
.mouseup(function(){
    $(this).off("mousemove");
});

This solution uses the "on" and "off" functions to bind an unbind a mousemove event (bind and unbind are deprecated). You can also detect the change in mouse x and y positions between two mousemove events.

Billups answered 11/6, 2015 at 9:33 Comment(1)
the issue with this solution is now on every mousedown there will be another event handler added onto this causing the body of that function to execute several times increasingly.Iolanthe
B
3

Try this: it shows when is 'dragged' state. ;) fiddle link

$(function() {
    var isDragging = false;
    $("#status").html("status:");
    $("a")
    .mousedown(function() {
        $("#status").html("status: DRAGGED");        
    })
    .mouseup(function() {
        $("#status").html("status: dropped");   
    });

    $("ul").sortable();
});
Boutin answered 18/10, 2013 at 15:5 Comment(0)
U
2
// here is how you can detect dragging in all four directions
var isDragging = false;
$("some DOM element").mousedown(function(e) {
    var previous_x_position = e.pageX;
    var previous_y_position = e.pageY;

    $(window).mousemove(function(event) {
        isDragging = true;
        var x_position = event.pageX;
        var y_position = event.pageY;

        if (previous_x_position < x_position) {
            alert('moving right');
        } else {
            alert('moving left');
        }
        if (previous_y_position < y_position) {
            alert('moving down');
        } else {
            alert('moving up');
        }
        $(window).unbind("mousemove");
    });
}).mouseup(function() {
    var wasDragging = isDragging;
    isDragging = false;
    $(window).unbind("mousemove");
});
Uttasta answered 27/5, 2015 at 11:46 Comment(0)
S
2

Make sure you set the element's draggable attribute to false so you don't have side effects when listening to mouseup events:

<div class="thing" draggable="false">text</div>

Then, you can use jQuery:

$(function() {
  var pressed, pressX, pressY,
      dragged,
      offset = 3; // helps detect when the user really meant to drag

  $(document)
  .on('mousedown', '.thing', function(e) {
    pressX = e.pageX;
    pressY = e.pageY;
    pressed = true;
  })
  .on('mousemove', '.thing', function(e) {
    if (!pressed) return;
    dragged = Math.abs(e.pageX - pressX) > offset ||
              Math.abs(e.pageY - pressY) > offset;
  })
  .on('mouseup', function() {
    dragged && console.log('Thing dragged');
    pressed = dragged = false;
  });
});
Schoolfellow answered 5/3, 2017 at 14:51 Comment(0)
P
2

I branched off from the accepted answer to only run when the click is being HELD down and dragged.

My function was running when I wasn't holding the mouse down. Here's the updated code if you also want this functionality:

var isDragging = false;
var mouseDown = false;

$('.test_area')
    .mousedown(function() {
        isDragging = false;
        mouseDown = true;
    })
    .mousemove(function(e) {
        isDragging = true;

        if (isDragging === true && mouseDown === true) {
            my_special_function(e);
        }
     })
    .mouseup(function(e) {

        var wasDragging = isDragging;

        isDragging = false;
        mouseDown = false;

        if ( ! wasDragging ) {
            my_special_function(e);
        }

    }
);
Perkin answered 21/12, 2018 at 2:49 Comment(0)
E
1

For this simplest way is touch start, touch move and touch end. That is working for both PC and touch device just check it in jquery documentation and hope this is the best solution for you. good luck

Esoteric answered 9/8, 2013 at 6:26 Comment(0)
S
1

jQuery plugin based on Simen Echholt's answer. I called it single click.

/**
 * jQuery plugin: Configure mouse click that is triggered only when no mouse move was detected in the action.
 * 
 * @param callback
 */
jQuery.fn.singleclick = function(callback) {
    return $(this).each(function() {
        var singleClickIsDragging = false;
        var element = $(this);

        // Configure mouse down listener.
        element.mousedown(function() {
            $(window).mousemove(function() {
                singleClickIsDragging = true;
                $(window).unbind('mousemove');
            });
        });

        // Configure mouse up listener.
        element.mouseup(function(event) {
            var wasDragging = singleClickIsDragging;
            singleClickIsDragging = false;
            $(window).unbind('mousemove');
            if(wasDragging) {
                return;
            }

            // Since no mouse move was detected then call callback function.
            callback.call(element, event);
        });
    });
};

In use:

element.singleclick(function(event) {
    alert('Single/simple click!');
});

^^

Scoter answered 22/12, 2014 at 19:24 Comment(0)
F
1

Late to the party, but this code also detects touch events (mobile, tablet).

$(".draggable").on('touchstart mousedown', function(ev) {
    ev.preventDefault();
    $(this).on("touchmove mousemove",function(e){
        var x = e.pageX || e.changedTouches[0].pageX;
        var y = e.pageY || e.changedTouches[0].pageY;
        var p1 = { x: x, y: y };
        var p0 = $(this).data("p0") || p1;
        console.log("dragging from x:" + p0.x + " y:" + p0.y + "to x:" + p1.x + " y:" + p1.y);
        $(this).data("p0", p0);
    });
}).on('touchend mouseup', function(ev) {
    ev.preventDefault();
    $(this).off("touchmove mousemove");
});
Fireweed answered 30/8, 2022 at 23:21 Comment(0)
P
0

You've need to set a timer. When the timer times out, start the throbber and register the click. When drag occurs, clear the timer so it never completes.

Pops answered 8/11, 2010 at 19:22 Comment(2)
..very clever, how do you detect a drag?Overflow
when you set up the Draggable, there should be an onDrag event. Hook it up there.Pops
C
0

You don`t have to setup variable, you can just set if it is moving in data-attribute

$youtubeSlider.find('a')
    .on('mousedown', function (e) {
        $(this).data('moving', false);
    })
    .on('mousemove', function (e) {
        $(this).data('moving', true);
    })
    .on('mouseup', function (e) {
        if (!$(this).data('moving')) {
            // Open link
        }
    });
Check answered 2/9, 2016 at 9:50 Comment(0)
W
0

I needed a function that always keeps track of mouse position and detect left-, right-, top-, bottom- dragging. It also does not trigger on click but needs a minimum of 15px move

/**
 * Check for drag when moved minimum 15px
 * Same time keep track of mouse position while dragging
 */
// Variables to be accessed outside in other functions
var dragMouseX;
var dragMouseY;
var myDragging = false; // true or false
var dragDirectionX = false; // left or right
var dragDirectionY = false; // top or bottom

$(document).on("mousedown", function(e) {
    // Reset some variables on mousedown
    var lastDirectionCheck = e.timeStamp;
    var dragStartX = e.pageX;
    var dragStartY = e.pageY;
    dragMouseX = e.pageX;
    dragMouseY = e.pageY;
    myDragging = false;
    dragDirectionX = false;
    dragDirectionY = false;

    // On the move
    $(document).on("mousemove", function(e) {
        dragMouseX = e.pageX;
        dragMouseY = e.pageY;

        // Recalculate drag direction every 200ms in case user changes his mind
        if (e.timeStamp > (lastDirectionCheck + 200)) {
            dragStartX = dragMouseX;
            dragStartY = dragMouseY;
            lastDirectionCheck = e.timeStamp;
        }

        // Check for drag when moved minimum 15px in any direction
        if (!myDragging && Math.abs(dragStartX - dragMouseX) > 15 || Math.abs(dragStartY - dragMouseY) > 15) {
            myDragging = true;
        }
        if (myDragging) {
            // Check drag direction X
            if (dragStartX > dragMouseX) dragDirectionX = 'left';
            if (dragStartX < dragMouseX) dragDirectionX = 'right';

            // Check drag direction Y
            if (dragStartY > dragMouseY) dragDirectionY = 'top';
            if (dragStartY < dragMouseY) dragDirectionY = 'bottom';

            // console.log(dragDirectionX + ' ' + dragDirectionY);
        }
    });
});

// Reset some variables again on mouseup
$(document).on("mouseup", function() {
    $(document).off("mousemove");
    myDragging = false;
    dragDirectionX = false;
    dragDirectionY = false;
});
Will answered 12/6, 2019 at 8:25 Comment(0)
H
-1

If you're using jQueryUI - there is an onDrag event. If you're not, then attach your listener to mouseup(), not click().

Hyperkinesia answered 8/11, 2010 at 20:48 Comment(4)
How would mouseup differentiate between a drag and a click?Overflow
Hmmm - yeah I guess I didn't think it through enough. Perhaps you can have one listener attached to mousedown that records the coordinates of the mouse. Then if the coordinates of the mouse on mouseup are the same (or really close), consider it a click through.Hyperkinesia
This does not work: #12231647Simferopol
The answer is also 4 years old - consider that.Hyperkinesia

© 2022 - 2024 — McMap. All rights reserved.