Showing Multiline text using d3.js - d3.js

Earlier i was appending .text element to g tag to show the text. But now as length of text is more than one line i am trying to follow below example and put div in g tag.
But it's not working Below is the Code. Am i missing something?.
var width = 960,
height = 500;
var w=300,h=300;
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
d3.json("data.json", function (json) {
/* Define the data for the circles */
var elem = svg.selectAll("g myCircleText")
.data(json.nodes)
/*Create and place the "blocks" containing the circle and the text */
var elemEnter = elem.enter()
.append("g")
.attr("transform", function (d) { return "translate(" + d.x + ",180)" })
/*Create the circle for each block */
var circle = elemEnter.append("circle")
.attr("r", function (d) { return d.r })
.attr("stroke", "black")
.attr("fill", "white")
.style("fill", "url(#image)")
.on("click", function (d) {
var g = svg.append("g")
.attr("transform", "translate(150,100)");
g.append("rect")
.attr("width", 200)
.attr("height", 100)
.style("fill", "#E6EFFA")
.transition()
.duration(750)
.attr("width", 500)
.attr("height", 400)
.each("end", function () {
var tempcircle = g.append("circle")
.attr("r", "50")
.attr("stroke", "black")
.attr("fill", "blue")
.style("fill", "url(#image)");
g.append("text")
.attr("dx", "200")
.attr("dy", "30")
.text(d.label);
var templine = g.append("line")
.attr("x1", 20)
.attr("y1", 50)
.attr("x2", 450)
.attr("y2", 50)
.attr("stroke-width", 2)
.attr("stroke", "white");
g.append("text")
.attr("dx", "180")
.attr("dy", "80")
.text(d.info);
g.append("text")
.attr("dx", "120")
.attr("dy", "120")
.style("font-weight", "bold")
.text(d.first);
g.append("div")
.attr("id", "div_01")
.style({ width: w + "px", height: h + "px" })
.attr("dx", "150")
.attr("dy", "150")
.text(d.Answer);
g.append("text")
.attr("dx", "120")
.attr("dy", "170")
.style("font-weight", "bold")
.text(d.second);
g.append("text")
.attr("dx", "150")
.attr("dy", "190")
.text(d.A2);
g.append("text")
.attr("dx", "120")
.attr("dy", "220")
.style("font-weight", "bold")
.text(d.third);
g.append("text")
.attr("dx", "150")
.attr("dy", "240")
.text(d.A3);
var templine1 = g.append("line")
.attr("x1", 20)
.attr("y1", 270)
.attr("x2", 450)
.attr("y2", 270)
.attr("stroke-width", 2)
.attr("stroke", "white");
g.append("circle")
.attr("r", "15")
.attr("cx", "505")
.attr("cy", "6")
.style("fill", "#B5CEE7")
.on('click', function () {
d3.selectAll("rect").remove();
d3.selectAll("text").remove();
d3.select(this).remove();
tempcircle.remove();
templine.remove();
templine1.remove();
})
g.append("text")
.attr("dx", "500")
.attr("dy", "8")
.text("x");
})
});
})
There is a div appending to g tag.
http://bl.ocks.org/JohnDelacour/5676636
is the example i am trying to follow.
Thanks

You can't add html to svg unless you use foreignObject. The example you show keeps the divs and the svg separated and superimposed. It's a modification of my foreignObject example: http://tributary.io/inlet/5320723

Related

How to display an image inside a circle inside an arc in d3

