I am trying to create a collapsible tree having a link to expand the current node (in stead of clicking the node using on.("click",clicked) method). Here is the jsfiddle
nodeEnter.append("foreignObject")
.attr("width", rectW)
.attr("height", rectH)
.append("xhtml:body")
.style("font", "14px 'Helvetica Neue'")
.html(function (d) { return "<a href='#' onclick=\x22clicked(" + source + ")\x22>"+d.name+"</a>"});
Problem is that your source variable is converted to string, so you don't actually pass object to clicked function;
You can rewrite this to append link like this:
nodeEnter.append("foreignObject")
.attr("width", rectW)
.attr("height", rectH)
.append("xhtml:body")
.style("font", "14px 'Helvetica Neue'")
.append("a").html(function(d){return d.name;})
.attr('href', '#')
.on('click', function(){
clicked(source);
});
see updated fiddle
To make it work for clicked node (instead of always parent node) you can replace source by clicked function argument
.on('click', function(d){
clicked(d);
});
another updated fiddle
Related
I am trying to add the label and icon in this codepen as per this example Labeled Force Layout
In my restart method, I have added the code
node.append("image")
.attr("xlink:href", "https://github.com/favicon.ico")
.attr("x", -8).attr("y", -8).attr("width", 16).attr("height", 16)
.on("mousedown", mousedownNode);
node.append("text").attr("dx", 12).attr("dy", ".35em").text(function(d) { return d.id });
after my existing code :
node.enter().insert("circle", ".cursor")
.attr("class", "node").attr("r", 10).on("mousedown", mousedownNode);
I understand that I am making the node as circle first then trying to add an icon to it, which is the issue here but I am not sure how to fix it.
You cannot append a <text> element to a <circle> element.
The idiomatic solution here is converting node into a group selection, just like Mike Bostock has in the example you shared:
node.enter().insert("g", ".cursor")
.attr("class", "node");
Then, appending the circles and the texts to node:
node.append("circle")
.attr("r", 10)
.on("mousedown", mousedownNode);
node.append("text")
.attr("dx", 12)
.attr("dy", ".35em")
.text(function(d) {
return d.id
});
And, finally, changing the tick function:
node.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")"
});
Here is the updated CodePen: https://codepen.io/anon/pen/VBepoo?editors=0010
Of course, this is just a quick refactor: you have to change other parts of your code for having node as a group selection.
Thank you Gerardo for this very helpful CodePen entry. Apparently, the CSS overrides the JS and must be modified in order for the icon to appear, for example:
From: .node { fill: #000; }
To: .node { fill: none; stroke: <some color so the labels appear>;}
My update of your update on CodePen
I'm new to D3 and am trying to build a table like structure out of rectangles. I would like the header to be a different color than the rest of the rectangles. I've written the following code:
table = svgContainer.selectAll('rect')
.data([managedObj])
.enter()
.append('rect')
.attr("width", 120)
.attr("height", 20)
.attr("fill", "blue")
.text(function(d) {
return d.name;
});
// create table body
table.selectAll('rect')
.data(managedObj.data)
.enter()
.append('rect')
.attr("y", function() {
shift += 20;
return shift;
})
.attr("width", 120)
.attr("height", 20)
.attr("fill", "red")
.text(function(d) {
return d.name;
});
This is producing the following results:
This is almost what I intended except it is nesting the second group of rectangles inside the first rectangle. This causes only the first blue rectangle to be visible. I'm assuming this has something to do with calling the data method twice. How can I fix this issue?
I think I understand the intended result, so I'll give it a go:
This line :
table.selectAll('rect')
is selecting the rectangle just created here:
table = svgContainer.selectAll('rect')....append('rect')....
You don't want to append rectangles to that rectangle (or any rectangle for that matter) because this won't work, but you do want to append them to the SVG itself.
So instead of table.selectAll you should be using svgContainer.selectAll, but there are two other issues:
if you use svgContainer.selectAll('rect') you will be selecting the rect you have already appended, when you actually want an empty selection. See the answer here.
you cannot place text in a rect (See answer here), instead you could append g elements and then append text and rect elements to those. And, for ease of positioning, you could translate the g elements so that positioning the rectangles and text is more straight forward.
So, your code could look like:
var data = ["test1","test2","test3","test4"];
var svgContainer = d3.select('body').append('svg').attr('width',900).attr('height',400);
var header = svgContainer.selectAll('g')
.data([data])
.enter()
.append('g')
.attr('transform','translate(0,0)');
header.append('rect')
.attr("width", 120)
.attr("height", 20)
.attr("fill", "blue");
header.append('text')
.attr('y',15)
.attr('x',5)
.text(function(d) {
return "header";
});
// create table body
var boxes = svgContainer.selectAll('.box')
.data(data)
.enter()
.append('g')
.attr('class','box')
.attr('transform',function(d,i) { return 'translate(0,'+((i+1)*20)+')'; });
boxes.append('rect').attr("width", 120)
.attr("height", 20)
.attr("fill", "red");
boxes.append('text')
.attr('y',15)
.attr('x',5)
.text(function(d) {
return d;
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
I have a cluster force layout which works fine, and I can add text to the circles. However, when I try to add a hypertext link, nothing is displayed.
What is the problem with my code?
The code for the text is
node.append("a")
.text(function(d){
return d.name;
})
.attr("href", function(d){
return '/profile/'+d.name;
})
.attr("dx", -10)
.text(function(d){
return d.name;
})
.style("stroke", "white");
In SVGs you are not allowed to put textual content into the <a> itself. For an overview of what is actually allowed in an <a> element have a look at the Content model section of the spec.
You need to wrap another text element around your link labels:
node.append("a")
.attr("xlink:href", function(d){
return '/profile/'+d.name;
})
.append("text") // <-- Wrap <text> element around link label
.text(function(d){
return d.name;
})
.attr("dx", -10)
.style("stroke", "white");
As described on http://bl.ocks.org/mbostock/1424037, I have seen the foreignObject feature working only with statically inputted text. What happens if I try something like this:
svg.append("foreignObject")
.attr("width", 400)
.attr("height", 200)
.append("xhtml:body")
.style("font", "16px 'Helvetica Neue'")
.html(function(d) {
return d.name;
})
Does the foreignObject feature work with data returned by functions?
Well, that does work. However one thing that needs to be taken care of is to replace .html with .text.
node.append("foreignObject")
.attr("class", "innerNode")
.text(function (d) {
return d.name; })
On click of parent node in tree layout, link and linktext should exit. Link is exiting but not the linktext. Here is the jsfiddle project. I tried the code - svg.selectAll(".line-label").remove(); which was deleting the linkText from all the labels that should not happen. What i am expecting is on click of node it exits the linkText like link and node from the current branch of the tree not from the whole tree itself.
You can do this by
Adding link like this in update:
//add text to the link
svg.selectAll(".line-label").data(links).enter().append("text")
.attr("font-family", "Arial, Helvetica, sans-serif")
.attr("fill", "Black")
.attr("class", "line-label")
.style("font", "normal 12px Arial")
.attr("transform", function (d) {
return "translate(" + (d.target.y - 30) + "," + (d.target.x - 10) + ")";
})
.attr("text-anchor", "middle")
.text(function (d) {
return d.target.label;
});
//remove the link text in update
svg.selectAll(".line-label").data(links).exit().remove();
Full running code here
Hope this clarifies your concern! :)