11

I have a table on which I need to implement draggable header columns. I implemented it using Chrome as my browser, and everything worked fine. When I tested it in Firefox (17.0.1), I noticed that the drag event doesn't fire. dragstart does, though. I simplified the problem in the markup below. When loaded in Chrome, the top label updates each time the mouse moves while dragging. In Firefox it remains 0.

<!DOCTYPE html>
<html>
<head>
<title>TH Drag Test</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<style>
table,td,th {
    border: solid thin black;
}
</style>
<script>
    $(document).ready(function() {
        $("th").bind("drag", function(event) {
            $("#lbl").html(event.originalEvent.offsetX);
        });
    });
</script>
</head>
<body>
    <span id="lbl">0</span>
    <table>
        <thead>
            <tr>
                <th draggable="true">Column A</th>
                <th draggable="true">Column B</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>One</td>
                <td>Two</td>
            </tr>
            <tr>
                <td>Three</td>
                <td>Four</td>
            </tr>
        </tbody>
    </table>
</body>
</html>
Jared
  • 2,043
  • 5
  • 33
  • 63
  • I can confirm this behavior on my system as well with latest Chrome and FF. Have you tried using an older FF version? Lately, FF in my experience often drops some features when hopping from one "major" version to another. – mkey Dec 17 '12 at 19:40

3 Answers3

6

I had nightmares fixing this issue for Firefox. I needed to drag a div onto a diary and detect the coordinates of the drop so I knew which date and time the user had selected.

To get the drag event to fire I added the below line to the dragstart event handler:

event.dataTransfer.setData('Text', this.id);

However, the hardest thing to work out was how to get the x and y coordinates when the drag ended, as these are not returned in the dragend event handler in Firefox. I tried using mouse events as mentioned above, but I found that these did not work while the drag is in progress and are only called after the dragend event handler has been called. So, I only used the dragend event to detect when the user had released the div, and then used the next mouse moved event to get the coordinates and do any other work that is required. I found this works in IE, Firefox and Chrome. Here is the html / javascript of a demo:

 <div>
<div id = "todrag" class = "testdiv" draggable="true"><p>Please drag me</p></div>

<div id = "destination" class = "testdiv"><p>To here</p></div>
<p id = "coords"></p>
<p id = "compareords"></p>
</div>

<script>
    var down = true;
    var m_xcoordDrag = 0;
    var m_ycoordDrag = 0;
    var m_xcoordMove = 0;
    var m_ycoordMove = 0;
    var m_dragReleased = false;
    var m_coordselement = document.getElementById("coords");
    var m_compareordselement = document.getElementById("compareords");

    function OnMouseMove(e) {
        m_xcoordMove = e.x;
        m_ycoordMove = e.y;
        m_coordselement.innerHTML = e.x + "," + e.y;

        if (m_dragReleased) {
            m_compareordselement.innerHTML = "X:" + m_xcoordDrag + ", " + m_xcoordMove + " Y:" + m_ycoordDrag + ", " + m_ycoordMove;

            m_dragReleased = false;
        }
    }

    dragstart = function (event) {

        event.dataTransfer.setData('Text', this.id);
        stop = false;
    }

    dragend = function (event) {
        m_dragReleased = true;

        m_xcoordDrag = event.x;
        m_ycoordDrag = event.y;
    }

    document.onmousemove = OnMouseMove;

    var toDrag = document.getElementById("todrag");

    toDrag.addEventListener('dragstart', dragstart);
    toDrag.addEventListener('dragend', dragend);

</script>

I hope this helps!

  • 1
    This works. Side effect is that setting text data will cause the browser to use a default handler for the drop event. If the goal is to trigger the drag event but not use browser default drop handler choose a non-standard data type other than `Text` – prototype Oct 31 '16 at 14:58
  • "setting text data will cause the browser to use a default handler ... to ... not use browser default drop handler choose a non-standard data type other than Text". I was having this exact problem. I wish this was an answer so I could upvote. Ended up using "weorijwefij" instead of "text" and it works how it's supposed to now. – James M. Lay Oct 24 '17 at 18:25
5

The bit that has been cut out http://pastebin.com/bD2g3SqL

EDIT:

This does work, however I'm yet to find a way to access the offsetX and offsetY properties, for some reason FF version of the event does not contain them.

