As far as I know, the two following snippets should be equivalent. Both create some variables, add a svg element, append a group element, and append a second group.
Case 1:
var margin = {top: 15, bottom: 15, left: 50, right: 50},
height = 350,
width = 800;
var svg = d3.select("body").append("svg")
.attr('height', height + margin.top + margin.bottom)
.attr('width', width + margin.left + margin.right)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")");
Case 2:
var margin = {top: 15, bottom: 15, left: 50, right: 50},
height = 350,
width = 800;
var svg = d3.select("body").append("svg");
svg.attr('height', height + margin.top + margin.bottom)
.attr('width', width + margin.left + margin.right)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")");
However, in the first case, the second group element is a child of the first group, i.e. the DOM has the following structure:
<svg height="380" width="900">
<g transform="translate(50,15)">
<g class="x axis" transform="translate(0,350)">
And in the second case, both groups are child of the svg element:
<svg height="380" width="900">
<g transform="translate(50,15)">
<g class="x axis" transform="translate(0,350)">
In the first case you get back the appended group in the svg variable. It is a common problem I encountered it several times.
var svg = d3.select("body").append("svg")
.attr('height', height + margin.top + margin.bottom)
.attr('width', width + margin.left + margin.right)
.append("g") // you get this object back
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
Related
I am creating a visualisation using d3.js and svg images in PowerBi (this uses version 3 of d3).
I have got my visual working, however my legend is not rendering. I tested this in a browser, and the legend items appear in the elements of the page, but just aren't showing up.
My code for the legend items are
var pbi = {
width:1108,
height:636,
colors:[
"#A70240",
"#4A2366",
"#009A44",
"#A0D081",
"#01B5BB",
"#137B88",
"#5D6771",
"#CDC8C1"
]
var margin = {top: 20, right: 30, bottom: 30, left: 140},
width = pbi.width - margin.left - margin.right,
height = pbi.height - margin.top - margin.bottom,
legendleft = pbi.width - margin.right;
var ly = d3.scale.ordinal() // For legend
.rangeRoundBands([0, height], barPad, barOuterPad);
var svg = d3.select("#chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
----
ly.domain(rData.map(function(d) { return d.milestone; })); // Legend
var milestoneMap = {}; // Maps years to colours
var legendArray = []; // For legend
rData.forEach(function (d) {
var entry = d.year;
var rowEntry = {
entry: entry, // Axis label
milestone: d.milestone, // For colour lookup
date: d.date, // For X position of points
y: y(entry)
}
if (!(d.milestone in milestoneMap)) {
// First occurrence of each year saved to legend
legendArray.push({milestone: d.milestone});
rowArray.push(rowEntry);
var legend = svg.append("g").attr("id", "legend")
.attr("transform", "translate(" + legendleft + "," + margin.top + ")").selectAll(null)
.data(legendArray)
.enter();
// Legend agency labels
legend.append("text")
.attr("class", "milestoneLabel")
.attr("x", 25)
.attr("y", function(d) { return ly(d.milestone)+5; })
.style("stroke", "black")
.style("stroke-width", .3)
.text(function(d) { return d.milestone; });
legend.append("circle")
.attr("class", "legend")
.attr("cx", 12)
.attr("cy", function(d) { return ly(d.milestone); })
.attr("r", 8)
.style("stroke", "black")
.style("stroke-width", 1)
.style("fill", function(d, i) { return milestoneMap[d.milestone]; });
This is omitting code that calculates all other elements.
Why is it that the legend circle and label is appearing as an element on the page but isn't rendering anything?
Thanks
Turns out, I just needed to add my legend left to my svg.
From
var svg = d3.select("#chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
to
var svg = d3.select("#chart")
.attr("width", width + margin.left + margin.right + legendleft)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
In this datavisualization, I have a curious problem : my svg is not correctly aligned with the whole container.
I'm using a viewbox:
var svg = d3.select("#viz")
.attr("viewBox", [
margin.left,
margin.top,
(width+margin.left),
(height+margin.bottom)
].join(" "))
.append("svg")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
Does anyone have any idea why?
I'm following the box plot on this page Box Plot Link. In this example it reads in the box plot values from a data file. I'm trying to add a button that will read in new data from another data file and update the box plot.
Here's the code they use to first create the box plot.
var svg = d3.select("#viz").selectAll("svg")
.data(data)
.enter().append("svg")
.attr("class", "box")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.bottom + margin.top)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(chart.duration(1000));
and this to update the box plots every second
setInterval(function() {
svg.datum(randomize).call(chart.duration(1000));
}, 2000);
When I click on the update button it calls my snippet here:
var svg = d3.select("#viz").selectAll("svg")
.data(data)
.enter().append("svg")
.attr("class", "box")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.bottom + margin.top)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.call(chart.duration(1000));
However this won't update my boxplot. If I want the box plot to change, I have to first remove the box plot SVG first
d3.selectAll('svg').remove();
But I'd like to have an transition rather than new box plots. Why do I have to remove the previous SVG first? Any help?
So I made the following changes to the update of the data
var svg = d3.select("#viz").selectAll("svg").data(data)
svg.call(chart.duration(duration));
svg.enter().append("svg")
.attr("class", "box")
.attr("id", function (d, i) {
return "box_" + i;
})
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.bottom + margin.top)
.append("g")
.attr("id", function (d, i) {
return "g_" + i;
})
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(chart.duration(duration))
svg.exit().remove();
and there is actually some bugs with that box.js code that needs to be fixed which I found about here:
D3 Datum Update Boxplot
I'm attempting to load a d3.js bullet chart into an existing svg object:
<span id='element'><svg class="elm_12"></svg></span>
and in the js:
var svg = d3.select(elementid)
.data(data)
.enter().append("svg")
.attr("class", "bullet")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(chart);
I assume I can't add an svg element to an existing svg element. Any thoughts on the best way to accomplish this?
I was going to place this as a comment but there are simply too many code statements. What about this:
<span id='element'></span>
...
var svg = d3.select("#element")
.append("svg")
.attr("class", "bullet")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
svg
.data(data)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(chart);
Actually, this was enough for me to figure it out - THANK YOU.
var svg = d3.select(elementid)
.data(data)
.enter()
.append("svg")
.attr("class", "bullet")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(chart);
if I have an SVG...
var canvasBars = d3.select("#chart1").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate("+margin.left+","+margin.top+")");
If I modify the width variable (used to calculate the width attribute) How do I then select the width attribute and modify it with this new width variable?
this does not seem to work:
canvasBars.attr("width", width + margin.left + margin.right);
Your other option is to ensure the former 'canvasBars' variable is actually the canvas.
var svgSelection = d3.select("#chart1").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
var baseGroup = svgSelection.append("g")
.attr("transform", "translate("+margin.left+","+margin.top+")");
Your former 'canvasBars' I have renamed to 'svgSelection'. If you want to modify the "canvas" directly, your former idea will now work.
svgSelection.attr("width", width + margin.left + margin.right);