I have created GANTT chart using D3 Gannt Chart (http://bl.ocks.org/dk8996/5449641). I embed it to an div that I wish to position as following:
<div id="gantt" class="container">
<div id="gantt-deep">
<script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script>
<script type="text/javascript" src="js/lib/gantt-chart-d3v2.js"></script>
<script type="text/javascript" src="js/gantt.js"></script>
</div>
</div>
The problem is that when I run the code the SVG created by the script jumps out of the div as seen in the following code when looked in Firefox:
<div id="gantt" class="container">
<div id="gantt-deep">
<script src="http://d3js.org/d3.v3.min.js" type="text/javascript">
<script src="js/lib/gantt-chart-d3v2.js" type="text/javascript">
<script src="js/gantt.js" type="text/javascript">
</div>
</div>
<svg class="chart" width="990" height="490">
<g class="gantt-chart" width="990" height="490" transform="translate(150, 20)">
</svg>
My question is how do I position that svg element that is created?
In gnatt-chart-d3v2.js that was included in the example you provided there's the following line:
var svg = d3.select("body")
.append("svg")
.attr("class", "chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("class", "gantt-chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.attr("transform", "translate(" + margin.left + ", " + margin.top + ")");
From what I can tell this appends an svg element to the body. That's why the svg "jumps" out of your div - because it is appended to the body, not inserted where you include the script tags.
A possible solution to this would be to change "body" to whatever element you want to append the svg to, in your case it would be #gantt-deep, as you want to target the div with id gantt-deep.
This is a one-time solution, because it modifies the source code which doesn't allow for inserting an svg wherever. If you need another chart then you'd have to pass in a parameter to that gnatt function to indicate where you want it.
Actually, here's the modified version of gnatt-chart-d3v2.js:
// some code here
function gantt(tasks, element) { // modified this line
initTimeDomain();
initAxis();
var svg = d3.select(element) // modified this line
.append("svg")
.attr("class", "chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("class", "gantt-chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.attr("transform", "translate(" + margin.left + ", " + margin.top + ")");
// leave the rest untouched
And then when you initiate the chart call the function with another parameter, for instance gantt(tasks, "#gantt-deep");
I didn't test this, but it should work.
Related
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 + ")");
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);
Still learning d3.js.
I would like to ignore the selection of an SVG panel when using .selectAll("svg").
I am building a visualization comprising four SVG panels. The top SVG panel is used to display header/title information for the visualization.
var svgHeader = d3.select("body")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", 100)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.append("g");
The next two SVG panels are dynamically created using a range of two numbers representing two years.
var svg = d3.select("body")
.selectAll("svg")
.data(d3.range(2012, 2013))
.enter().append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", 200)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.append("g");
The final SVG contains detail information as the user interacts with the visualization.
Problem: I want to exclude the first SVG panel from the .selectAll("svg") which is used to create the two middle panels. I would like to dynamically build SVG panels and have them locate underneath the previously created header SVG.
Is there any way to exclude the header SVG when dynamically creating the middle panels?
I think the best way you should be going about this is taking advantage of classes and adding an appropriate class to the different svgs and then selecting based on the class rather than the svg. This way you know what each of the svgs represent and you can easily reference them.'
var svgHeader = d3.select("body")
.append("svg")
.attr("class", "svgHeader")
.attr("width", width + margin.left + margin.right)
.attr("height", 100)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.append("g");
And then the other two you add a different class name
var svg = d3.select("body")
.selectAll("svg")
.data(d3.range(2012, 2013))
.enter().append("svg")
.attr("class", "data")
.attr("width", width + margin.left + margin.right)
.attr("height", 200)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.append("g");
Now you can do d3.selectAll("svg.data") and select only svg elements with the class data
Alternatively, you can embed your svg elements in different divs. Assuming you have a div whose id is 'center-div' the following snippet returns you only the svgs contained in it.
d3.selectAll("#center-div svg")
Please also consider that you can append whatever DOM element via d3, so divs can be dinamically generated.