Trying to append a click function to the node text in d3 - d3.js

I'm trying to generate a modal with information pulled from my json file when the text of a node is clicked on. I'm having trouble, so I'm just trying to generate an alert box at the moment. I think that I need to declare a variable for the node text, but I'm having trouble. Here is what I have right now:
node.append("svg:text")
.attr("text-anchor", "middle")
.attr("dy", "3.5em")
.text(function(d) { return d.name; })
var textNodes = node.append("svg:text").text()
.on("click", function(d, i) { alert("Hello world"); });
Or this, I don't see why this wouldn't work either.
node.append("svg:text")
.attr("text-anchor", "middle")
.attr("dy", "3.5em")
.text(function(d) { return d.name; })
.on("click", function(d) { alert("Hello world"); });

You are explicitly setting pointer-events: none for the text nodes (maybe a copy and paste error). Removing this line in the styles allows the click event to fire.

I managed to do do recogize a click on the label using something like this:
const text = this.g.selectAll('text')
.data(nodes)
.enter().append('text')
.attr('class', 'label')
.style('fill-opacity', function(d) { return d.parent === root ? 1 : 0; })
.style('display', function(d) { return d.parent === root ? 'inline' : 'none'; })
.style('pointer-events', 'auto')
.on("click", function(d) {
d3.event.stopPropagation();
console.log(d.data.name);
})
.text(function(d) { return d.data.name});

to expand on the topic, the following function
d3.event.stopPropagation();
will make the node not expand or collapse if you clicked on the text but the node will continue to expand and collapse if you clicked on the node circle next to the name. Pretty neat.

Related

make text a link in svg

I'm using d3 to draw a chart (I'm using thix example: https://gist.github.com/keiranlovett/8766741). I need to add a link. I'm adding text and I'm trying to make it a link, but it's not appearing as a link (I'm using IE 11):
svg.append("g") //without doing this, impossible to put grid lines behind text
.attr({
xmlns: "http://www.w3.org/2000/svg",
xlink: "http://www.w3.org/1999/xlink",
width: 100,
height: 300
})
.attr({ "xlink:href": "#" })
.on("mouseover", function (d, i) {
//alert('aaa');
d3.select(this)
.attr({ "xlink:href": "http://example.com/" + "eeeeeeeeeeeeeeeeeee" });
})
.selectAll("text")
.data(tasks)
.enter()
.append("text")
.text(function (d) {
if (ammendmentsLinks.indexOf(d.contract) < 0) {
ammendmentsLinks = ammendmentsLinks + d.contract + ";";
return d.contract;
//return "";
}
else {
return "";
}
})
.attr("x", 20)
.attr("y", function (d, i) {
return i * 20;
})
.attr("font-size", 11)
.attr("text-anchor", "start")
.attr("text-height", 14)
.on("click", function (d) {
return click(d.contract);
});
I also added some lines of code from http://jsfiddle.net/christopheviau/B9zcF/ to make the text a link but it's not working.
How to make my text a link?
Try changing the following line:
d3.select(this)
.attr({ "xlink:href": "http://example.com/" + "eeeeeeeeeeeeeeeeeee" });
to
d3.select(this)
.wrap("<a xlink:href \"http://example.com/\"></a>")
.text("eeeeeeeeeeeeeeeeeee");
and make sure the <svg> tag has xmlns:xlink="http://www.w3.org/1999/xlink" specified.
This is a good solution too:
d3.select(this)
.text(d)
.on("click", function() { window.open("https://www.random.com/" + d ); });

How to partially update a graph after adding element?

