score:14

From my point of view, d3.js is terrible documented library. Sorry Mike Bostock, but personally for me it's sometimes absolutely impossible to find "why", without debugging the source and examples.

Now, the answer:

THE FIRST

d3 can create and recognize own events like d3.event.x properly in any browser, BUT you should draw draggable interface elements INSIDE <g></g>! You should not place circles, rectangles or whatever you going to drag later precisely in <svg></svg>. This is not documented.

Example:

<svg>
   <rect></rect>  // you can not use 'd3.event.x' or 'd3.event.y' for this element.
                  // for this element is possible to use 'd3.event.sourceEvent.x' in Chrome 
                  // and 'd3.event.sourceEvent.offsetX' in Firefox.
                  // d3.event.x === NaN, d3.event.y === NaN in any browser
   <g>
      <rect></rect> //  'd3.event.x' and 'd3.event.y' will work for this element
   </g>
<svg>

THE SECOND

The <g></g> wrapper should store the data([{x:..., y:...}]) even you not need it. Without the data in <g></g>, the .origin() method will not work properly and element will jump unpredictably under cursor over the window.

FINALLY

Lets combine all knowledge about dragging in one example:

    var draggable = d3.behavior.drag()
            .origin(Object) // the sequence function(d){return(d);} in not necessary 
            .on("drag", drg);

    var board = d3.select("body").append("svg:svg").attr({width: 500, height: 500});
    var wrap = board.append("svg:g")
            .data([{x: 100, y: 100}]); // this is the coordinates of draggable element
                                       // they should be stored in <g></g> wrapper as the data

// IMPORTANT! There is no wrap.data().enter() sequence
// You should not call .enter() method for <g></g> data,
// but nested elements will read the data() freely 

    var handle = wrap.append("svg:circle")
            .attr({
                cx: function(d){return(d.x);}, // position of circle will be stored in the data property of <g> element
                cy: function(d){return(d.x);}, 
                r: 30, 
                fill: "gray", 
                stroke: "black"
            }).call(draggable);

    function drg(d){
        // now d3.event returns not a mouse event, 
        // but Object {type: "drag", x: ..., y: ..., dx: ..., dy: ...}
        d3.select(this).attr({
            // reposition the circle and
            // update of d.x, d.y
            cx: d.x = Math.max(0, Math.min(500 - 60, d3.event.x)), // 500 - width of svg, 60 - width of circle  
            cy: d.y = Math.max(0, Math.min(500 - 60, d3.event.y))  // 500 - height of svg, 60 - height of circle
        });        
    }

DEMO: http://jsfiddle.net/c8e2Lj9d/4/


Related Query

More Query from same tag