This is somewhat of an abstract question. I'm referencing Mike Bostock's code for importing an external svg file: http://bl.ocks.org/mbostock/1014829
My plan is to use an external SVG as an icon (or sprite) that will be used multiple times in one interface. Is there a method for creating a copy of the xml.documentElement below? In summary, how does one create multiple instances of one svg file without loading the file for each instance?
d3.xml("rect01.svg", "image/svg+xml", function(xml) {
document.body.appendChild(xml.documentElement);
});
The end goal is to add an external svg to each cell in the grid created from this example: http://bl.ocks.org/bunkat/2605010
Referencing the link above, how does one work the xml.documentElement data into the grid in the code below, so that the external svg is visible, rather than a rectangle?
var col = row.selectAll(".cell")
.data(function (d) { return d; })
.enter().append("svg:rect")
.attr("class", "cell")
.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; })
.attr("width", function(d) { return d.width; })
.attr("height", function(d) { return d.height; })
.on('mouseover', function() {
d3.select(this)
.style('fill', '#0F0');
})
.on('mouseout', function() {
d3.select(this)
.style('fill', '#FFF');
})
.on('click', function() {
console.log(d3.select(this));
})
.style("fill", '#FFF')
.style("stroke", '#555');
}
Referencing this jsfiddle, the line of code I needed is cloneNode(true):
.each(function(d, i){
var plane = this.appendChild(importedNode.cloneNode(true));
d3.select(plane).select("path").attr("fill", "blue");
})
Related
Plunker: https://next.plnkr.co/edit/17t5ujwC71IK3PCi
Why is following not adding a "test" label to all my polygons?
/* NOT Working code */
groups.selectAll('.path_placeholder')
.enter()
.append('text')
.text("test")
Update
.enter() wasn't required as mentioned by Xavier. Removing it showed "test" for all nodes. But why then its not working when I do provide data and use enter() as following:
groups.selectAll('.path_placeholder')
.data(groupIds, function(d) {
return d;
})
.enter()
.append('text')
.text(function(d){
console.log(d);
return d;
})
I am trying to be able to show label for each of my polygon and for now just trying to add a dummy label to each of them.
Your problem here is the paths is a <path> selection, not a <g> one:
paths = groups.selectAll('.path_placeholder')
.data(groupIds, function(d) { return +d; })
.enter()
.append('g')
.attr('class', 'path_placeholder')
.append('path')//this makes the selection pointing to <path> elements
.attr('stroke', function(d) { return color(d); })
.attr('fill', function(d) { return color(d); })
.attr('opacity', 0);
Because of that, when you do...
groups.selectAll('.path_placeholder')
.data(groupIds, function(d) {
return d;
})
.enter()
//etc...
... your "enter" selection is empty, because you already have data associated to that paths selection.
Besides that, it makes little sense using a proper "enter" selection for the texts, since the data is the same data bound to the groups.
Solution: the solution here, which is the idiomatic D3 for this situation, is creating an actual <g> selection.
We can do that by breaking the paths selection, and giving it another name:
pathGroups = groups.selectAll('.path_placeholder')
.data(groupIds, function(d) {
return +d;
})
.enter()
.append('g')
.attr('class', 'path_placeholder');
Then you can just do:
paths = pathGroups.append('path')
.attr('stroke', function(d) {
return color(d);
})
.attr('fill', function(d) {
return color(d);
})
.attr('opacity', 0)
texts = pathGroups.append('text')
.text(function(d) {
return d;
});
Here is the forked Plunker: https://next.plnkr.co/edit/31ZPXIvSI287RLgO
var bar = chart.selectAll("g")
.data(data)
.enter()
.append("g")
.attr("transform", function(d, i) {
return "translate("+barWidth*i+",0)";
})
.on("mouseover", function() {
d3.select(this)
.attr("fill", "red");
})
.on("mouseout", function(d, i) {
d3.select(this)
.attr("fill", "blue");
});
I am trying to do a mouseover to show data. I am starting by trying to change the colour on a mouseover as per code above.
http://codepen.io/JohnnyBizzel/pen/xqbjVQ
I updated your codepen
A couple of things wrong here:
You are trying to apply the transition to the g and not the rect
Instead of attr you want to use style
Here is the solution:
.on("mouseover", function() {
d3.select(this).select('rect').style('fill', 'red');
})
.on("mouseout", function(d, i) {
d3.select(this).select('rect').style('fill', 'blue');
});
You need to select the shape that is into your element and his style function to change the color.
In reference to D3 Sankey (https://bost.ocks.org/mike/sankey/), I wished to inquire about the way to color the nodes using some categorical variable in JSON file.
Link to the JSON file being used in the actual code:
https://bost.ocks.org/mike/sankey/energy.json
My question therefore is that if I provide some info pertaining to the category of "name":
{"name":"Solar PV", "category":"A"},
{"name":"Solar Thermal","category":"A"},
{"name":"Solar","category":"B"},
{"name":"Tidal","category":"C"},
{"name":"UK land based bioenergy","category":"A"},
{"name":"Wave","category":"A"},
{"name":"Wind","category":"B"}
Then what changes should be made to the source code to color the nodes according to the category.
P.S: Not an expert in D3/JS, please pardon my limited skills if this question appears too naive.
Thanks
In the linked example you'll see this block of code:
node.append("rect")
.attr("height", function(d) { return d.dy; })
.attr("width", sankey.nodeWidth())
.style("fill", function(d) { return d.color = color(d.name.replace(/ .*/, "")); })
.style("stroke", function(d) { return d3.rgb(d.color).darker(2); })
.append("title")
.text(function(d) { return d.name + "\n" + format(d.value); });
Replace the .style("fill" line with:
.style("fill", function(d) { return d.color = color(d.category); })
In Bostock's code, this is the line that colours the rectangles:
.style("fill", function(d) {
return d.color = color(d.name.replace(/ .*/, ""));
})
So, just change that to:
.style("fill", function(d) {
return d.color = color(d.category);
})
I have a multiline graph that displays 10 series of data, I am trying to get the lines to update with new data but for some reason I can't get that happening.
The transition with the new data is working for the points on the lines so I assume I am not selecting the right elements but for the life of me I can't figure out where my mistake is.
At one point I had one line changing which indicated it was only updating from the first index of the data array.
Any insight would be appreciated:
Initial Series creation-
var series = svg.selectAll(".series")
.data(seriesData)
.enter().append("g")
.attr("class", "series");
series.append("path")
.attr("id", function (d) {
return d.name;
})
.attr("stay", "false")
.attr("class", "line")
.attr("d", function (d) {
d.line = this;
return line(d.values);
})
.attr("opacity", ".2")
.on("click", function () {
fadeOuts(this);
})
.style("stroke", function (d) {
return strokeCol;
})
.style("stroke-width", "4px")
.style("fill", "none");
Update function:
This is where I am stuck, the points respond to the new data but the paths do not.
series.data(newseriesData);
series.selectAll("path")
.attr("id", function (d) {
return d.name;
})
.attr("d", function (d) {
d.line = this;
return line(d.values);
})
.attr("opacity", ".2")
.on("click", function () {
fadeOuts(this);
})
.style("stroke", function (d) {
return strokeCol;
})
.style("stroke-width", "4px")
.style("fill", "none");
series.selectAll(".point")
.data(function (d) {
return d.values;
})
.transition()
.attr("cx", function (d) {
return x(d.label) + x.rangeBand() / 2;
})
.attr("cy", function (d) {
return y(d.value);
})
.style("fill", function (d) {
return color(d.name);
})
.style("stroke", "grey")
.style("stroke-width", "2px")
.on("mouseover", function (d) {
showPopover.call(this, d);
})
.on("mouseout", function (d) {
removePopovers();
})
Yes this is a university project, this is the last piece of work in a solid 50+ hour effort on this and I'd just like to get it knocked out.
The short answer is that instead of series.selectAll("path") you should use series.select("path"). Remember that series is already a selection, and the subselection is done for each element in it. You've appended exactly one element to each of the selection, so .select() is fine and no .selectAll() is required.
The main difference this makes is that .select() inherits the data from the parent selection, while .selectAll() doesn't -- when doing .selectAll() the data is simply not updated and therefore no change occurs.
i played with this example of a force directed graph layout.
www.bl.ocks.org/GerHobbelt/3071239
or to manipulate directly, here with fiddle,
http://jsfiddle.net/BeSAb/
what i want was to replace the circle element
node = nodeg.selectAll("circle.node").data(net.nodes, nodeid);
node.exit().remove();
node.enter().append("circle")
.attr("class", function(d) { return "node" + (d.size?"":" leaf"); })
.attr("r", function(d) { return d.size ? d.size + dr : dr+1; })
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.style("fill", function(d) { return fill(d.group); })
.on("click", function(d) {
console.log("node click", d, arguments, this, expand[d.group]);
expand[d.group] = !expand[d.group];
init();
});
with a group (g) element that contains a svg foreignObject
node = nodeg.selectAll("g.node").data(net.nodes, nodeid);
node.exit().remove();
var nodeEnter = node.enter().append("foreignObject")
//simplified for this demo
.attr("class", function(d) { return "node" + (d.size?"":" leaf"); })
.attr('width', '22px')
.attr('height', '22px')
.attr('x', -11)
.attr('y', -11)
.append('xhtml:div')
.style("background",function(d){return fill(d.group)})
.style("width","20px")
.style("height","20px")
.style("padding","2px")
.on("click", function(d) {
console.log("node click", d, arguments, this, expand[d.group]);
expand[d.group] = !expand[d.group];
init();
});
The Graph is build correct but if i try to expand a node by clicking it, it seems that the graph isn't updated. So that all old nodes are duplicated.
i make an other Fiddle where you can show this problem by clicking a node.
http://jsfiddle.net/xkV4b/
does anyone know what i forgot, or what the issue is?
Thank you very much!
Your enter append should probably match your selection on nodeg. But even then it appears that d3 has some trouble selecting 'foreignObject' things. That may be a question/issue to bring up on the d3 google group - it may be a bug.
However you can get around it by just selecting on the class. I updated the code to read:
node = nodeg.selectAll(".fo-node").data(net.nodes, nodeid);
node.exit().remove();
var nodeEnter = node.enter().append("foreignObject")
.attr("class", function(d) { return "fo-node node" + (d.size?"":" leaf"); })
.attr('width', '22px')
...
Which seems to work.