I've got a sunburst chart drawn and this works fine. I've tried to add a circle that displays an image within each segment (path) and I can't get them to show up. Weirdly if I inspect the DOM the circles are exactly where I want them to be in the tree but they simply don't display. Is anybody able to help?
.create("svg")
.attr("viewBox", [0, 0, width, width])
.style("font-size", ".6rem");
const g = svg
.append("g")
.attr("transform", `translate(${width / 2},${width / 2})`);
const path = g
.append("g")
.selectAll("path")
.data(root.descendants().slice(1))
.join("path")
.attr("fill", (d) => {
while (d.depth > 1) d = d.parent;
return color(d.data.name);
})
.attr("fill-opacity", (d) =>
arcVisible(d.current) ? (d.children ? 0.7 : 0.5) : 0
)
.attr("d", (d) => arc(d.current));
path
.filter((d) => d.children)
.style("cursor", "pointer")
.on("click", clicked);
const defs = path.append("defs").attr("id", "imgdefs");
const iconPattern = defs
.append("pattern")
.attr("id", "iconPattern")
.attr("height", 1)
.attr("width", 1)
.attr("x", "0")
.attr("y", "0");
iconPattern
.append("image")
.attr("xlink:href", function (d) {
return d.data.img;
})
.attr("height", 15)
.attr("width", 15);
path
.append("circle")
.attr("r", 5)
.attr("cy", 0)
.attr("cx", 0)
.attr("fill", "url(#iconPattern)");
path
.append("title")
.attr("pointer-events", "none")
.attr("color", "white")
.on("click", clicked)
.text((d) => d.data.name);```
Here is your code sample refactored to add g instead of path and add path, circle, and text under g:
const segments = g
.selectAll("g.segment")
.data(root.descendants().slice(1))
.join("g")
.classed("segment", true)
segments.append("path)
.attr("fill", ...)
.attr("fill-opacity", ...);
segments.filter((d) => d.children)
.style("cursor", "pointer")
.on("click", clicked);
const defs = segments.append("defs").attr("id", "imgdefs");
const iconPattern = defs
.append("pattern")
...
iconPattern
.append("image")
...
segments.append("circle")
.attr("r", 5)
.attr("cy", 0)
.attr("cx", 0)
.attr("fill", "url(#iconPattern)");
segments.append("title")
.attr("pointer-events", "none")
.attr("color", "white")
.on("click", clicked)
.text((d) => d.data.name);

d3 rect in one group interfering with rect in another group

I have a group called groove which has two rects. These are not bound to the data.
I also have a group called group which has many rects which are bound to the data.
In the second group called group there are only three data points, but only two are displaying.
Why is the first one not being rendered?
I have seen this before but can't remember how to solve it.
var margin = {
top: 20,
right: 20,
bottom: 20,
left: 20
},
width = 600 - margin.left - margin.right,
height = 600 - margin.top - margin.bottom;
var svg = d3.select("svg");
var data = [{
x: 200
},
{
x: 300
},
{
x: 400
}
];
var groove = svg.append("g")
.attr("class", "groove_group");
groove.append("rect")
.attr("x", 100)
.attr("y", 150)
.attr("rx", 2)
.attr("ry", 2)
.attr("height", 6)
.attr("width", 800)
.style("fill", "grey");
groove.append("rect")
.attr("x", 102)
.attr("y", 152)
.attr("rx", 2)
.attr("ry", 2)
.attr("height", 2)
.attr("width", 796)
.style("fill", "black");
// create group
var group = svg.selectAll("g")
.data(data)
.enter().append("g")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended))
.on("click", removeElement);
group.append("rect")
.attr("x", function(d) {
return d.x;
})
.attr("y", 100)
.attr("height", 100)
.attr("width", 15)
.style("fill", "lightblue")
.attr('id', function(d, i) {
return 'handle_' + i;
})
.attr("rx", 6)
.attr("ry", 6)
.attr("stroke-width", 2)
.attr("stroke", "black");
group.append("text")
.attr("x", function(d) {
return d.x
})
.attr("y", 100)
.attr("text-anchor", "start")
.style("fill", "black")
.text(function(d) {
return "x:" + d.x
});
function dragstarted(d) {
d3.select(this).raise().classed("active", true);
}
function dragged(d) {
d3.select(this).select("text")
.attr("x", d.x = d3.event.x);
d3.select(this).select("rect")
.attr("x", d.x = d3.event.x);
}
function dragended(d) {
d3.select(this)
.classed("active", false);
}
function removeElement(d) {
d3.event.stopPropagation();
data = data.filter(function(e) {
return e != d;
});
d3.select(this)
.remove();
}
<svg width="960" height="500"></svg>
<script src="//d3js.org/d3.v4.min.js"></script>
When you do this:
var group = svg.selectAll("g")
You are selecting a previously existing group (which is groove). Therefore, your enter selection has one less element.
Solution
Select nothing:
var group = svg.selectAll(null)
Here is your code with that change:
var margin = {
top: 20,
right: 20,
bottom: 20,
left: 20
},
width = 600 - margin.left - margin.right,
height = 600 - margin.top - margin.bottom;
var svg = d3.select("svg");
var data = [{
x: 200
}, {
x: 300
}, {
x: 400
}];
var groove = svg.append("g")
.attr("class", "groove_group");
groove.append("rect")
.attr("x", 100)
.attr("y", 150)
.attr("rx", 2)
.attr("ry", 2)
.attr("height", 6)
.attr("width", 800)
.style("fill", "grey");
groove.append("rect")
.attr("x", 102)
.attr("y", 152)
.attr("rx", 2)
.attr("ry", 2)
.attr("height", 2)
.attr("width", 796)
.style("fill", "black");
// create group
var group = svg.selectAll(null)
.data(data)
.enter().append("g")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended))
.on("click", removeElement);
group.append("rect")
.attr("x", function(d) {
return d.x;
})
.attr("y", 100)
.attr("height", 100)
.attr("width", 15)
.style("fill", "lightblue")
.attr('id', function(d, i) {
return 'handle_' + i;
})
.attr("rx", 6)
.attr("ry", 6)
.attr("stroke-width", 2)
.attr("stroke", "black");
group.append("text")
.attr("x", function(d) {
return d.x
})
.attr("y", 100)
.attr("text-anchor", "start")
.style("fill", "black")
.text(function(d) {
return "x:" + d.x
});
function dragstarted(d) {
d3.select(this).raise().classed("active", true);
}
function dragged(d) {
d3.select(this).select("text")
.attr("x", d.x = d3.event.x);
d3.select(this).select("rect")
.attr("x", d.x = d3.event.x);
}
function dragended(d) {
d3.select(this)
.classed("active", false);
}
function removeElement(d) {
d3.event.stopPropagation();
data = data.filter(function(e) {
return e != d;
});
d3.select(this)
.remove();
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="960" height="500"></svg>

Adding background color to axis labels in D3 parallel coordinates

I'm working on a modified version of this D3 code for parallel coordinates. I would like to add a background color for the axis labels. I realize I cannot set a background color using CSS styles because this is SVG text, so I tried to append rectangles but to no avail. Here is what I wrote but it's not drawing anything:
d3.selectAll(".label")
.append("rect")
.attr("x", function(d){ return d3.select(this).node().getBBox().x - 5;})
.attr("y", function(d){ return d3.select(this).node().getBBox().y - 5;})
.attr("width", function(d){ return d3.select(this).node().getBBox().width;})
.attr("height", function(d) {return d3.select(this).node().getBBox().height;})
.style("stroke", "black")
.style("fill", "yellow");
What am I doing wrong?
EDIT:
based on comments, I appended to the parent g rather than the text label itself. Now the code looks like this:
// Add an axis and title.
var gsvg= g.append("svg:g")
.attr("class", "axis")
.attr("id", "gsvg")
.each(function(d) { d3.select(this).call(axis.scale(yscale[d])); })
.attr("transform", "translate(0,0)");
d3.selectAll("#gsvg")
.append("rect")
.attr("x", function(d){ return this.parentNode.getBBox().x - 5;})
.attr("y", function(d, i){ return i%2 === 0 ? this.parentNode.getBBox().y - 20: this.parentNode.getBBox().y - 35;})
.attr("width", function(d){ return this.parentNode.getBBox().width;})
.attr("height", function(d) {return 20;})
.style("stroke", "lightgrey")
.style("stroke-width", 2)
.style("fill", function(d){
return self.fcolorMap[d];
})
.style("opacity", 0.5);
gsvg.append("svg:text")
.style("text-anchor", "middle")
.attr("y", function(d,i) { return i%2 == 0 ? -14 : -30 } )
.attr("x",0)
.attr("class", "axis-label")
.text(function(d) {
var s = d.split("|");
return s[s.length-1]; });
The only problem is now I need to figure out how to get the bounding boxes of the labels rather than those of the axes.

How to add a background color to d3 text elements?

I'm trying to add a rect element behind text with d3 to simulate background-color which doesn't exist for d3 text elements. I would like the rect to have the exact same size as the text itself.
node.append("text")
.attr("class", "text")
.attr("text-anchor", "middle")
.attr("dx", 0)
.attr("dy", ".35em")
.text(function(d) {
var bbox = this.getBBox();
node.insert("rect",":first-child")
.attr("x", bbox.x)
.attr("y", bbox.y)
.attr("width", bbox.width)
.attr("height", bbox.height)
.style("fill", "yellow");
return d.name;
});
this.getBBox() returns 0 for both x and y.
The following code display the box, but it size doesn't very with text size, and the box is drawn even when text is not (when an image exists).
node.filter(function(d) {return (!d.image)}).append("text")
.attr("class", function(d) { return "text "+d.type; })
.attr("text-anchor", "middle")
.attr("dx", 0)
.attr("dy", ".35em")
//.text(function(d) { if (!d.imgB64) { return d.label; }
.text(function(d) {
return d.name;
})
.each(function(d) {
var bbox = this.getBBox();
node.insert("rect", "text")
.style("fill", "#FFE6F0")
.attr("x", bbox.x)
.attr("y", bbox.y)
.attr("width", bbox.width)
.attr("height", bbox.height);
});
SOLUTION
Thanks to Cool Blue, the following code now properly works: display a rect behind the text so that it is readable when larger than the node circle. In the futur it could be improved with a sphere arc rather than the rect to only hide the circle frame behind the text...
// only display node labels if node has no image
node.filter(function(d) {return (!d.image)}).append("text")
.attr("class", function(d) { return "text "+d.type; })
.attr("text-anchor", "middle")
.attr("dx", 0)
.attr("dy", ".35em")
.text(function(d) {
return d.name;
})
.call(getTextBox);
// only display a rect behind labels if node has no image
node.filter(function(d) {return (!d.image)}).insert("rect","text")
.attr("x", function(d){return d.bbox.x})
.attr("y", function(d){return d.bbox.y})
.attr("width", function(d){return d.bbox.width})
.attr("height", function(d){return d.bbox.height})
.style("fill", "#FFE6F0");
function getTextBox(selection) {
selection.each(function(d) { d.bbox = this.getBBox(); })
}
As mentioned in the comments, use this pattern and add whatever details you need...
var textNode = node.filter(function(d) {return (!d.image)})
textNode.append("text")
.attr("class", "text")
.attr("text-anchor", "middle")
.attr("dx", 0)
.attr("dy", ".35em")
.text(function(d) {
return d.name;
}).call(getBB);
textNode.insert("rect","text")
.attr("width", function(d){return d.bbox.width})
.attr("height", function(d){return d.bbox.height})
.style("fill", "yellow");
function getBB(selection) {
selection.each(function(d){d.bbox = this.getBBox();})
}

Multidonut chart d3 legendds not working

I am a d3 newbie . I was trying to create a multidonut chart.
but I am not getting the legend columns.I have added my code here
http://jsfiddle.net/YsvT8/
i tried to add this code
var legend = d3.select("body").append("svg")
.attr("class", "legend")
.attr("width", radius * 2)
.attr("height", radius * 2)
.selectAll("g")
.data(color.domain().slice().reverse())
.enter().append("g")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", 24)
.attr("y", 9)
.attr("dy", ".35em")
.text(function(d) { return d; });
but my page is not loading.
I not able to figure out what changes I need make to get the legends on the left side of the page.
Try this code. It Will give you required output.
var legend = d3.select("body").append("svg")
.attr("class", "legend")
.attr("width", radius * 2)
.attr("height", radius * 2)
.selectAll("g")
.data(pairs) //Changed this line from your code
.enter().append("g")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", 24)
.attr("y", 9)
.attr("dy", ".35em")
.text(function(d) { return d.key; });//Changed this line from your code

Resources