<!DOCTYPE html>
<html>
<head>
<title>TH Drag Test</title>
<style>
table,td,th {
    border: solid thin black;
}
</style>
<script>
    function Init(){
        var n= document.getElementsByTagName("th");
        var j=0;

        for (var i=0; i<n.length; i++){
            n[i].addEventListener('drag', function (e){
                document.getElementById("lbl").textContent= j++;
            }, false);
        }

        for (var i=0; i<n.length; i++){
            n[i].addEventListener('dragstart', function (e){
                e.dataTransfer.setData('text/plain', 'node');
            }, false);
        }
    }
</script>
</head>
<body onload="Init();">
    <span id="lbl"></span>
    <table>
        <thead>
            <tr>
                <th draggable="true">Column A</th>
                <th draggable="true">Column B</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>One</td>
                <td>Two</td>
            </tr>
            <tr>
                <td>Three</td>
                <td>Four</td>
            </tr>
        </tbody>
    </table>
</body>
</html>

Apparently, what you need to do is to "initialize" the drag (source.)

EDIT2:

Apparently, there's a bug in the drag event (go figure) which does not update the clientX and clientY properties (source.) They are updated on some other events, like the dragover event, however that event will fire only while the object is being dragged over a plausible drop target. A way out of such a silly situation would be something as crude as this:

<!DOCTYPE html>
<html>
<head>
<title>TH Drag Test</title>
<style>
table,td,th {
    border: solid thin black;
}
</style>
<script>    
    var down= false;

    document.onmousemove= OnMouseMove;

    function Init(){
        var n= document.getElementsByTagName('th');

        for (var i=0; i<n.length; i++){
            n[i].onmousedown= OnMouseDown;
        }

        document.onmouseup= OnMouseUp;
    }

    function OnMouseDown(e){
        down= true;
    }

    function OnMouseUp(e){
        down= false;
    }

    function OnMouseMove(e){
        if (!down) return;

        document.getElementById('lbl').textContent= e.pageX ? ('x: ' + e.pageX + ' y: ' + e.pageY) : ('x: ' + (e.clientX + document.documentElement.scrollLeft + document.body.scrollLeft) + ' y: ' + (e.clientY + document.documentElement.scrollTop + document.body.scrollTop));
    }

</script>
</head>
<body onload="Init();">
    <span id="lbl"></span>
    <table>
        <thead>
            <tr>
                <th draggable="true">Column A</th>
                <th draggable="true">Column B</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>One</td>
                <td>Two</td>
            </tr>
            <tr>
                <td>Three</td>
                <td>Four</td>
            </tr>
        </tbody>
    </table>
</body>
</html>
jonhopkins
  • 3,844
  • 3
  • 27
  • 39
mkey
  • 535
  • 2
  • 12
  • 2
    I killed *two* kittens because of your inline javascript. –  Dec 17 '12 at 20:09
  • 3
    Well, the important thing to remember here is that you are the one who killed them. Also, [this](http://www.thefreedictionary.com/test). – mkey Dec 17 '12 at 20:12
  • 1
    I just tested all of these in Firefox portable 3.6.28 and none of them work as expected. Something is rotten in the state of Denmark. – mkey Dec 17 '12 at 20:16
  • Yes indeed. You need to call dataTransfer.setData in order for the drag event to be fired. I didn't notice that in the FF docs anywhere – Jared Dec 17 '12 at 22:37
  • Yep, thanks for digging this issue up, I just know something like this would turn up when I needed it the least :) – mkey Dec 17 '12 at 22:38
  • Yeah and unfortunately for me, FF provides no data about the mouse coordinates in the drag event, so I have to put in a hack of a document-level "dragover" event listener. – Jared Dec 18 '12 at 17:33
  • 1
    `count_of_years_and_its_still_as_bad_as_it_used_to_be += 2` – Dan Abramov Nov 02 '14 at 23:17
1

Firefox requires 'something' (that we call 'init' here) to be set in dragstart event to initialize the rest of drag events to occur.

This is probably because all of the DOM elements are draggable="true" by default in XUL. (reference: https://bugzilla.mozilla.org/show_bug.cgi?id=646823#c4)

Example:

<div id="something" draggable="true" ondragstart="event.dataTransfer.setData('text/plain', 'node');">Drag me</div>

Chrome doesn't require such 'initialization'.

nenadg
  • 420
  • 11
  • 16