Inserting new line in .html() using D3 - text

I have JS objects like these:
{
name : "sldjfvbsdolbobgsd",
myWeight : "[1,65,34,4,6,7,4,4,7,8,5]"
}
For each such object, there is a circle in an SVG element in force-directed layout.
'i' is a global variable. I am displaying the 'name' element of each object like this currently:
node.append("text")
.attr("class", "nodetext")
.attr("dx", 12)
.attr("dy", ".35em")
.text(function (d) { return d.name; });
I want to display both the name and the current weight of each object. I tried something like this but it didn't work:
*node.append("text")
.attr("class", "nodetext")
.attr("dx", 12)
.attr("dy", ".35em")
.html(function (d) { return d.name; } + "<br />" + function (d) { return d.myWeight[i]; });*
The output for every node is like this:
function (d) { return d.name; }function (d) { return d.myWeight[i]; }
whereas I want:
sldjfvbsdolbobgsd
34 // say, if `i` is 2
Please help me with displaying it correctly.

The reason your code doesn't work is because d3 expects the argument passed to .html to be either a function or a constant. In your case the + causes the functions to be converted to strings. So the constant that you pass to .html ends up being:
function (d) { return d.name; } <br /> function (d) { return d.myWeight[i]; }
However, the SVG text element does not render the <br/> tag and so you don't get the line break.
The proper way to add line breaks inside SVG text is to use tspan with dy offets:
var text = node.append("text")
.attr("class", "nodetext")
.attr("dx", 12)
.attr("dy", ".35em");
text.append("tspan")
.attr("dy", 0)
.attr("x",0)
.text(function(d) {return d.name;});
text.append("tspan")
.attr("dy", "1.2em") // offest by 1.2 em
.attr("x",0)
.text(function(d) {return d.myWeight;});

Related

D3 filter data every three rows in line chart label

I am working on a line chart based on the tutorial https://datawanderings.com/2019/11/01/tutorial-making-an-interactive-line-chart-in-d3-js-v-5/
For the code block below, I want to add points every three rows (every three days):
//---------------------------POINTS-----------------------------//
lines.selectAll("points")
.data(function(d) {return d.values})
.enter()
.append("circle")
.attr("cx", function(d) { return xScale(d.date); })
.attr("cy", function(d) { return yScale(d.measurement); })
.attr("r", 10)
.attr("class","point")
.style("opacity", 1);
I followed the link Add a Circle for Every nth Data Element d3.js, and tried to change it to
//---------------------------POINTS-----------------------------//
lines.selectAll("points")
.data(slices.filter(function(d,i) {return i%3 == 0}))
.enter()
.append("circle")
.attr("cx", function(d, i) { return xScale(d.date); })
.attr("cy", function(d, i) { return yScale(d.measurement); })
.attr("r", 10)
.attr("class","point")
.style("opacity", 1);
However, all the points appears to locate at the left top corner of the graph.
May I ask what I did wrong, and how can I modify my code to make the circles to locate on the lines as showing the circles every three days? Thank you!

Embellishing D3 SVG circle pack diagram

