I am working off of the d3 "Calendar View" example and would like to display 1 year at a time with some buttons to progress or regress the year being shown. In this example all of the data (years 1990-2010) arrive with the d3.csv call and is being rendered in a chart defined by...
var svg = d3.select("body")
.selectAll("svg")
.data(d3.range(1990, 2011))
.enter().append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + ((width - cellSize * 53) / 2) + "," + (height - cellSize * 7 - 1) + ")");
I would like to be able to update the data attribute on the d3 class and update the chart via a clickable event.
For example maybe showing the year 2010 is default like this...
var svg = d3.select("body")
.selectAll("svg")
.data(d3.range(2010, 2011))
.enter().append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + ((width - cellSize * 53) / 2) + "," + (height - cellSize * 7 - 1) + ")");
and i just want to modify the data element to d3.range(2009, 2010) on an event and redraw the chart.
I've tried removing and re-rendering the chart but haven't had success. There must be an easier way to do this.
In your code above you are only ever calling enter which is designed to add new elements to the DOM. There's a really good article from the author Mike Bostock called 3 Little Circles that explains how the enter/exit/update pattern works (note that it was for v3 though).
const join = d3
.select("body")
.selectAll("svg")
.data(d3.range(2010, 2011), d => d);
First thing to note is that we've provided a key function to .data(). In this case the key is literally the value d which will return 2010 and 2011 for the two data points.
join.exit().remove();
Get rid of the old calendars!
join.enter()
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + ((width - cellSize * 53) / 2) + "," + (height - cellSize * 7 - 1) + ")")
.merge(join)
... do more stuff
The next importing bit you're missing is to update the existing stuff. You do that by taking your enter selection and calling merge(join). That gives you all the new and updated items so you can start changing attributes/styles or do further nested joins on.
Related
I'm trying to implement box plots as part of a data visualization interface that uses d3 and AngularJS. I'm working with this box plot package: https://bl.ocks.org/mbostock/4061502.
However, I can't figure out which part of the sample code controls the positioning of the box plots. In the example, the five box plots are arranged sequentially. When I try to generate my plots, they all appear on top of each other.
Here is the code that I'm using to generate the box plots:
boxplots = svg.selectAll("svg")
.data(boxPlotData)
.enter().append("svg")
.attr("class", "box")
.attr("width", boxWidth + margin.left + margin.right)
.attr("height", boxHeight + margin.bottom + margin.top)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(chart);
Here's the code for how my svg canvas is created. This is done in an angular directive:
template:"<svg width='825' height='600'></svg>",
link: function($scope, elem){
var d3 = $window.d3;
var rawSvg=elem.find('svg'); // this is the svg created in the template
var width = rawSvg[0].attributes[0].value;
var height = rawSvg[0].attributes[1].value;
var svg = d3.select(rawSvg[0]);
Edit: not perfect yet but getting there:
What you need is an ordinal scale to position the svg-elements for the boxes within the parent svg. Assuming width represents the width of your parent svg element and data is an array of your data elements, you can use this to create the scale:
const x = d3.scaleBand()
.range( [0, width] )
.domain( data.map( (el,i) => i ) );
Within the svg creation you can now use
boxplots = svg.selectAll("svg")
.data(boxPlotData)
.enter().append("svg")
.attr( "x", (d,i) => x(i) ) // this is added
.attr("class", "box")
.attr("width", boxWidth + margin.left + margin.right)
.attr("height", boxHeight + margin.bottom + margin.top)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(chart);
PS: This assumes you use v4 of d3js. The syntax in v3 for the scale is different.
PPS: I currently can not test the code, but it should work like described.
I am trying following code to make responsive chart with D3-4.2.2.
import * as D3 from 'd3';
let d3:any = D3;
var container = d3.getElementsByTagName('line-graph'),
width = container[0].offsetWidth,
height = container[0].offsetHeight;
var svg = d3.select(this.htmlElement).append("svg")
.attr("class", "line-graph")
.attr("width", "100%")
.attr("height", "100%")
.attr('viewBox', '0 0 ' + Math.min(width, height) + ' ' + Math.min(width, height))
.attr('preserveAspectRatio', 'xMinYMin')
.append("g")
.attr("transform", "translate(" + Math.min(width, height) / 2 + "," + Math.min(width, height) / 2 + ")");
Browser console gives
d3.getElementsByTagName is not a function
Any suggestion are appreciated.
Thank You
Change: d3.getElementsByTagName('line-graph')
to: d3.select('line-graph')
There is no function getElementsByTagName in D3.js. This is a native JavaScript method of the document object. Thus, you have to use document:
var container = document.getElementsByTagName('line-graph'),
width = container[0].offsetWidth,
height = container[0].offsetHeight;
document.getElementsByTagName returns a node list, and that's why you need the index (in your case, [0]) after container.
Here is the documentation: https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementsByTagName
PS: I never heard about a tag named "line-graph". Maybe you want getElementsByClassName, or getElementById.
again I am struggling with d3.js. I have a working Line Chart and partially working mouseover. The goal is to limit the mouseover solely to the svg element, like Mark has it working in his answer Multiseries line chart with mouseover tooltip
I have created a Plunker with it. My is-situation is like that.
http://plnkr.co/edit/Jt5jZhnPQy4VpjwY3YBv?p=preview
And I have tried things like:
http://plnkr.co/edit/lRMfa0OiDWEXWYBAjoPd?p=preview
by adding:
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
But it's always pushing the circles and the bar out of the chart, I am fiddling for some days now and would be extremely glad if someone happens to point me in the right direction.
Thank you in advance.
Here is the plunker:
http://plnkr.co/edit/MEtbBqN5qr82yr0CNhUN?p=preview
I simply changed the size of your rectangle:
mouseG.append('rect')
.attr("x", margin.left)
.attr("y", margin.top)
.attr('width', w - margin.left - margin.right)
.attr('height', height - margin.bottom - margin.top)
PS: I don't know if you want the line limited to the chart area as well, but if you want, this is the plunker: http://plnkr.co/edit/RP4uYKBYnHtX1SvYsLKq?p=preview
Instead of giving width as width :
mouseG.append('svg:rect')
.attr('width', width)
do this (give the width of the group same as domain x for the line chart)
mouseG.append('svg:rect')
.attr('width', w - padding * 2)
Reason:
var xScale = d3.time.scale()
.domain([xExtents[0], xExtents[1]])
.range([padding, w - padding * 2]);
Your width of the x scale is w - padding * 2 so the width of the group listening to the mouse event should be same.
working code here
I am trying to create an export function for
D3 Calendar View
But the problem is that the original code is creating for each month one svg. The export is then only exporting the first svg. How can I make it happen that it is one big svg with all the months inside as group?
The code in question is this:
var svg = d3.select("body").selectAll("svg")
.data(d3.range(1990, 2011))
.enter().append("svg")
.attr("width", width)
.attr("height", height)
.attr("class", "RdYlGn")
.append("g")
.attr("transform", "translate(" + ((width - cellSize * 53) / 2) + "," + (height - cellSize * 7 - 1) + ")");
Thanks for any ideas.
gb5256
Here's a quick refactor which places it in a single svg and moves each year into it's own g.
Sizes become:
var width = 960,
yearHeight = 136, // height of each year
height = yearHeight * 20, // height of entire svg
cellSize = 17; // cell size
Append initial SVG with large height:
var svg = d3.select("body").append("svg")
.attr("width",width)
.attr("height",height);
Create g for each year and translate to position:
var g = svg.selectAll(".yearG")
.data(d3.range(1990, 2011))
.enter().append("g")
.attr("class","yearG")
.attr("class", "RdYlGn")
.attr("transform", function(d,i){
return "translate(" + ((width - cellSize * 53) / 2) + "," + ((yearHeight - cellSize * 7 - 1) + (yearHeight * i)) + ")";
});
Append elements to those gs:
g.append("text")
...
var rect = g.selectAll(".day")
...
g.selectAll(".month")
...
Here's an updated gist.
If you are not bound by your restriction to put it into one group, you could as well nest all svgs into one outer svg. In this case just one line needed to be changed. Instead of
var svg = d3.select("body").selectAll("svg")
you would go for
var svg = d3.select("body").append("svg").selectAll("svg")
Your export might then just grab the outer container svg containing all nested svgs.
I'm creating a diagram with D3 and JSON which is based on this:
http://bl.ocks.org/mbostock/4063550
I'm completely new to this...and I just can't seem to figure out how to move the position of the tree to the right or left of the page. Seems simple enough?
Reason I'm asking is because some of my text are cut out on the left side of screen.
Any help appreciated!
The easiest way to do this is to adjust the transform parameter of the top-level g element. In the example, it is created like this.
var svg = d3.select("body").append("svg")
.attr("width", diameter)
.attr("height", diameter - 150)
.append("g")
.attr("transform", "translate(" + diameter / 2 + "," + diameter / 2 + ")");
To move everything to the right, you would need to add something to the x translation, e.g.
var svg = d3.select("body").append("svg")
.attr("width", diameter)
.attr("height", diameter - 150)
.append("g")
.attr("transform", "translate(" + (diameter / 2 + 10) + "," + diameter / 2 + ")");