D3: d3.behavior.drag - javascript

I'm trying this example and I applied the d3.behavior.drag with the function
var drag = d3.behavior.drag()
.on("drag", function(d,i) {
d.x += d3.event.dx
d.y += d3.event.dy
d3.select(this).attr("transform", function(d,i){
return "translate(" + [ d.x,d.y ] + ")"
})
});
Please see my example here.
My problem is after dragging the svg.
When I click on an element the zoom isn't well apllied.
For instance, the root disappear...
How can I fix this situation?
Thanks,
Carlos.

The problem is that when you try to drag the elements the click event is also fired and both the event handlers are getting executed.
You need to ignore the click event if it was suppressed (i.e. when you are dragging).
Modify your click event handler as
function click(d) {
// Ignore the click event if it was suppressed
if (d3.event.defaultPrevented){
return;
}
path.transition()
.duration(750)
.attrTween("d", arcTween(d));
};

Given in your case that an element can be dragged and clicked, in addition to prashant's answer, you'll want to suppress the click on drag as follows:
drag.on("dragstart", function() {
d3.event.sourceEvent.stopPropagation(); // silence other listeners
});
See http://bl.ocks.org/mbostock/6123708 for an example :-)

use this,
d3.event.sourceEvent.stopPropagation();

Related

How to implement a transition duration using D3.js?

A text box should pop up upon mouseenter. It currently works, however there's no gradual transition, its instantaneous.
Here's my code so far:
let svgTarget = document.querySelector('svg #target' + newId);
let tr = d3.transition()
.duration(5000)
.ease(d3.easeLinear);
svgTarget.addEventListener('mouseenter', function (e) {
d3.select('svg #tooltip' + newId)
.transition(tr)
.duration(8000)
.ease(d3.easeLinear)
.style("display", "inline");
},)
The mouseenter functionality works and displays the box but again it is instantaneous. Any ideas or suggestions? Thanks!
To add a listener, use selection.on().
Like a lot of things in D3, event listeners act on selections:
d3.select('svg #tooltip' + newId)
.on('mouseenter', function() {
d3.select(this) // this being the prior selection
.transition(tr)
.duration(8000)
.ease(d3.easeLinear)
.style("display", "inline");
});

Set only one node (root) note to be draggable in d3 force layout

I'm working on a d3 force layout graph, with looks pretty like this.
What I want is the root node to be fixed not draggable. I fixed the root node in the json file by adding
"fixed": true
but it is still draggable. In my JS file there is the code
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.on("click", click)
.call(force.drag);
If I remove the last line of this code, the whole graph isn't draggable anymore. I think 'force' is a global variable and determines, that the whole graph is draggable. But I want only the root node not to be draggable and the should be draggable. How can I do that?
You have to remove the drag event from the node you wish to stay fixed.
Here is an example : http://jsfiddle.net/qvco2Ljy/112/
I have looped through the data to give the first element a fixed attribute and also a donotmove attribute as when you drag and let go, perhaps you want the node to stay fixed (for children i mean), so using d.fixed here will cause a conflict :
graph.nodes.forEach(function(d, i) {
d.donotmove = false;
if (i == 0) {d.donotmove = true; d.fixed = true;}
})
And when calling drag events, check for the donotmove attribute like so :
var drag = d3.behavior.drag()
.origin(function(d) {
return d;
})
.on("dragstart", function(d) {
if (d.donotmove) return;
dragstarted(d)
})
.on("drag", function(d) {
if (d.donotmove) return;
dragged(d)
})
.on("dragend", function(d) {
if (d.donotmove) return;
dragended(d)
})
Hope that helps :)

Running functions after a drag occurs?

I'm trying to run a block of code after a drag occurs in my program. I had thought that the following would work:
//behavior for a dragged point
var drag = d3.behavior.drag()
.origin(function (d) {
return d;
})
.on("drag", dragmove);
function dragmove(d) {
d3.select(this).attr("transform", "translate(" + (d.x = d3.event.x) + "," + (d.y = d3.event.y) + ")");
//events to update line to fit dots
updateXs();
updateLineData();
//update line
d3.select(".myLine").transition()
.attr("d", lineFunc(lineData));
}
But after seeing it run I think that the block is running while the drag is occurring and the object is moving, which may be causing it to not work correctly. What I want to find is the correct method for handling code that should run after a drag completely occurs. If there's a way to make the line update while the point is being dragged, that would be really cool and preferred, but I don't mind having it execute after the drag finishes, either.
Here's the full code:
https://jsfiddle.net/cuhwvj8t/4/
To execute some code once the drag has completed you should use the dragend event. So you'd use:
var drag = d3.behavior.drag()
.on("dragend", function(d) {
// Update lines
});
However you should be able to update the lines during the drag using the drag event which you're already wired up to:
var drag = d3.behavior.drag()
.on("drag", function(d) {
// Update lines
});

