Reputation: 637
I have the following D3 code, but I'm unable to drag the rect element successfully. (please ignore bad indentation) I'm unable to understand what I'm doing wrong here.
var svg = d3.select('#slider').append('svg')
.attr('width', width)
.attr('height', height);
var drag = d3.behavior.drag()
.origin(function (d) {
return d;
})
.on("dragstart", dragstarted)
.on("drag", dragged);
//Called when drag event starts. It stop the propagation of the click event
function dragstarted(d) {
d3.event.sourceEvent.stopPropagation();
}
//Called when the drag event occurs (object should be moved)
function dragged(d) {
d.x = d3.event.x;
d.y = d3.event.y;
//Translate the object on the actual moved point
d3.select(this).attr({
transform: "translate(" + d.x + "," + d.y + ")"
});
};
svg.append('rect')
.attr('width', 500)
.attr('height', 250)
.attr('x', 10)
.attr('y', 50)
.attr('fill', "transparent")
.attr('stroke', "orange")
.attr('stroke-width', 2)
.append("title")
.text(function (d) {
return "Zone 1";
})
.call(drag);
Upvotes: 1
Views: 519
Reputation: 21578
There are some issues:
You are not binding any data to your <rect>
. Your callbacks for .origin()
and .on()
will fail when trying to refer to that data via their parameter d
. You could set up an object with your <rect>
s configuration which may be used throughout your code.
// Configuration for rect
var rect = {
x: 50,
y: 50,
w: 500,
h: 250,
t: "Zone 1"
};
svg.selectAll("rect")
.data([rect]) // bind your config to the rect to be appended
.enter().append('rect')
.attr('width', function(d) { return d.w; })
.attr('height', function(d) { return d.h; })
This way it is bound to your <rect>
and will therefore be accessible even from within the drag handler. This is a very common pattern in D3.js.
You are calling .drag()
after appending the <title>
element to the <rect>
, which adds the drag behaviour to the title instead of to the <rect>
. To add the drag to the <rect>
you have to move it up the call chain.
svg.selectAll("rect").data([rect]).enter().append('rect')
// ...
.call(drag)
.append("title")
.text(function(d){return d.t;});
To position your <rect>
you are using a combination of a) its attributes x
and y
and b) a transformation when setting translate
while handling the drag's offset. Mixing both methods will usually screw things up. You should choose either one of those methods and then consistently stick to it.
I have set up a working snippet with some more comments explaining the changes.
var width = 10000,
height = 10000;
var svg = d3.select('body').append('svg')
.attr('width', width)
.attr('height', height);
var drag = d3.behavior.drag()
.origin(function(d) { return d; }) // this will access the bound config accessing its x and y
.on("dragstart", dragstarted)
.on("drag", dragged);
//Called when drag event starts. It stop the propagation of the click event
function dragstarted(d) {
d3.event.sourceEvent.stopPropagation();
}
//Called when the drag event occurs (object should be moved)
function dragged(d) {
d.x = d3.event.x; // update x of the config object
d.y = d3.event.y; // update y of the config object
// Set new x and y on drag
d3.select(this).attr({
"x": d.x,
"y": d.y
});
};
// Configuration for rect
var rect = {
x: 50,
y: 50,
w: 500,
h: 250,
t: "Zone 1"
};
svg.selectAll("rect")
.data([rect])
.enter().append('rect')
.attr('width', function(d) { return d.w; })
.attr('height', function(d) { return d.h; })
.attr('x', function(d) { return d.x; })
.attr('y', function(d) { return d.y; })
.attr('fill', "transparent")
.attr('stroke', "orange")
.attr('stroke-width', 2)
.call(drag) // call this before the title is appended
.append("title")
.text(function(d) {
return d.t;
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
Upvotes: 2