I draw a graph from a set of nodes and links using the following update function with nodes and links arrays defined earlier in the code.
function update() {
var link = svg.selectAll(".link")
.data(links);
link.enter().insert("line")
.attr("class", "link");
var node = svg.selectAll(".node")
.data(nodes);
node.enter().append("svg:g")
.attr("class", "node")
.attr("id", function(d) { return d.name })
.on("dblclick", dblclick)
.on("mouseover", function(d) {drawTags(tags, d.name);})
.on("mouseout", function(d) {
d3.select("#"+d.name).selectAll(".tool")
.transition()
.delay(800)
.remove();
})
.call(force.drag);
node.append("circle")
.attr("class", "circle")
.attr("r", 40);
// display name in nodes if node structure
node.append("text")
.attr("text-anchor", "middle")
.attr("dy", ".35em")
.text(function(d) { return d.name; });
force.on("tick", function() {
link.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
});
force.start();
}
On dynamic update of the graph, after double click event on a node, I fetch additional nodes and links and display them. So far, I was able to append nodes and links to their corresponding arrays, hence update drawing. However, the update function would loop through all elements for the display.
Therefore I would like to pass newNodes and newEdges to the update function and only draw new elements. The issue is that new elements replace existing ones in the svg code rather than to be appended.
Is there a way to append new elements to previously existing ones in the drawing?
Full code is available on fiddle (https://jsfiddle.net/pducrot/4eyb81kx/) mouse over a node and click on any appearing tag to see what's happening.

Removing text on mouseout event

Small problem on one viz.
I have a bar chart, and I want it to display some text in the line on 'mouseenter'. That works fine, but I want to remove this text on 'mouseout', and I can't seem to get the hang of it.
Thank you very much for your help!
Attached: the concerned section of the d3.js script
d3.selectAll("div.line")
.append("div")
.attr("class","bar")
.style("width", function(d){return d.occurrence /10 + "px"})
.on("mouseenter", function(d) {
d3.select(this)
.append("text")
.text(function(d){return d.occurrence + " occurences"});
})
.on("mouseout", function(d) {
d3.select(this)
.select(".text").remove();
});
In your code, you're trying to select .text on mouseout. You're selecting nodes with which have the class: "text" rather than text nodes. Remove the dot.
I'd probably also change the mouseout "select" to "selectall" just in case you miss a mouseout event and accidentally add two text nodes.
Edit:
http://jsfiddle.net/QbGRE/
d3.select("div.line").selectAll("div.bar")
.data(data, function(d) { return d.id; })
.enter()
.append("div").attr("class","bar")
.style("width", function(d){return d.occurrence /10 + "px";})
.on("mouseover", function(d) {
d3.select(this)
.append("text").style("pointer-events", "none")
.text(function(d){return d.occurrence + " occurences";});
})
.on("mouseout", function(d) {
d3.select(this)
.select("text").remove();
});
Cleaned up your code.
"mouseover/mouseout" instead of "mouseenter/mouseout"
.select("text") instead of ".text".
style("pointer-events", "none) on the text to stop it causing extra mouse events when it's added.
Added fake data.
The easiest way to do this is to assign a unique ID to the new text element and select by that:
.on("mouseenter", function(d) {
d3.select(this)
.append("text")
.attr("id", "myText")
.text(function(d){return d.occurrence + " occurences"});
})
.on("mouseout", function(d) {
d3.select("#myText").remove();
});

Adding/Removing labels of line points with update

I'm using a range slider to add/remove points from a line which follows an histogram.
Without updating the data, the text labels are shown correctly, when I update the values with the slider I'd like that the text-labels follow the line, so they show the values of the points correctly.
The update for the line works, but I am not able to update the text labels correctly.
Right now the laberls are plotted like so outside of the on-change function:
var SerifText = svg.append("g")
.attr("id", "serif-labels")
.selectAll("text")
.data(histogSerif)
.enter()
.append("text")
.text(function (d) { return d.y >= 1 ? d.y : null; })
.attr("text-anchor", "middle")
.attr("font-size", 10 + "px")
.attr("fill", "#f0027f")
.attr("x", function (d) { return x(d.x)})
.attr("y", function (d) { return y(d.y) - padding; })
.attr("visibility", "hidden"); //the hidden & visible is managed by an external checkbox
And then on the on-change function I put this code:
SerifText.selectAll("text").remove();
/* If I do SerifText.remove() it does remove the text but also the group of course,
but as it is it doesn't work */
SerifText.selectAll("text")
.data(histogSerif)
.enter()
.append("text")
.text(function (d) { return d.y >= 1 ? d.y : null; })
.attr("text-anchor", "middle")
.attr("font-size", 10 + "px")
.attr("fill", "#f0027f")
.attr("x", function (d) { return x(d.x)})
.attr("y", function (d) { return y(d.y) - padding; });
But this doesn't work unfortunately. I'd like also to be able to retain the manage of the visible/hidden status by the external checkbox if possible.
Full code is here
Instead of trying to save a reference to the labels that you are going to delete, and then re-selecting them, how about just use a variable that references the SerifText element selection, e.g.:
var SerifTextGroup = svg.append("g")
.attr("id", "serif-labels");
SerifTextGroup.selectAll("text")
.data(histogSerif)
.enter()
//... and so on
Then the rest of your code should work as written (just change SerifText to SerifTextGroup).

Labels on bilevel D3 partition / sunburst layout

I am trying to add labels to the bilevel sunburst / partition shown here - http://bl.ocks.org/mbostock/5944371:
I have added labels to the first 2 levels and rotated them correctly. But I cant now add the new level's labels during a transition. To get started I have put ...
text = text.data(partition.nodes(root).slice(1), function(d) { return d.key; });
straight after the ...
path = path.data(partition.nodes(root).slice(1), function(d) { return d.key; });
line but it throws the following error ...
Uncaught TypeError: Cannot read property '__data__' of undefined
What am I doing wrong?
I used bilevel partition to add labels via a mouseover, and found that in this version (unlike the other sunburst partition), there are two different sections where "path" is defined and updated. The first is here:
var path = svg.selectAll("path")
and then again below the transition you highlighted in your code:
path.enter().append("path")
When i added my mouseover labels, I needed to reference my text function in both places or it wouldn't work after transition.
If you can post your code to JSFiddle or bl.ocks.org we can try to play with it and see, but that was where I got tripped up at first.
Hope that helps.
*NOTE: Comment didn't post:
I'm sorry I'm not able to help more, but I'm also a newbie at D3. Here's where I got:
copy and paste your svg.selectAll("text") snippet after the second "path.enter().append("path") snippet. This will cause it to appear on subsequent zooms as well.
Problems I see: there's no "g" element so you need separate transitions for text as well or they all pile up. Also, I can't understand why they're positioned at their original partition spot instead of where the arc exists now.
My approach was add labels on mouseOver. I've uploaded that here: https://github.com/johnnymcnugget/d3-bilevelLabels
In this, I'm calling the two functions in both sets of "path" snippets, and the transitions are handled within the single variable of "legend"
Another D3 newbie, but I managed to resolve this. Beggining from the original Bilevel Partition:
Add the text for the first time the chart is drawn:
var path = svg.selectAll("path")
.data(partition.nodes(root).slice(1))
.enter().append("path")
.attr("d", arc)
.style("fill", function(d) { return d.fill; })
.each(function(d) { this._current = updateArc(d); })
.on("click", zoomIn);
var text = svg.selectAll("text")
.data(partition.nodes(root).slice(1))
.enter().append("text")
.attr("transform", function(d) { return "rotate(" + computeTextRotation(d) + ")"; })
.attr("x", function(d) {return radius / 3 * d.depth; })
.attr("dx", "6") // margin
.attr("dy", ".35em") // vertical-align
.html(function(d) { return d.name; });
when zooming in or out you have to redraw labels, add the following in function zoom(root, p)
text.enter().append("text")
.attr("transform", function(d) { return "rotate(" + computeTextRotation(d) + ")"; })
.attr("x", function(d) {return radius / 3 * d.depth; })
.attr("dx", "6") // margin
.attr("dy", ".35em") // vertical-align
.text(function(d) { return d.name; });
text.exit().transition()
.style("fill-opacity", function(d) { return d.depth === 1 + (root === p) ? 1 : 0; })
.remove();
text.transition()
.style("fill-opacity", 1)
.attr("transform", function(d) { return "rotate(" + computeTextRotation(d) + ")"; })
.attr("x", function(d) {return radius / 3 * d.depth; })
.attr("dx", "6") // margin
.attr("dy", ".35em") // vertical-align
Finally modify the computeTextRotation function as:
function computeTextRotation(d) {
return (d.x + (d.dx)/2) * 180 / Math.PI - 90;
}
Hope I haven't missed anything.
Indeed one modified line is missing in "2.". In function zoom(root, p), you should also add a line after path = path.data(partition ... .key; });:
path = path.data(partition.nodes(root).slice(1), function (d) {
return d.key;
});
text = text.data(partition.nodes(root).slice(1), function (d) {
return d.key;
});

Resources