How to stop d3.drag triggering mouseover/mouseout events in chrome

I have the following d3 visualisation. The darker colour at the top indicates that a node has been selected. When I mouseover a non selected node it changes opacity so a user can see which node would be selected if I click.
This is achieved via a CSS style sheet and the following js/d3:
nodeSelection.select("circle").on('mouseover', function(e) {
d3.select(this).classed("hover", true);
_this.fireDataEvent("mouseOverNode", this);
});
nodeSelection.select("circle").on('mouseout', function(e) {
d3.select(this).classed("hover", false);
_this.fireDataEvent("mouseOutNode", this);
});
So, far, so good. However, when I drag, the drag function seems to randomly trigger mouse over and mouse out events on the nodes that I am not dragging. This causes the node opacity to flicker. If I watch on the development tools in chrome I can see that this is because it is causing nodes to gain the class "hover". The code above to add this CSS class appears nowhere else, and by use of the console logging, I have confirmed that mouseover and mouseout events are being fired. These nodes are often far from the cursor.
This issue does not occur in Firefox.
UPDATE: I actually managed to fix this almost immediately after posting this. I just explicitly de-register the listeners inside drag start, and re register in drag end. It might still be interesting to some people if they are having similar issues.
My drag function now looks like:
var drag = d3.behavior.drag()
.on("dragstart", function(d) {
console.log("dragstart");
d.dragstart = d3.mouse(this); // store this
d.fixedbeforedrag = d.fixed;
d.fixed=true;
// deregister listeners
nodeSelection.select("circle").on("mouseover", null).on("mouseout", null);
})
.on("drag", function(d) {
d.px = d.x; // previous x
d.py = d.y;
var m = d3.mouse(this);
d.x += m[0] - d.dragstart[0];
d.y += m[1] - d.dragstart[1];
nodeSelection.attr("transform", "translate(" + [d.x, d.y] + ")");
_this.getForce().start();
})
.on("dragend", function(d) {
console.log("dragend");
delete d.dragstart;
d.fixed = d.fixedbeforedrag;
//reregisters listeners
_this.updateSVG();
});

d3 mouseover on element conflicts with svg mousemove

I have attached a mouseover event to an element - say, a circle - within the SVG element. I also need a "mousemove" event handler associated with the SVG element/"background" itself. However, they seem to conflict: when mousing over the circle, the handler attached to the circle does not supersede that associated with the SVG element itself.
How do I get the circle's mouseover to supersede the SVG element's event handler? I need them both, but only want the mouseover to be triggered over the circle and the mousemove to be triggered by movement anywhere else in the SVG element.
A simplified example can be seen in this JSFiddle: http://jsfiddle.net/aD8x2/ (JS code below). If you click on a circle (starting a line) and then mouse over another circle, you will see the flickering of color associated with both events being triggered when mousing over the circle.
var svg = d3.select("div#design")
.append("svg")
.attr("width", "500").attr("height", "500");
svg.selectAll("circle").data([100, 300]).enter().append("circle")
.attr("cx", function(d) { return d; })
.attr("cy", function(d) { return d; })
.attr("r", 30)
.on("mouseover", function () {
d3.select(this).attr("fill", "red");
})
.on("mouseout", function() {
d3.select(this).attr("fill", "black");
})
.on("click", function() {
svg.append("line")
.attr(
{
"x1": d3.select(this).attr("cx"),
"y1": d3.select(this).attr("cy"),
"x2": d3.select(this).attr("cx"),
"y2": d3.select(this).attr("cy")
})
.style("stroke-width", "10")
.style("stroke", "rgb(255,0,0)");
});
svg.on("mousemove", function() {
var m = d3.mouse(this);
svg.selectAll("line")
.attr("x2", m[0])
.attr("y2", m[1]);
});
In your case, it is actually the line causing the problem and not the SVG. That is, you're moving the mouse over the line you're drawing and thus a mouseout event is triggered for the circle.
You can prevent this by setting pointer-events to none for the line so it's "transparent" with respect to mouse events. Modified example here.

Categories