My D3 circle pack looks like this: (also accesible via jsfiddle)
However, I would like the diagram to look like this: (don't pay attention on labels, or circle pack placement, they are not essential for my case; I meant just co show "3d" looks of circles, and their coloring)
What would be the good way to achieve this?
After #Delapouite answer, I put together another jsfiddle:
The key code is:
var data2 = pack.nodes(data);
var grads = svg.append("defs").selectAll("radialGradient")
.data(data2)
.enter()
.append("radialGradient")
.attr("gradientUnits", "objectBoundingBox")
.attr("cx", 0)
.attr("cy", 0)
.attr("r", "100%")
.attr("id", function(d, i) { return "grad" + i; });
grads.append("stop").attr("offset", "0%").style("stop-color", "white");
grads.append("stop").attr("offset", "100%").style("stop-color", "navy");
and
var circles = vis.append("circle")
.attr("stroke", "black")
.attr("fill", function(d, i) {
return !d.children ? "url(#grad" + i + ")" : "beige";
})
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.attr("r", function(d) { return d.r; });
You can fake the 3D effect of each ball by applying a soft radial gradient to the fill property of the circles :
https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Gradients

d3 - foreign object - getting the right scope

I am using foreignObject to use HTML inside a d3 circle.
What I want:
use HTML inside d3 objects
reach the normal scopes outside of HTML code
Now I have problems to get the right scope for using functions.
If I use "this" inside the
.html(".....")
I get the "g" element of d3. So is there a way to get to my normal scopes? Or is there even a more elegant way to solve this problem?
var foreign = that._foreignObject = d3.select(this).append("foreignObject")
.attr("width", 30)
.attr("height", 30)
.append("xhtml:div")
.style("font", "15px 'Arial'")
.html("<button name=Klickmich type=button value=play onclick=>");
EDIT: 16.12.2013:
var nodeEnter = node.enter().append("svg:g")
.attr("class", "node")
.attr("transform", function (d) {
return "translate(" + source.y0 + "," + source.x0 + ")";
})
//click once
.on("click", function (d) {
if (that._foreignObject) {
that._foreignObject.remove();
}
that.toggle(d);
that.update(d);
that.onNodeClick(d);
var circle = d3.select(this).select("circle")
.transition()
.duration(750)
.attr("r", 17)
.attr("width", 40)
.attr("heigth", 40)
.style("stroke-dasharray", ("5,2"))
.style("stroke-width", 2)
.style("stroke", "black");
var button = "<button name=Klickmich type=button value=play onclick=>";
var foreign = that._foreignObject = d3.select(this).append("foreignObject")
.attr("width", 30)
.attr("height", 30)
.append("xhtml:div")
.style("font", "15px 'Arial'")
.html(button)
.on("click", function (d) {
console.log("heyho");
});
d3.select(this).select("text").transition()
.duration(750)
.style("font-size", 15)
.style("font-weight", "bold")
.attr("x", function (d) {
return d.children || d._children ? -20 : -20;
});
})
If i Click on my html button i set
this._clicked = 1;
And the onClick function of the node is build as follows:
.on("click", function (d) {
if (this._clicked == null) {
// some code
}
}
this._clicked = null;

How to redraw the svg filled with circles in D3?

I'm following this zoom example. In my case, I don't know how to redraw my data for my svg.
In the example, the svg is initialized like this
chartBody.append("svg:path")
.datum(data)
.attr("class", "line")
.attr("d", line);
var line = d3.svg.line()
.x(function (d) {
return x(d.date);
})
.y(function (d) {
return y(d.value);
});
And is redrawn like this in function "zoomed"
svg.select(".line")
.attr("class", "line")
.attr("d", line);
While in my case
Init:
userSvg.selectAll("circle")
.data(userNodes.slice(1))
.enter().append("svg:circle")
.on("click", function(d){ console.log(d.ind); })
.on("mousemove", function(d){ brushOnUser(d.ind); })
.on("mouseout", function(){ brushOnUser(); })
.attr("r", function(d) { return d.radius; })
.attr("cx", function(d, i) { return userNodesScaleX(d.x); })
.attr("cy", function(d, i) { return userNodesScaleY(d.y); })
.style("fill", function(d, i) { return 'gray'; });
Redraw:
userSvg.selectAll("circle")
.attr("class", "circle");
Of course this redraw doesn't work.
So how do I redraw this?
In the redraw function, you need to set all the attributes that are changed. For a line, this is basically only the d attribute, as it contains all the information that determines how the line is drawn. For a circle, this would be the position of the circle and the radius. That is, your redraw function would look something like this.
userSvg.selectAll("circle")
.attr("r", function(d) { return d.radius; })
.attr("cx", function(d, i) { return userNodesScaleX(d.x); })
.attr("cy", function(d, i) { return userNodesScaleY(d.y); });
Depending on what you're changing, you may have to set a different set of attributes. That is, if you're not changing the radius, then there's no need to set that, but if you're changing the fill color, you would need to set that as well.

Modifying SVG path opacity and it's marker

I'm trying to make some modifications to a path, defined using D3 programmatically. The change I want to make is quite simple, modifying the opacity of the path. The problem I've got is while the path itself will change, the end marker does not, and I'm not quite sure how to make it do so.
The marker is defined as so:
// define arrow markers for graph links
svg.append('svg:defs').append('svg:marker')
.attr('id', 'end-arrow')
.attr('viewBox', '0 -5 10 10')
.attr('refX', 6)
.attr('markerWidth', 3)
.attr('markerHeight', 3)
.attr('orient', 'auto')
.append('svg:path')
.attr('d', 'M0,-5L10,0L0,5')
.attr('fill', '#CCCCCC');
The path:
// Create the links between the nodes
var links = svg.append("g")
.selectAll(".link")
.data(data.links)
.enter()
.append("path")
.attr("class", "link")
.attr("d", sankey.link())
.style('marker-end', "url(#end-arrow)")
.style("stroke-width", function (d) { return Math.max(1, d.dy); })
.sort(function (a, b) { return b.dy - a.dy; });
The code that I use to change the paths, which doesn't update the markers:
d3.selectAll("path.link")
.filter(function (link) {
// Find all the links that come to/from this node
if (self.sourceLinksMatch(self, link, node)) {
return true;
}
if (self.targetLinksMatch(self, link, node)) {
return true;
}
return false;
})
.transition()
.style("stroke-opacity", 0.5);
Can anyone suggest what I might need to change to modify the marker-end style too?
Modifying the opacity instead of the stroke-opacity works.. so
d3.selectAll("path.link")
.transition()
.style("stroke-opacity", 0.5);
becomes
d3.selectAll("path.link")
.transition()
.style("opacity", 0.5);
You should be able to do the same for the marker path definition:
d3.selectAll("marker path")
.transition()
.style("stroke-opacity", 0.5);
You can set define preset names for your arrow markers
// build the arrow.
svg.append("svg:defs").selectAll("marker")
.data(["HELPS","HELPED_BY","DAMAGES","REPELS","FAMILY", "KINGDOM"]) // Different link/path types can be defined here
.enter().append("svg:marker") // This section adds in the arrows
.attr("id", String)
.attr("viewBox", "0 -5 10 10")
.attr("refX", 15)
.attr("refY", -1.5)
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("orient", "auto")
.append("svg:path")
.attr("d", "M0,-5L10,0L0,5");
// add the links and the arrows
var path = svg.append("svg:g").selectAll("path")
.data(force.links())
.enter().append("svg:path")
.attr("class", function(d) { return "link " + d.type; })
.attr("marker-end", function(d) { return "url(#" + d.type +")"; });
And configure their respective styles with CSS
marker#HELPS{fill:green;}
path.link.HELPS {
stroke: green;
}
marker#HELPED_BY{fill:#73d216;}
path.link.HELPED_BY {
stroke: #73d216;
}
marker#DAMAGES{fill:red;}
path.link.DAMAGES {
stroke: red;
}

Resources