Displaying text over a node in a d3 force-layout graph - javascript

I am creating a force-layout graph using d3 and I am trying to make the name of a specific node appear when I mouse over that node. I know how to add the text before any mouse over effect and thought that I could just move that part of the code into the mouseover function but that did not work. I will also need to make the text disappear when I move the mouse off of the node. Here is the mouseover function where I attempt to add the name to the node:
function mouseover() {
d3.select(this).transition()
.duration(750)
.attr("r", function(d) {return d.size + 10;});
var labels = gnodes.append("text")
.text(function(d) { return d.name;})
console.log(labels);
}
Here is a link to a fiddle of the complete code as well:
http://jsfiddle.net/ohiobucks23/QvVU6/

If not using a tooltip per good recommendation by Bhatt, you will need to:
1) declare gnodes outside the drawGraph() function so that it is visible to the mouse functions, and
2) make the following changes to the mouse functions:
function mouseover(d) {
d3.select(this).transition()
.duration(750)
.attr("r", function (d) {return d.size + 10;});
// locate node and append text; add class to facilitate subsequent deletion
gnodes.filter(function (o) {return o.index === d.index;})
.append("text")
.attr("class", "nodetext")
.text(d.name);
}
function mouseout(d) {
d3.select(this).transition()
.duration(750)
.attr("r", function (d) {return d.size;});
// delete text based on class
d3.selectAll(".nodetext").remove();
}
Here is the complete FIDDLE. I changed the text of the root node element so that you can see that the mouseover function is really acting on the selected node.

Related

D3 - how to fire an on-click function for only a specific group of nodes

In this d3 force layout viz project I'm trying to remove the user's ability to click on the bigger yellow nodes. I have an on-click, fire the clicknodeControl function,
nodes.append('circle')
.attr("r", 28)
.attr("id", "hoverdots")
.style("opacity", 0)
.style("fill", "#00bedd")
.on("click", function(d) { return clicknodeControl(d.group); })
.style("z-index", "10")
.on("mouseover", mouseover)
.on("mouseout", mouseout)
and then ideally this variable would pass through the clicknode function to one group of nodes but not the other:
var clicknodeControl = d3.scaleOrdinal([`clicknode`, ``]);
This does not seem to be working in practice, the clicknode function is not being passed through.
Thanks if anyone has any ideas on this!!
In D3 V7 mouse event handlers have event as the first argument and datum as the second.
Replace
.on("click", function(d) { return clicknodeControl(d.group); })
with:
.on("click", (_, d) => clicknodeControl(d.group))

How to update node data in a D3.js force graph?

I'm looking for a way to update multiple node attributes and appended elements on data change.
Here is how I'm trying to do it right now.
This function gets called every-time node or link data changes.
function restart() {
link = link.data(links);
link
.enter().insert("line", ".node")
.attr("class", "link")
.on('click' , function(d, i){
console.log(d);
links.splice(i,1);
restart();
})
.on("mouseover", function() {
d3.select(this).style("stroke","red");
d3.select(this).style("stroke-width","5px");
})
.on("mouseout", function() {
d3.select(this).style("stroke","#999");
d3.select(this).style("stroke-width","initial");
});
link
.exit().remove();
node = node.data(nodes);
node
.enter()
.append("g")
.attr("class", "node")
.call(node_drag);
node
.insert("circle", ".cursor")
.attr("r", function(d) { return calcSize(d.links+1); })
node
.append("text")
.attr("dx", 12)
.attr("dy", ".35em")
.text(function(d) { return d.links });
node
.exit().remove();
force.start();
}
The full code at the current state can be seen here: http://jsbin.com/takatugazo/edit?html,js,output
The expected behaviour is that when two nodes get dragged into each other they create a link and the more links they have the bigger they get.
Your appended text IS updating. The only issue seems to be that the instead of replacing the old text, it's adding a <text> element on top. If you just inspect a node, you'll find multiple <text> elements based on how many links it has. For some reason your code doesn't update the text the way it should.
Try removing the text element before you append one:
node.select("text").remove()
node
.append("text")
.attr("dx", 12)
.attr("dy", ".35em")
.text(function(d) { return d.links });
node
.exit().remove();
I made those changes in JS Bin and it seems to work for me (with a bit of a delay): http://jsbin.com/goqumutelu/edit?html,js,output
There is a delay in the dragend() function whenever is a link is pushed (not sure if intentional?) So if you don't want the delay: removing the timeout, or simply replacing the delay value (1500) to 0 should solve that: http://jsbin.com/hazecozumu/1/edit?html,js,output
About the size of the nodes, what's wrong it? How else did you want it to update?

How to change d3.js text entry from within that text's onclick method?

