Path not showing d3 (binding issue?) - javascript
I've been working on a project using fake data and sketches I've found online to create a line graph. However, I can't display paths with a JSON file containing numerical data. This is not the final data but I'm using the field "Id" as numerical value.
I should be able to see a diagonal line, but nothing shows on the graph.
<body>
<script>
var margin = {top: 20, right: 30, bottom: 30, left: 50},
width = 900 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom,
a = 3.779528*5,
f = width/height,
ticksX = 1+ (width/a),
ticksY = 1+ (height/a*f)
c = 1 + (width/a*10);
var x = d3.scale.linear()
.domain([194, 2229])
.range([0, width]);
var y = d3.scale.linear()
.domain([194, 2229])
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.tickSize(-height)
.tickPadding(10)
.tickSubdivide(true)
.ticks(ticksX)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.tickPadding(10)
.tickSize(-width)
.tickSubdivide(true)
.ticks(ticksY)
.orient("left");
var zoom = d3.behavior.zoom()
.x(x)
//.y(y)
.scaleExtent([1, 10])
.on("zoom", zoomed);
var svg = d3.select("body").append("svg")
.call(zoom)
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.json("pacientes.json", function(error, data) {
data.forEach(function(d) {
d.Id = +d.Id;
d.Edad = +d.Edad;
console.log(d.Id);
});
x.domain(d3.extent(data, function(d) { return d.Id; }));
y.domain(d3.extent(data, function(d) { return d.Id; }));
svg.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
var line = d3.svg.line()
.interpolate("linear")
.x(function(d) { return x(d.Id); })
.y(function(d) { return y(d.Edad); });
svg.selectAll('svg-path')
.data(data)
.enter()
.append("path")
.attr("x", function(d) { return x(d.Id); })
.attr("y", function(d) { return x(d.Id); })
.style("stroke-width", 10)
.attr("class", "line")
.attr("clip-path", "url(#clip)")
.attr('stroke', 'blue')
.attr("d", line);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
svg.append("g")
.attr("class", "y axis")
.append("text")
.attr("class", "axis-label")
.attr("transform", "rotate(-90)")
.attr("y", (-margin.left) + 10)
.attr("x", -height/2)
.text('mV');
}); // closing line
function zoomed() {
svg.select(".x.axis").call(xAxis);
svg.select(".y.axis").call(yAxis);
svg.selectAll('path.line').attr('d', line);
);
}
</script>
</body>
</html>
I'm pretty convinced that my error is here
svg.selectAll('svg-path')
.data(data)
.enter()
.append("path")
.attr("x", function(d) { return x(d.Id); })
.attr("y", function(d) { return x(d.Id); })
.style("stroke-width", 10)
.attr("class", "line")
.attr("clip-path", "url(#clip)")
.attr('stroke', 'blue')
.attr("d", line);
The data is being loaded correctly and the SVGs are being created (as you can see from the pictures). Any help will be appreciated.
I did not add the CSS, but it should be a blue line.
Thank you!
Related
How to dynamically create append svg line using d3js?
I am attempting to draw markers to a zoomable / pannable graph. My current version is available at https://jsfiddle.net/n2enn80o/ raw = {"l2":[-58,-44,-31,-21,-17,-16,-16,-15,-15,-14,-13,-12,-11,-10,-9,-7,-5,-3,-1,2,4,8,11,14,17,20,24,28,32,37,41,46,51,56,60,64,69,73,77,82,84,87,87,87,87,85,82,77,73,66,60,53,46,39,33,26,20,13,8,4,-1,-6,-10,-14,-16,-19,-22,-25,-26,-27,-28,-29,-30,-31,-31,-32,-32,-32,-32,-31,-31,-31,-31,-31,-30,-31,-30,-30,-30,-30,-29,-28,-27,-27,-27,-27,-27,-26,-26,-26,-26,-26,-26,-26,-26,-25,-25,-25,-25,-25,-25,-25,-25,-25,-24,-24,-24,-25,-25,-24,-23,-23,-23,-23,-23,-23,-23,-23,-23,-22,-22,-22,-22,-22,-22,-21,-21,-21,-21,-20,-20,-20,-20,-19,-18,-16,-13,-10,-6,-2,1,4,7,9,12,14,16,16,16,15,13,9,6,2,-1,-4,-7,-10,-11,-12,-14,-15,-16,-17,-18,-19,-19,-20,-20,-20,-20,-20,-20,-20,-24,-32,-45,-54,-50,-25,25,97,182,252,270,220,126,35,-25,-53,-65,-71,-70,-60,-46,-32,-22,-18,-16,-16,-15,-14,-13,-13,-12,-11,-10,-8,-7,-5,-3,-1,1,4,7,10,14,17,21,24,28,32,36,40,45,50,55,59,64,69,72,77,81,84,87,88,88,87,84,82,77,73,67,61,54,47,39,33,26,20,14,9,4,-1,-6,-9,-13,-16,-19,-22,-24,-26,-28,-29,-29,-30,-30,-31,-32,-32,-32,-31,-31,-32,-31,-31,-31,-31,-30,-30,-29,-29,-29,-29,-28,-28,-27,-27,-28,-28,-26,-26,-26,-26,-26,-26,-26,-26,-25,-25,-25,-25,-25,-25,-26,-26,-25,-24,-24,-24,-24,-24,-24,-24,-23,-23,-23,-23,-23,-23,-23,-24,-22,-22,-22,-22,-22,-22,-22,-21,-22,-20,-20,-20,-20,-20,-19,-18,-16,-13,-10,-8,-3,0,4,7,9,11,13,15,16,16,16,14,11,6,2,-2,-4,-7,-9,-11,-12,-14,-16,-17,-18,-19,-19,-19,-19,-20,-20,-20,-20,-20,-20,-23,-30,-43,-54,-53,-30,17,88,172,245,271,230,138,45,-19,-50,-63,-70,-70,-61,-48,-34,-23,-18,-17,-16,-16,-15,-14,-13,-13,-11,-10,-8,-7,-5,-3,-1,1,4,7,9,13,16,20,23,27,31,36,40,44,49,54,59,63,68,73,76,81,83,86,88,87,87,85,82,78,73,68,62,55,48,40,33,27,20,14,9,4,0,-5,-9,-13,-16,-19,-22,-24,-26,-28,-29,-29,-29,-30,-31,-31,-32,-31,-32,-32,-32,-32], "startts":1357714800000,"marker1":[50,100]}; var data =[]; for(var i=0;i<raw.l2.length;i++) { var marker = Number.NaN; for(var j=0;j<raw.marker1.length;j++) { if(i==raw.marker1[j]) { marker=i; break; } } var obj = {"date":raw.startts+(1/244.140625*1000)*i, "value":raw.l2[i]/75,"marker":marker} data.push(obj) } margin = { top: 20, right: 20, bottom: 20, left: 45 }; width = 1800 - margin.left - margin.right; height = 600 - margin.top - margin.bottom; var x = d3.time.scale() .domain(d3.extent(data, function (d) { return d.date; })) .range([0, width]); var y = d3.scale.linear() .domain(d3.extent(data, function (d) { return d.value; })) .range([height, 0]); var line = d3.svg.line() .x(function (d) { return x(d.date); }) .y(function (d) { return y(d.value); }); var zoom = d3.behavior.zoom() .x(x) .y(y) .on("zoom", zoomed); svg = d3.select('#chart') .append("svg:svg") .attr('width', width + margin.left + margin.right) .attr('height', height + margin.top + margin.bottom) .append("svg:g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")") .call(zoom); svg.append("svg:rect") .attr("width", width) .attr("height", height) .attr("class", "plot"); var make_x_axis = function () { return d3.svg.axis() .scale(x) .orient("bottom") .ticks(5); }; var make_y_axis = function () { return d3.svg.axis() .scale(y) .orient("left") .ticks(5); }; var xAxis = d3.svg.axis() .scale(x) .orient("bottom") .ticks(5); svg.append("svg:g") .attr("class", "x axis") .attr("transform", "translate(0, " + height + ")") .call(xAxis); var yAxis = d3.svg.axis() .scale(y) .orient("left") .ticks(5); svg.append("g") .attr("class", "y axis") .call(yAxis); var marker = svg.append("line") .attr("x1",100) .attr("y1", 0) .attr("x2",100) .attr("y2", height) .attr("class", "marker") svg.append("text") .attr("x", 100) .attr("y", 0) .attr("dy", ".35em") .attr("dx", 5) .attr("class", "marker-text") .text(function(d) { return "test"; }); svg.append("g") .attr("class", "x grid") .attr("transform", "translate(0," + height + ")") .call(make_x_axis() .tickSize(-height, 0, 0) .tickFormat("")); svg.append("g") .attr("class", "y grid") .call(make_y_axis() .tickSize(-width, 0, 0) .tickFormat("")); var clip = svg.append("svg:clipPath") .attr("id", "clip") .append("svg:rect") .attr("x", 0) .attr("y", 0) .attr("width", width) .attr("height", height); var chartBody = svg.append("g") .attr("clip-path", "url(#clip)"); chartBody.append("svg:path") .datum(data) .attr("class", "line") .attr("d", line); function zoomed() { console.log(d3.event.translate); console.log(d3.event.scale); svg.select(".x.axis").call(xAxis); svg.select(".y.axis").call(yAxis); svg.select(".x.grid") .call(make_x_axis() .tickSize(-height, 0, 0) .tickFormat("")); svg.select(".y.grid") .call(make_y_axis() .tickSize(-width, 0, 0) .tickFormat("")); svg.select(".line") .attr("class", "line") .attr("d", line); } I have been able to add static lines to my d3 plot in the example, what i am trying to achieve is to make the marker line along with the text "test" to be moving along with the graph. What I have tried is to use a d3.svg.line() with a line generator, but it ends up not getting the reference to d. Another version I have attempted is to create a function as below but that does not get the reference of d, and draws the line at zero. Here i attempted to draw the line if and only if the marker tag was present in the data. By default when populating the data i have ensured that it has Number.NaN when i dont need to draw the marker. var marker = svg.append("line") .attr("x1",function(d) { if(d.marker!=Number.NaN) return x(d.date); }) .attr("y1", 0) .attr("x2",function(d) { if(d.marker!=Number.NaN) return x(d.date); }) .attr("y2", height) .attr("class", "marker")
I have made a couple of small changes, as per my reply to your question. Inside your zoom handler zoomed() you need to change the x1 & x2 attributes of marker, and the x attr of markerText. This function fires on each zoom event to move stuff around or redraw stuff according to your zoom, and your axes. raw = {"l2":[-58,-44,-31,-21,-17,-16,-16,-15,-15,-14,-13,-12,-11,-10,-9,-7,-5,-3,-1,2,4,8,11,14,17,20,24,28,32,37,41,46,51,56,60,64,69,73,77,82,84,87,87,87,87,85,82,77,73,66,60,53,46,39,33,26,20,13,8,4,-1,-6,-10,-14,-16,-19,-22,-25,-26,-27,-28,-29,-30,-31,-31,-32,-32,-32,-32,-31,-31,-31,-31,-31,-30,-31,-30,-30,-30,-30,-29,-28,-27,-27,-27,-27,-27,-26,-26,-26,-26,-26,-26,-26,-26,-25,-25,-25,-25,-25,-25,-25,-25,-25,-24,-24,-24,-25,-25,-24,-23,-23,-23,-23,-23,-23,-23,-23,-23,-22,-22,-22,-22,-22,-22,-21,-21,-21,-21,-20,-20,-20,-20,-19,-18,-16,-13,-10,-6,-2,1,4,7,9,12,14,16,16,16,15,13,9,6,2,-1,-4,-7,-10,-11,-12,-14,-15,-16,-17,-18,-19,-19,-20,-20,-20,-20,-20,-20,-20,-24,-32,-45,-54,-50,-25,25,97,182,252,270,220,126,35,-25,-53,-65,-71,-70,-60,-46,-32,-22,-18,-16,-16,-15,-14,-13,-13,-12,-11,-10,-8,-7,-5,-3,-1,1,4,7,10,14,17,21,24,28,32,36,40,45,50,55,59,64,69,72,77,81,84,87,88,88,87,84,82,77,73,67,61,54,47,39,33,26,20,14,9,4,-1,-6,-9,-13,-16,-19,-22,-24,-26,-28,-29,-29,-30,-30,-31,-32,-32,-32,-31,-31,-32,-31,-31,-31,-31,-30,-30,-29,-29,-29,-29,-28,-28,-27,-27,-28,-28,-26,-26,-26,-26,-26,-26,-26,-26,-25,-25,-25,-25,-25,-25,-26,-26,-25,-24,-24,-24,-24,-24,-24,-24,-23,-23,-23,-23,-23,-23,-23,-24,-22,-22,-22,-22,-22,-22,-22,-21,-22,-20,-20,-20,-20,-20,-19,-18,-16,-13,-10,-8,-3,0,4,7,9,11,13,15,16,16,16,14,11,6,2,-2,-4,-7,-9,-11,-12,-14,-16,-17,-18,-19,-19,-19,-19,-20,-20,-20,-20,-20,-20,-23,-30,-43,-54,-53,-30,17,88,172,245,271,230,138,45,-19,-50,-63,-70,-70,-61,-48,-34,-23,-18,-17,-16,-16,-15,-14,-13,-13,-11,-10,-8,-7,-5,-3,-1,1,4,7,9,13,16,20,23,27,31,36,40,44,49,54,59,63,68,73,76,81,83,86,88,87,87,85,82,78,73,68,62,55,48,40,33,27,20,14,9,4,0,-5,-9,-13,-16,-19,-22,-24,-26,-28,-29,-29,-29,-30,-31,-31,-32,-31,-32,-32,-32,-32], "startts":1357714800000,"marker1":[50,100]}; var data =[]; for(var i=0;i<raw.l2.length;i++) { var marker = Number.NaN; for(var j=0;j<raw.marker1.length;j++) { if(i==raw.marker1[j]) { marker=i; break; } } var obj = {"date":raw.startts+(1/244.140625*1000)*i, "value":raw.l2[i]/75,"marker":marker} data.push(obj) } margin = { top: 20, right: 20, bottom: 20, left: 45 }; width = 1800 - margin.left - margin.right; height = 600 - margin.top - margin.bottom; var x = d3.time.scale() .domain(d3.extent(data, function (d) { return d.date; })) .range([0, width]); var y = d3.scale.linear() .domain(d3.extent(data, function (d) { return d.value; })) .range([height, 0]); var line = d3.svg.line() .x(function (d) { return x(d.date); }) .y(function (d) { return y(d.value); }); var zoom = d3.behavior.zoom() .x(x) .y(y) .on("zoom", zoomed); svg = d3.select('#chart') .append("svg:svg") .attr('width', width + margin.left + margin.right) .attr('height', height + margin.top + margin.bottom) .append("svg:g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")") .call(zoom); svg.append("svg:rect") .attr("width", width) .attr("height", height) .attr("class", "plot"); var make_x_axis = function () { return d3.svg.axis() .scale(x) .orient("bottom") .ticks(5); }; var make_y_axis = function () { return d3.svg.axis() .scale(y) .orient("left") .ticks(5); }; var xAxis = d3.svg.axis() .scale(x) .orient("bottom") .ticks(5); svg.append("svg:g") .attr("class", "x axis") .attr("transform", "translate(0, " + height + ")") .call(xAxis); var yAxis = d3.svg.axis() .scale(y) .orient("left") .ticks(5); svg.append("g") .attr("class", "y axis") .call(yAxis); svg.append("g") .attr("class", "x grid") .attr("transform", "translate(0," + height + ")") .call(make_x_axis() .tickSize(-height, 0, 0) .tickFormat("")); svg.append("g") .attr("class", "y grid") .call(make_y_axis() .tickSize(-width, 0, 0) .tickFormat("")); // Moved this after you append your grids var marker = svg.append("line") .attr("x1",100) .attr("y1", 0) .attr("x2",100) .attr("y2", height) .attr("class", "marker") // Moved this after you append your grids & saved it to a var for later use var markerText = svg.append("text") .attr("x", 100) .attr("y", 0) .attr("dy", ".35em") .attr("dx", 5) .attr("class", "marker-text") .text(function(d) { return "test"; }); var clip = svg.append("svg:clipPath") .attr("id", "clip") .append("svg:rect") .attr("x", 0) .attr("y", 0) .attr("width", width) .attr("height", height); var chartBody = svg.append("g") .attr("clip-path", "url(#clip)"); chartBody.append("svg:path") .datum(data) .attr("class", "line") .attr("d", line); function zoomed() { console.log(d3.event.translate); console.log(d3.event.scale); svg.select(".x.axis").call(xAxis); svg.select(".y.axis").call(yAxis); svg.select(".x.grid") .call(make_x_axis() .tickSize(-height, 0, 0) .tickFormat("")); svg.select(".y.grid") .call(make_y_axis() .tickSize(-width, 0, 0) .tickFormat("")); svg.select(".line") .attr("class", "line") .attr("d", line); // Position at the start time according to your x scale. x(val) // It's not clear from your question where you want to position it var markerPos = x(raw.startts); // Move marker to position marker.attr("x1", markerPos ).attr("x2", markerPos ); // Move marker text to position markerText.attr("x", markerPos ); } Fiddle: https://jsfiddle.net/kqnLfkvw/1/
This can be solved by using d3 axis. A working example is available at https://jsfiddle.net/3gww76s0/ var make_x_marker = function (scale=1, transform=[0,0]) { console.log(transform) var markers =[]; var markerLabels={}; for (i=0; i<raw.marker1.length; i++) { var value=transform[0]+scale*(1/244.140625*1000)*raw.marker1[i]; markers.push(value) markerLabels[value]="A"+Math.floor(Math.random()*26); } return d3.svg.axis() .orient("top") .tickValues(markers) .tickSize(height-15) .tickFormat(function(d){return markerLabels[d]}) };
D3 line chart Invalid value for <path> attribute
So i have the following js to create my linechart: var data = scope.dataset; var margin = {top: 20, right: 20, bottom: 30, left: 50}, width = 960 - margin.left - margin.right, height = 500 - margin.top - margin.bottom; var x = d3.time.scale() .range([0, width]); var y = d3.scale.linear() .range([height, 0]); var xAxis = d3.svg.axis() .scale(x) .orient("bottom"); var yAxis = d3.svg.axis() .scale(y) .orient("left"); var line = d3.svg.line() .x(function (d) { return x(d.label); }) .y(function (d) { return y(d.value); }); data.forEach(function (d) { d.label = d.label; d.value = d.value; }); var svg = d3.select("#lbLineChart").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 + ")"); x.domain(d3.extent(data, function (d) { return d.label; })); y.domain(d3.extent(data, function (d) { return d.value; })); svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + height + ")") .call(xAxis); svg.append("g") .attr("class", "y axis") .call(yAxis) .append("text") .attr("transform", "rotate(-90)") .attr("y", 6) .attr("dy", ".71em") .style("text-anchor", "end") .text("Price ($)"); svg.append("path") .datum(data) .attr("class", "line") .attr("d", line); Where the data is an array of objects: {label: 'mylabel', value: 5} Invalid value for <path> attribute Im not quite sure what im doing wrong? ive checked the variables again and again but was unable to find where there is an error.
Add text on top of bar in d3js chart -- no <text> elements added
I am creating a bar chart with d3.js from data stored in tsv file. I want to insert a text in each bar. How I can do? I have tried even this solution, but doesn't work. Here is the code of my function: var margin = {top: 20, right: 20, bottom: 30, left: 40}, width = 960 - margin.left - margin.right, height = 500 - margin.top - margin.bottom; var formatPercent = d3.format(".0%"); var x = d3.scale.ordinal() .rangeRoundBands([0, width], .1, 1); var y = d3.scale.linear() .range([height, 0]); var xAxis = d3.svg.axis() .scale(x) .orient("bottom"); var yAxis = d3.svg.axis() .scale(y) .orient("left"); var svg = d3.select("body").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 + ")"); function visualize(file){ d3.tsv(file, function(error, data) { data.forEach(function(d) { d.weight = +d.weight; }); x.domain(data.map(function(d) { return d.concept; })); y.domain([0, d3.max(data, function(d) { return d.weight; })]); svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + height + ")") .call(xAxis); svg.append("g") .attr("class", "y axis") .call(yAxis) .append("text") .attr("transform", "rotate(-90)") .attr("y", 6) .attr("dy", ".71em") .style("text-anchor", "end") .text("weight"); svg.selectAll(".bar") .data(data) .enter().append("rect").style("fill", function (d){return d.color;}) .attr("class", "bar") .attr("x", function(d) { return x(d.concept); }) .attr("width", x.rangeBand()) .attr("y", function(d) { return y(d.weight); }) .attr("height", function(d) { return height - y(d.weight); }); svg.selectAll("text") .data(data) .enter() .append("text") .text(function(d) { return d.concept; }) .attr("text-anchor", "middle") .attr("x", x) .attr("y",y) .attr("font-family", "sans-serif") .attr("font-size", "11px") .attr("fill", "white"); }); } All my code with the files tsv are here: full code
AmeliaBR is right as usual, and I am only putting this answer because while I was working on it, I saw myself changing the code so that it really makes use of the Enter, Update, Exit selection paradigm. I have made quite a few changes in that regard. Here is the code, FWIW: var margin = {top: 20, right: 20, bottom: 30, left: 40}, width = 960 - margin.left - margin.right, height = 500 - margin.top - margin.bottom; var formatPercent = d3.format(".0%"); var x = d3.scale.ordinal() .rangeRoundBands([0, width], .1, 1); var y = d3.scale.linear() .range([height, 0]); var xAxis = d3.svg.axis() .scale(x) .orient("bottom"); var yAxis = d3.svg.axis() .scale(y) .orient("left"); var svg = d3.select("body").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 + ")"); svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + height + ")") .call(xAxis); svg.append("g") .attr("class", "y axis") .call(yAxis) .append("text") .attr("transform", "rotate(-90)") .attr("y", 6) .attr("dy", ".71em") .style("text-anchor", "end") .text("weight"); var g = svg.append("g"); function update(file){ d3.tsv(file, function(error, data) { data.forEach(function(d) { d.weight = +d.weight; }); x.domain(data.map(function(d) { return d.concept; })); y.domain([0, d3.max(data, function(d) { return d.weight; })]); var bar = g.selectAll(".bar") .data(data); bar.enter() .append("rect") .attr("class","bar"); bar .style("fill", function (d){return d.color;}) .attr("class", "bar") .attr("x", function(d) { return x(d.concept); }) .attr("width", x.rangeBand()) .attr("y", function(d) { return y(d.weight); }) .attr("height", function(d) { return height - y(d.weight); }); bar.exit().remove(); var text = g.selectAll(".text") .data(data); text.enter() .append("text") .attr("class","text"); text .attr("text-anchor", "right") .attr("x", function(d) { return x(d.concept); }) .attr("y", function(d) { return y(d.weight) + 22;}) .attr("font-family", "sans-serif") .attr("font-size", "11px") .attr("fill", "white") .text(function(d) { return d.concept; }); text.exit().remove(); }); } And you call it like by doing update("data16.tsv") and then update("data15.tsv").
When you draw an axis, it creates separate <text> elements for each label inside the axis group inside the SVG. So if you then try to select all the <text> elements in the SVG, you're going to select all your axis labels. If you have more axis labels than data for text elements, your enter() selection will be empty and nothing will happen. To be sure you're only selecting the correct <text> elements, give your text labels a class to distinguish them from the axis labels. And then use that class to narrow-down your selector: svg.selectAll("text.bar-label") .data(data) .enter() .append("text") .attr("class", "bar-label")
D3: Panning along date x axis
I'm trying to display dates in the x axis and at the same time zoom it when you scroll. So, I have this code: <script type="text/javascript"> var data = [ [{'x':20111001,'y':1},{'x':20111002,'y':6},{'x':20111003,'y':11},{'x':20111004,'y':1},{'x':20111005,'y':2},{'x':20111006,'y':12},{'x':20111007,'y':2},{'x':20111008,'y':3},{'x':20111009,'y':13},{'x':20111010,'y':3}], [{'x':20111001,'y':2},{'x':20111002,'y':2},{'x':20111003,'y':12},{'x':20111004,'y':2},{'x':20111005,'y':3},{'x':20111006,'y':1},{'x':20111007,'y':2},{'x':20111008,'y':7},{'x':20111009,'y':2},{'x':20111010,'y':7}], [{'x':20111001,'y':3},{'x':20111002,'y':10},{'x':20111003,'y':13},{'x':20111004,'y':3},{'x':20111005,'y':12},{'x':20111006,'y':14},{'x':20111007,'y':6},{'x':20111008,'y':1},{'x':20111009,'y':7},{'x':20111010,'y':9}], [{'x':20111001,'y':4},{'x':20111002,'y':4},{'x':20111003,'y':14},{'x':20111004,'y':14},{'x':20111005,'y':10},{'x':20111006,'y':15},{'x':20111007,'y':3},{'x':20111008,'y':0},{'x':20111009,'y':3},{'x':20111010,'y':12}] ]; var colors = [ 'steelblue', 'green', 'red', 'purple' ] var b =[]; var parseDate = d3.time.format("%Y%m%d").parse; data.forEach(function (d) { f = d; f.forEach(function(f){ b.push(parseDate(String(f.x))); }) }) var margin = {top: 20, right: 30, bottom: 30, left: 50}, width = 960 - margin.left - margin.right, height = 500 - margin.top - margin.bottom; var x = d3.time.scale() .domain([d3.extent(b)]) .range([0, width]); var y = d3.scale.linear() .domain([-1, 16]) .range([height, 0]); var xAxis = d3.svg.axis() .scale(x) .tickSize(-height) .tickPadding(10) .tickSubdivide(true) .orient("bottom"); var yAxis = d3.svg.axis() .scale(y) .tickPadding(10) .tickSize(-width) .tickSubdivide(true) .orient("left"); var zoom = d3.behavior.zoom() .x(x) .y(y) .scaleExtent([1, 10]) .on("zoom", zoomed); var svg = d3.select("body").append("svg") .call(zoom) .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + height + ")") .call(xAxis); svg.append("g") .attr("class", "y axis") .call(yAxis); svg.append("g") .attr("class", "y axis") .append("text") .attr("class", "axis-label") .attr("transform", "rotate(-90)") .attr("y", (-margin.left) + 10) .attr("x", -height/2) .style("text-anchor", "end") .text("Ventas (Miles €)"); svg.append("clipPath") .attr("id", "clip") .append("rect") .attr("width", width) .attr("height", height); var line = d3.svg.line() .interpolate("linear") .x(function(d) { return x(d.x); }) .y(function(d) { return y(d.y); }); svg.selectAll('.line') .data(data) .enter() .append("path") .attr("class", "line") .transition() .attr("clip-path", "url(#clip)") .attr('stroke', function(d,i){ return colors[i%colors.length]; }) .attr("d", line); var div = d3.select("body").append("div") .attr("class", "tooltip") .style("opacity", 0); var points = svg.selectAll('.dots') .data(data) .enter() .append("g") .attr("class", "dots") .attr("clip-path", "url(#clip)"); points.selectAll('.dot') .data(function(d, index){ var a = []; d.forEach(function(point,i){ a.push({'index': index, 'point': point}); }); return a; }) .enter() .append('circle') .attr('class','dot') .attr("r", 2.5) .attr('fill', function(d,i){ return colors[d.index%colors.length]; }) .attr("transform", function(d) { return "translate(" + x(d.point.x) + "," + y(d.point.y) + ")"; } ).on("mouseover", function(d) { div.transition() .duration(200) .style("opacity", .9); div .html(d.point.x + "K<br/>" + d.point.y) .style("left", (d3.event.pageX) + "px") .style("top", (d3.event.pageY) + "px"); }) .on("mouseout", function(d) { div.transition() .duration(500) .style("opacity", 0); }) function zoomed() { svg.select(".x.axis").call(xAxis); svg.select(".y.axis").call(yAxis); svg.selectAll('path.line').attr('d', line); points.selectAll('circle').attr("transform", function(d) { return "translate(" + x(d.point.x) + "," + y(d.point.y) + ")"; } ); } </script> I can make it with numbers but can't implement it with dates. I've checked other examples and how they make it but can't find the way to code it in my chart. I'd like to know how to display dates on x axis.
Is your question how to make the axis dates instead of just numbers? Or is it how to make the axis pannable? If it's the first, use code like this: var x=d3.time.scale() .domain([minDate,maxDate]) The minDate and maxDate have to be javascript Date objects.
Adding tooltips to line graph data points AFTER lines animate
I have a basic line graph with two lines. When I click a button, one of the two lines will draw on the graph. I use stroke-dasharray to draw the lines. Would anyone know how I could add tooltips to my lines? Does using stroke-dasharray make it harder? Heres my code. var button=d3.select("#button"); //defines canvas (area in which graph is placed) var margin = {top: 30, right: 20, bottom: 50, left: 60}, width = 800 - margin.left - margin.right, height = 700 - margin.top - margin.bottom; var parseDate = d3.time.format("%d-%b-%y").parse; //OUTPUT RANGE var x = d3.time.scale() .range([0, width]); //OUTPUT RANGE var y = d3.scale.linear() .range([height, 0]); var xAxis = d3.svg.axis().scale(x) .orient("bottom") .ticks(5); var yAxis = d3.svg.axis().scale(y) .orient("left") .ticks(5); //assigns coordinates for each piece of data var valueline = d3.svg.line() .interpolate("interpolation") .x(function(d) { return x(d.date); }) .y(function(d) { return y(d.close); }); //second line data var valueline2 = d3.svg.line() .x(function(d) { return x(d.date); }) .y(function(d) { return y(d.open); }); //create area for 'area' below line var area = d3.svg.area() .x(function(d) { return x(d.date); }) .y0(height) .y1(function(d) { return y(d.close); }); //creates area to draw graph var svg = d3.select("body") .append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) //groups content .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); function make_x_axis() { return d3.svg.axis() .scale(x) .orient("bottom") .ticks(5) } function make_y_axis() { return d3.svg.axis() .scale(y) .orient("left") .ticks(30) } // csv callback function d3.csv("myData3.csv", function(data) { data.forEach(function(d) { d.date = parseDate(d.date); d.close = +d.close; d.open = +d.open; }); //INPUT DOMAINS //.extent() returns min and max values of argument x.domain(d3.extent(data, function(d) { return d.date; })); //returns max of whichever set of data is bigger y.domain([0, d3.max(data, function(d) { return Math.max(d.close, d.open); })]); d3.select("#button1").on("click", function(){ var path = svg.append("path") // Add the valueline path. .attr("class", "line") .attr("d", valueline(data)) .attr("stroke", "steelblue") .attr("stroke-width", "5") .attr("fill", "none"); var totalLength = path.node().getTotalLength(); path .attr("stroke-dasharray", totalLength + "30" * 30) .attr("stroke-dashoffset", totalLength) .transition() .duration(2000) .ease("linear") .attr("stroke-dashoffset", 0); }) d3.select("#button2").on("click", function(){ var path2 = svg.append("path") // Add the valueline path. .attr("class", "line2") .attr("d", valueline2(data)) .attr("stroke", "steelblue") .attr("stroke-width", "5") .attr("fill", "none"); var totalLength = path2.node().getTotalLength(); path2 .attr("stroke-dasharray", totalLength + "30" * 30) .attr("stroke-dashoffset", totalLength) .transition() .duration(2000) .ease("linear") .attr("stroke-dashoffset", 0) }) svg.append("g") // Add the X Axis .attr("class", "x axis") //moves x axis to bottom of graph .attr("transform", "translate(0," + height + ")") .call(xAxis); //text label for x-axis svg.append("text") // text label for the x axis .attr("transform", "translate(" + (width / 2) + " ," + (height + margin.bottom - 5 ) + ")") .style("text-anchor", "middle") .text("Date"); svg.append("g") // Add the Y Axis .attr("class", "y axis") .call(yAxis); //text label for y-axis svg.append("text") .attr("transform", "rotate(-90)") .attr("y", 0 - margin.left) .attr("x",0 - (height / 2)) //adds extra left padding as original y pos = 0 .attr("dy", "1em") .style("text-anchor", "middle") .text("Value"); //adding a title to the graph svg.append("text") .attr("x", (width / 2)) .attr("y", 0 - (margin.top / 2)) .attr("text-anchor", "middle") .style("font-size", "16px") .style("text-decoration", "underline") .text("Graph"); //draw x axis grid svg.append("g") .attr("class", "grid") .attr("transform", "translate(0," + height + ")") .call(make_x_axis() .tickSize(-height, 0, 0) .tickFormat("") ) //draw y axis grid svg.append("g") .attr("class", "grid") .call(make_y_axis() .tickSize(-width, 0, 0) .tickFormat("") ) });<!--d3.csv close--> Thanks in advance!
The easiest way to add a tooltip is to append an svg:title element to the elements you want to have a tooltip for. It will be displayed by the browser automatically when you hover over the element. It works for all kinds of elements as well. So your code would need to look something like var path = svg.append("path") // Add the valueline path. .attr("class", "line") .attr("d", valueline(data)) .attr("stroke", "steelblue") .attr("stroke-width", "5") .attr("fill", "none") .append("title").text("whatever"); If you need more sophisticated functionality, you could try for example tipsy, which works in a similar way.