My AngularJS app uses d3.js to draw a nice chart.
While drawing this chart, it uses paints some text on the screen.
I want to change that text when someone clicks on it based on the boolean value of myCondition. This is how I do it:
var nodeEnter = node.enter()
var myLabel = nodeEnter.append("text")
.attr("x", 50)
.attr("dy", "3")
.text("Hello World")
.style("fill-opacity", 0)
.style("cursor", "pointer")
.on("click", function(d) {
if (myCondition)
myLabel.text("Mars");
else
myLabel.text("Venus");
}
);
It sorta works. The value of the text does indeed change from Hello World to Mars or Venus. But there is a problem. This code is called within a recursive function and within a loop. That recursion + loop use the same code to draw numerous such texts on the SVG Container. So when I click this label, not only does it change the text that I want. It also changes the text in other places too! I don't want that. How can I prevent it?
I really just need a way I can address this or myself from within the click function so it knows I'm talking about the object. How?
Without knowing your recursive function and the loop, I'll try two different approaches, I hope that one of them works.
The first one is using this for the click event:
var myLabel = nodeEnter.append("text")
.attr("x", 50)
.attr("dy", "3")
.text("Hello World")
.style("fill-opacity", 0)
.style("cursor", "pointer")
.on("click", function(d) {
if (myCondition)
d3.select(this).text("Mars");
else
d3.select(this).text("Venus");
}
);
If this doesn't work, you can try to set a specific class to your different myLabel texts. Doing this, even if you have several myLabel in your SVG, each one has a unique class. Suppose that index is a specific value for the loop (like i). So, you can try:
var myLabel = nodeEnter.append("text")
.attr("x", 50)
.attr("dy", "3")
.attr("class", "myLabel" + index)//index is any value unique for the loop
.text("Hello World")
.style("fill-opacity", 0)
.style("cursor", "pointer")
.on("click", function(d) {
if (myCondition)
d3.selectAll(".myLabel" + index).text("Mars");
else
d3.selectAll(".myLabel" + index).text("Venus");
}
);

Fading chords in d3js chord graph

I'm a total beginner with d3js, so please be patient if my question looks dumb.
I'm trying to reproduce a chord graph like the one proposed by Mike Bostock. In the code by Bostock if you go with your mouse on an arc, all the chords that are not involved (as target as well as source) in the arc will fade.
I'd like to change it in order to let all the chords fade except the one on which there is a mouse (in order to emphasize one single two-way relationship).
I've added a fade_single function that is triggered when the mouse is over a chord:
svg.append("g")
.attr("class", "chord")
.selectAll("path")
.data(chord.chords)
.enter().append("path")
.style("fill", function(d) { return fill(d.target.index); })
.attr("d", d3.svg.chord().radius(r0))
.style("opacity", 1)
.on("mouseover", fade_single(0.1))
.on("mouseout", fade_single(1));
The fade_single function follows:
function fade_single(opacity) {
return function(g, i) {
svg.selectAll("g.chord path")
.filter(function(d) {
//return d.source.index != 0 && d.target.index != 0;
})
.transition()
.style("opacity", opacity);
};
}
The problem is that I don't know what to put in the commented line, i.e. to filter out all the relationship that are have not the row and column of the single chord. I've tried to play with the subindexes but the parameter i only gives you the row, so I don't know how to isolate the chord I want to exclude from the fading.
Any idea? Any hint?
Thank you,
Elisa
To fade everything but the current elemeent, the easiest way is to use the this reference to the current DOM element:
function fade_single(opacity) {
return function() {
var me = this;
svg.selectAll("g.chord path")
.filter(function(d) {
return this != me;
})
.transition()
.style("opacity", opacity);
};
}

animate this element in d3js

Ok so I have the following code example where I have circles in an svg element. Each circle has a click event and I'm trying to animate the circle that was clicked. Currently all circles animate because I'm referring to the bubble object. What I want is to refer to the clicked object its self and not the other ones:
var data_items=[100,200,300];
var svg = d3.select("#chart")
.append("svg").attr("width", 800).attr("height", 600);
var g = svg.selectAll(".bubbleContainer")
.data(data_items)
.enter().append("g")
.attr("class","bubbleContainer");
var bubble = g.append("circle")
.attr("class","bubble")
.attr("cx", function(d) {
return d;
})
.attr("cy", function(d) {
return d
})
.attr("r", function(d) {
return d/2
})
.on("click",function(d){
bubble
.transition()
.duration(1000)
.attr("r",1000)
})
Any help is much appreciated
Thanks!
What Lars Kotthoff wrote would work. Alternatively – and I'm not sure which is more idiomatic:
Inside the click handler, the this context refers to the clicked DOM element.
So the following would do it too:
.on("click",function(d){
d3.select(this)
.transition()
.duration(1000)
.attr("r",1000)
});
You can use d3.event.target to access the element that is being clicked in the event handler. See for example this jsfiddle.

Categories

Resources