so I am trying to make something where the user can drag and create two equal sized brushes in a line chart, the problem is that brush disappears after I try to draw the second one. I have tried to create separate attr and seperate brush call but still can't do it.
this is my code https://jsfiddle.net/f0gxs41t/
any help?
<!DOCTYPE html>
<html>
<head>
<svg width="960" height="400"></svg>
<meta charset="utf-8">
<title>
line chart with drag and drop
</title>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script src="http://code.jquery.com/jquery-1.7.2.min.js"></script>
<script src="./line_graph.js"></script>
<link rel="stylesheet" type= "text/css" href="./style.css">
</head>
<body>
</body>
</html>
js file :
var data=[1,5,2,7,4,7,8,9,5,3,6,8,2,3,5,9,8,5]
var svg=d3.select("svg")
var margin={top:100,bottom:50,left:100,right:0},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var x_extent=d3.extent(data,function(d,i){return i})
var y_extent=d3.extent(data,function(d,i){return d})
x=d3.scale.linear()
.range([0,width])
.domain(x_extent)
y=d3.scale.linear()
.range([height,0])
.domain(y_extent)
var line=d3.svg.line()
.x(function(d,i){ return x(i)})
.y(function(d,i){return y(d)})
var xAxis = d3.svg.axis().scale(x)
.orient("bottom");
var yAxis = d3.svg.axis().scale(y)
.orient("left");
// g.append("g")
// .append("text")
// .attr("fill", "#000")
// .attr("transform","rotate(-90)")
// .attr("y",-35)
// .attr("dy","0.71em")
// .attr("text-anchor","end")
// .text("break something")
g.append("path")
.attr("class","line")
.datum(data)
.attr("d",line)
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate("+margin.left+"," + (height+margin.top) + ")")
.call(xAxis);
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate("+margin.left+ ","+margin.top+")")
.call(yAxis);
var focus = svg.append("g")
.attr("class", "focus")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// if (brush.empty()!==true){
//
// }
var brushes = [];
var parent_brush=d3.svg.brush()
.x(x)
.on("brushend",brushed)
var brush1=d3.svg.brush()
.x(x)
.on("brushend",brushed)
function brushed(){
if (parent_brush.empty()==true){
console.log(parent_brush,"empty")
}
else {
brush_width=parent_brush.extent()[1]-parent_brush.extent()[0]
console.log(parent_brush,"not empty")
}
}
svg.append("g")
.attr("class","parent")
.call(parent_brush)
.selectAll("rect")
.attr("x",margin.left)
.attr("y",margin.top)
.attr("height",height)
.style("fill","orange")
.style("fill-opacity",".2")
svg.append("g")
.attr("class","child")
.call(brush1)
.selectAll("rect")
.attr("x",margin.left)
.attr("y",margin.top)
.attr("height",height)
.style("fill","orange")
.style("fill-opacity",".2")
css file:
.axis path,
.axis line {
fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;
}
.hover-line {
stroke: #6F257F;
stroke-width: 2px;
stroke-dasharray: 3,3;
}
.area {
fill: lightsteelblue;
}
.line{
fill:none;
stroke:brown;
stroke-linejoin:round;
stroke-linecap:round;
stroke-width:1.5
}
.focus circle {
fill: none;
stroke: steelblue;
}
.brush {
fill: grey;
pointer-events: all;
fill-opacity:0.3;
}
.resize {
fill: grey;
pointer-events: all;
fill-opacity:0.7;
}
consider using d3.js v4...
Child brush is created in the parent brush "end" event handler, where we also need to disable new parent brush selections.
var parent_brush = d3.brushX()
.extent([
[margin.left, margin.top],
[margin.left + width, margin.top + height]
])
.on("end", brushedParent);
var child_brush;
svg.append("g")
.attr("class", "parent")
.call(parent_brush);
function brushedParent() {
// remove new brush selection capture area
svg.select('.parent .overlay').remove();
if (!child_brush) {
child_brush = d3.brushX()
.extent([
[margin.left, margin.top],
[margin.left + width, margin.top + height]
])
.on("end", brushedChild);
svg.append("g")
.attr("class", "child")
.call(child_brush);
}
}
function brushedChild() {
// remove new brush selection capture area
svg.select('.child .overlay').remove();
child_selection = d3.brushSelection(svg.select('.child').node());
var parent_selection = d3.brushSelection(svg.select('.parent').node());
var parent_width = parent_selection[1] - parent_selection[0];
var resized_child = [child_selection[0], child_selection[0] + parent_width];
child_brush.on("end", null);
child_brush.move(svg.select('.child'), resized_child);
}
Here is the working code snippet:
var data = [1, 5, 2, 7, 4, 7, 8, 9, 5, 3, 6, 8, 2, 3, 5, 9, 8, 5]
var svg = d3.select("svg")
var margin = {
top: 100,
bottom: 50,
left: 100,
right: 0
},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var x_extent = d3.extent(data, function(d, i) {
return i
})
var y_extent = d3.extent(data, function(d, i) {
return d
})
x = d3.scaleLinear()
.range([0, width])
.domain(x_extent)
y = d3.scaleLinear()
.range([height, 0])
.domain(y_extent)
var line = d3.line()
.x(function(d, i) {
return x(i)
})
.y(function(d, i) {
return y(d)
})
var xAxis = d3.axisBottom(x);
var yAxis = d3.axisLeft(y);
g.append("path")
.attr("class", "line")
.datum(data)
.attr("d", line)
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(" + margin.left + "," + (height + margin.top) + ")")
.call(xAxis);
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(yAxis);
var focus = svg.append("g")
.attr("class", "focus")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var brushes = [];
var parent_brush = d3.brushX()
.extent([
[margin.left, margin.top],
[margin.left + width, margin.top + height]
])
.on("end", brushedParent);
var child_brush;
svg.append("g")
.attr("class", "parent")
.call(parent_brush);
function brushedParent() {
// remove new brush selection capture area
svg.select('.parent .overlay').remove();
if (!child_brush) {
child_brush = d3.brushX()
.extent([
[margin.left, margin.top],
[margin.left + width, margin.top + height]
])
.on("end", brushedChild);
svg.append("g")
.attr("class", "child")
.call(child_brush);
}
}
function brushedChild() {
// remove new brush selection capture area
svg.select('.child .overlay').remove();
child_selection = d3.brushSelection(svg.select('.child').node());
var parent_selection = d3.brushSelection(svg.select('.parent').node());
var parent_width = parent_selection[1] - parent_selection[0];
var resized_child = [child_selection[0], child_selection[0] + parent_width];
child_brush.on("end", null);
child_brush.move(svg.select('.child'), resized_child);
}
.axis path,
.axis line {
fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;
}
.hover-line {
stroke: #6F257F;
stroke-width: 2px;
stroke-dasharray: 3, 3;
}
.area {
fill: lightsteelblue;
}
.line {
fill: none;
stroke: brown;
stroke-linejoin: round;
stroke-linecap: round;
stroke-width: 1.5
}
.focus circle {
fill: none;
stroke: steelblue;
}
.brush {
fill: grey;
pointer-events: all;
fill-opacity: 0.3;
}
.resize {
fill: grey;
pointer-events: all;
fill-opacity: 0.7;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="960" height="400"></svg>
Related
I am trying to get my d3 bar graph to update with a csv and for some reason I keep getting the error that data is undefined when I run it with the live server extension, however I am not sure why because it works totally fine with a different csv. Earlier I was having a different error with this code where it would show up but only one bar would be filled in all the way to the top no matter what was selected from the dropdown. Any help would be greatly appreciated!! Here is my code:
d3 Updating Bar Chart With Dropdown
<style>
input {
position: relative;
font-size: large;
z-index: +1;
padding: .5em;
margin-bottom: .5em;
}
body {
font: 14px sans-serif;
background-color: lightgray;
}
select {
display: block;
}
.bar {
fill: rgb(209, 20, 184);
fill-opacity: 0.7;
stroke:rgb(0, 0, 0);
stroke-width:2.5; /* this changes the stroke width*/
stroke-linecap:butt;
stroke-opacity: 1.0;
}
.axis path,
.axis line {
fill: none;
stroke: rgb(0, 0, 0);
stroke-width:3; /*again stroke width*/
shape-rendering: crispEdges;
}
</style>
<h1>Bar Graph of Crashes by Light Condition</h1>
</head>
<body>
<div id='vis-container'></div>
<script type="text/javascript">
var fileName = "CSVs\TownTotalcrashesFatalsInjuryNoinjury.csv"; //Pulling Data
var conditionFields = ["Crashes", "Fatals", "Injury", "No Injury"];
d3.csv(fileName, function(error, data) { //Error CheckingS
var townMap = {};
data.forEach(function(d) {
var town = d.Town;
townMap[town] = [];
conditionFields.forEach(function(field) {
townMap[town].push( +d[field] );
});
});
makeVis(townMap);
});
var makeVis = function(townMap) {
var margin = { top: 30, right: 50, bottom: 30, left: 50 }, //Dimensions of the Visual
width = 800 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
var xScale = d3.scale.ordinal() //Getting Scale for Axis
.domain(conditionFields)
.rangeRoundBands([0, width], 0.1);
var yScale = d3.scale.linear()
.range([height, 0]);
var canvas = d3.select("#vis-container") //Creating Canvas
.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 + ")");
var xAxis = d3.svg.axis() //Creation of X-Axis
.scale(xScale)
.orient("bottom");
canvas.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
var yAxis = d3.svg.axis() //Creation of Y-axis
.scale(yScale)
.orient("left");
var yAxisHandleForUpdate = canvas.append("g")
.attr("class", "y axis")
.call(yAxis);
canvas.append("text") //Addition of Axis Labels And Title
.attr("transform",
"translate(" + (width/2) + " ," +
(height + margin.top + 20) + ")")
.style("text-anchor", "middle")
.text("Light Condition");
canvas.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin.left)
.attr("x",0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Crashes");
canvas.append("text")
.attr("x", (width / 2))
.attr("y", 0 - (margin.top / 2))
.attr("text-anchor", "middle")
.style("font-size", "18px")
var updateBars = function(data) { //updating for new data
yScale.domain( d3.extent(data) );
yAxisHandleForUpdate.call(yAxis);
var bars = canvas.selectAll(".bar").data(data);
bars.enter() // Add bars for new data
.append("rect")
.attr("class", "bar")
.attr("x", function(d,i) { return xScale( conditionFields[i] ); })
.attr("width", xScale.rangeBand())
.attr("y", function(d,i) { return yScale(d); })
.attr("height", function(d,i) { return height - yScale(d); });
bars //updating new bars, removing old ones
.transition().duration(250)
.attr("y", function(d,i) { return yScale(d); })
.attr("height", function(d,i) { return height - yScale(d); });
bars.exit().remove();
};
var dropdownChange = function() { //dropdown menu handler
var newTown = d3.select(this).property('value'),
newData = townMap[newTown];
updateBars(newData);
};
var towns = Object.keys(townMap).sort(); //Creating Drop Down Menu
var dropdown = d3.select("#vis-container")
.insert("select", "svg")
.on("change", dropdownChange);
dropdown.selectAll("option")
.data(towns)
.enter().append("option")
.attr("value", function (d) { return d; })
.text(function (d) {
return d[0].toUpperCase() + d.slice(1,d.length); // capitalize 1st letter
});
var initialData = townMap[ towns[0] ];
updateBars(initialData);
};
</script>
</body>
</html>
I am plotting a bar chart in D3.js (Version 3). Which has two axis, one is receive_data and another one is responses. I have a JSON file where I stored the data. JSON format looks like,
[{"receive_date":"2013-11-04","responses":"2"}]
In my JSON, I have two responses values for the same date 2013-11-04 .
Like,
[{"receive_date":"2013-11-04","responses":"2"},{"receive_date":"2013-11-04","responses":"8668"}
This is the JSON Source :- https://api.myjson.com/bins/gdpu7
So, when I am plotting the graph, it is not taking the sum of the values for the same receive_date instead it is showing two times. I want it to show the sum of responses. responses should be (8668+2) for the receive_date 2013-11-04
I also found it that by using reduce we can do this. I tried to use d3.json.reduce . But it is showing error d3.json.reduce is not a function.
var margin = {
top: 20,
right: 30,
bottom: 30,
left: 40
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// scale to ordinal because x axis is not numerical
var x = d3.scale.ordinal().rangeRoundBands([0, width], .1);
//scale to numerical value by height
var y = d3.scale.linear().range([height, 0]);
var chart = d3.select("#chart")
.append("svg") //append svg element inside #chart
.attr("width", width + (2 * margin.left) + margin.right) //set width
.attr("height", height + margin.top + margin.bottom); //set height
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom"); //orient bottom because x-axis will appear below the bars
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
d3.json("https://api.myjson.com/bins/gdpu7", function(error, data) {
x.domain(data.map(function(d) {
return d.receive_date
}));
y.domain([0, d3.max(data, function(d) {
return d.responses
})]);
var bar = chart.selectAll("g")
.data(data)
.enter()
.append("g")
.attr("transform", function(d, i) {
return "translate(" + x(d.receive_date) + ", 0)";
});
bar.append("rect")
.attr("y", function(d) {
return y(d.responses);
})
.attr("x", function(d, i) {
return x.rangeBand() + (margin.left / 2);
})
.attr("height", function(d) {
return height - y(d.responses);
})
.attr("width", x.rangeBand()); //set width base on range on ordinal data
bar.append("text")
.attr("x", x.rangeBand() + margin.left)
.attr("y", function(d) {
return y(d.responses) - 10;
})
.attr("dy", ".75em")
.text(function(d) {
return d.responses;
});
chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(" + margin.left + "," + height + ")")
.call(xAxis);
chart.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + margin.left + ",0)")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("responses");
});
function type(d) {
d.receive_date = +d.receive_date; // coerce to number
return d;
}
#chart rect {
fill: #4aaeea;
}
#chart text {
fill: white;
font: 10px sans-serif;
text-anchor: end;
}
.axis text {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #fff;
shape-rendering: crispEdges;
}
body {
background: #1a1a1a;
color: #eaeaea;
padding: 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="chart"></div>
JSfiddle :- https://jsfiddle.net/bL9940at/
The relevant part:
var array1 = data; //input
var array2 = [];
var last_d;
array1.reduce(function (accumulator, currentValue, i) {
var r = Number(currentValue.responses),
d = currentValue.receive_date;
if (d == last_d) r += accumulator;
array2[i] = {
receive_date: d,
responses: r
};
last_d = d;
return accumulator + Number(currentValue.responses);
}, 0);
data = array2; //output
var margin = {
top: 20,
right: 30,
bottom: 30,
left: 40
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// scale to ordinal because x axis is not numerical
var x = d3.scale.ordinal().rangeRoundBands([0, width], .1);
//scale to numerical value by height
var y = d3.scale.linear().range([height, 0]);
var chart = d3.select("#chart")
.append("svg") //append svg element inside #chart
.attr("width", width + (2 * margin.left) + margin.right) //set width
.attr("height", height + margin.top + margin.bottom); //set height
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom"); //orient bottom because x-axis will appear below the bars
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
d3.json("https://api.myjson.com/bins/gdpu7", function(error, data) {
//create new arrays
var array1 = data; //input
var array2 = [];
var last_d;
array1.reduce(function (accumulator, currentValue, i) {
var r = Number(currentValue.responses),
d = currentValue.receive_date;
if (d == last_d) r += accumulator;
array2[i] = {
receive_date: d,
responses: r
};
last_d = d;
return accumulator + Number(currentValue.responses);
}, 0);
data = array2; //output
x.domain(data.map(function(d) {
return d.receive_date;
}));
y.domain([0, d3.max(data, function(d) {
return d.responses;
})*1.1]);
var bar = chart.selectAll("g")
.data(data)
.enter()
.append("g")
.attr("transform", function(d, i) {
return "translate(" + x(d.receive_date) + ", 0)";
});
bar.append("rect")
.attr("y", function(d) {
return y(d.responses);
})
.attr("x", function(d, i) {
return x.rangeBand() + (margin.left / 2);
})
.attr("height", function(d) {
return height - y(d.responses);
})
.attr("width", x.rangeBand()); //set width base on range on ordinal data
bar.append("text")
.attr("x", x.rangeBand() + margin.left)
.attr("y", function(d) {
return y(d.responses) - 10;
})
.attr("dy", ".75em")
.text(function(d) {
return d.responses;
});
chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(" + margin.left + "," + height + ")")
.call(xAxis);
chart.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + margin.left + ",0)")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("responses");
});
function type(d) {
d.receive_date = +d.receive_date; // coerce to number
return d;
}
#chart rect {
fill: #4aaeea;
}
#chart text {
fill: white;
font: 10px sans-serif;
text-anchor: end;
}
.axis text {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #fff;
shape-rendering: crispEdges;
}
body {
background: #1a1a1a;
color: #eaeaea;
padding: 10px;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js"></script>
<script src="https://code.jquery.com/jquery-git.js"></script>
<div id="chart"></div>
</body>
</html>
I am trying to draw a Dovish & Hawkish line chart diagram.
Example:
However, it's throwing an error.
I took the example from a bar chart, that draws the bars with a positive and negative value. Then I have used the code only related to draw the xScale/yScale and the lines for xAxis/yAxis.
JSFiddle can be found here.
The problem here is that you are using an ordinal scale for the x axis:
var x = d3.scale.ordinal()
.domain(d3.range(data.length))
Which has this domain:
[0, 1, 2, 3, 4, 5, 6, 7]
However, you're passing the datum to the line generator:
var line = d3.svg.line()
.x(function(d) { return x(d);})
//datum here -------------^
Instead of that, pass the index of the datum:
var line = d3.svg.line()
.x(function(d, i) { return x(i);})
//index here ----------------^
Here is your updated code:
var data = [-0.1, -0.5, -0.32, 0.2, 1, 0.5, -0.3, -1.0];
var margin = {
top: 30,
right: 10,
bottom: 10,
left: 30
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var y0 = Math.max(Math.abs(d3.min(data)), Math.abs(d3.max(data)));
var y = d3.scale.linear()
.domain([-y0, y0])
.range([height, 0])
.nice();
var x = d3.scale.ordinal()
.domain(d3.range(data.length))
.rangeRoundBands([0, width], .2);
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 + ")");
var line = d3.svg.line()
.x(function(d, i) {
return x(i);
})
.y(function(d) {
return y(d);
});
var dataSum = d3.sum(data, function(d) {
return d;
});
svg.append("g")
.attr("class", "x axis")
.call(yAxis);
svg.append("g")
.attr("class", "y axis")
.append("line")
.attr("y1", y(0))
.attr("y2", y(0))
.attr("x1", 0)
.attr("x2", width);
svg.append("path")
.data([data])
.attr("class", "line")
.attr("d", line);
.axis text {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.line{
fill: none;
stroke: blue;
stroke-width: 2px;
}
.tick text{
font-size: 12px;
}
.tick line{
opacity: 0.2;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
PS: You don't need that awkward line in the middle of the chart. You can create a proper axis:
var data = [-0.1, -0.5, -0.32, 0.2, 1, 0.5, -0.3, -1.0];
var margin = {
top: 30,
right: 10,
bottom: 10,
left: 30
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var y0 = Math.max(Math.abs(d3.min(data)), Math.abs(d3.max(data)));
var y = d3.scale.linear()
.domain([-y0, y0])
.range([height, 0])
.nice();
var x = d3.scale.ordinal()
.domain(d3.range(data.length))
.rangeRoundBands([0, width], .2);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
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 + ")");
var line = d3.svg.line()
.x(function(d, i) {
return x(i);
})
.y(function(d) {
return y(d);
});
var dataSum = d3.sum(data, function(d) {
return d;
});
svg.append("g")
.attr("class", "x axis")
.call(yAxis);
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(0," + y(0) + ")")
.call(xAxis)
svg.append("path")
.data([data])
.attr("class", "line")
.attr("d", line);
.axis text {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.line {
fill: none;
stroke: blue;
stroke-width: 2px;
}
.tick text {
font-size: 12px;
}
.tick line {
opacity: 0.2;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
I want to implement a bar chart in D3, but my values on the dx axis are of type Date, data type which the D3 library should accept, but it seems to give me an error like this: attribute width: Expected length, "NaN".
This is my code:
<html>
<head>
<meta charset="utf-8">
<title>a bar graph</title>
</head>
<style>
.axis path,
.axis line{
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 11px;
}
.MyRect {
fill: steelblue;
}
.MyText {
fill: white;
text-anchor: middle;
}
</style>
<body>
<script src="http://d3js.org/d3.v4.min.js" charset="utf-8"></script>
<script>
var width=400;
var height=400;
var svg=d3.select("body")
.append("svg")
.attr("width",width)
.attr("height",height);
var padding = {left:30, right:30, top:20, bottom:20};
var dataset=[10,20,30,40,33,24,12,5];
var xScale = d3.scaleBand()
.domain(d3.range(dataset.length))
.range([0, width-padding.left-padding.right]);
var yScale = d3.scaleLinear()
.domain([0,d3.max(dataset)])
.range([height-padding.top-padding.bottom,0]);
var xAxis = d3.axisBottom()
.scale(xScale)
var yAxis = d3.axisLeft()
.scale(yScale)
var rectPadding=4;
var rects = svg.selectAll(".Myrect")
.data(dataset)
.enter()
.append("rect")
.attr("class","Myrect")
.attr("transform","translate(" + padding.left + "," + padding.top + ")")
.attr("x",function(d,i){
return xScale(i) + rectPadding/2;
})
.attr("y",function(d){
return yScale(d);
})
.attr("width",xScale.range()- rectPadding)
.attr("height",function(d){
return height - padding.top - padding.bottom - yScale(d);
});
var texts = svg.selectAll(".MyText")
.data(dataset)
.enter()
.append("text")
.attr("class","MyText")
.attr("transform","translate(" + padding.left + "," + padding.top + ")")
.attr("x", function(d,i){
return xScale(i) + rectPadding/2;
})
.attr("y",function(d){
return yScale(d);
})
.attr("dx",function(){
return (xScale.range() - rectPadding)/2;
})
.attr("dy",function(d){
return 20;
})
.text(function(d){
return d;
});
svg.append("g")
.attr("class","axis")
.attr("transform","translate(" + padding.left + "," + (height - padding.bottom) + ")")
.call(xAxis);
svg.append("g")
.attr("class","axis")
.attr("transform","translate(" + padding.left + "," + padding.top + ")")
.call(yAxis);
</script>
</body>
</html>
And the another error: attribute dx: Expected length, "NaN". I think it arises from band scales, but after using the introduction of the official, it still can't work.
The introduction of the official:
var x = d3.scaleBand()
.domain(["a", "b", "c"])
.range([0, width]);
So when I want to call the code, I think it should be used like this in my pasted code:
var texts = svg.selectAll(".MyText")
.data(dataset)
.enter()
.append("text")
.attr("class","MyText")
.attr("transform","translate(" + padding.left + "," + padding.top + ")")
.attr("x", function(d,i){
return xScale(i) + rectPadding/2;
})
.attr("y",function(d){
return yScale(d);
})
.attr("dx",function(){
return (xScale.range() - rectPadding)/2;
})
.attr("dy",function(d){
return 20;
})
.text(function(d){
return d;
});
But it seems to give me two errors. I'm a beginner. Thank you very much!
Right now, for the width of the rectangles and the dx of the texts, you're using:
xScale.range() - rectPadding
But xScale.range() returns an array, and array - number will give you a NaN. And you're not getting anywhere with a NaN...
Instead of xScale.range(), which will return an array, you should use:
xScale.bandwidth();
Which not only returns a proper number, but it's also what you're looking for.
Here is your code with that change:
<style>
.axis path,
.axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 11px;
}
.MyRect {
fill: steelblue;
}
.MyText {
fill: white;
text-anchor: middle;
}
</style>
<body>
<script src="https://d3js.org/d3.v4.min.js" charset="utf-8"></script>
<script>
var width = 400;
var height = 400;
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var padding = {
left: 30,
right: 30,
top: 20,
bottom: 20
};
var dataset = [10, 20, 30, 40, 33, 24, 12, 5];
var xScale = d3.scaleBand()
.domain(d3.range(dataset.length))
.range([0, width - padding.left - padding.right]);
var yScale = d3.scaleLinear()
.domain([0, d3.max(dataset)])
.range([height - padding.top - padding.bottom, 0]);
var xAxis = d3.axisBottom()
.scale(xScale)
var yAxis = d3.axisLeft()
.scale(yScale)
var rectPadding = 4;
var rects = svg.selectAll(".Myrect")
.data(dataset)
.enter()
.append("rect")
.attr("class", "Myrect")
.attr("transform", "translate(" + padding.left + "," + padding.top + ")")
.attr("x", function(d, i) {
return xScale(i) + rectPadding / 2;
})
.attr("y", function(d) {
return yScale(d);
})
.attr("width", xScale.bandwidth() - rectPadding)
.attr("height", function(d) {
return height - padding.top - padding.bottom - yScale(d);
});
var texts = svg.selectAll(".MyText")
.data(dataset)
.enter()
.append("text")
.attr("class", "MyText")
.attr("transform", "translate(" + padding.left + "," + padding.top + ")")
.attr("x", function(d, i) {
return xScale(i) + rectPadding / 2;
})
.attr("y", function(d) {
return yScale(d);
})
.attr("dx", function() {
return (xScale.bandwidth() - rectPadding) / 2;
})
.attr("dy", function(d) {
return 20;
})
.text(function(d) {
return d;
});
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(" + padding.left + "," + (height - padding.bottom) + ")")
.call(xAxis);
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(" + padding.left + "," + padding.top + ")")
.call(yAxis);
</script>
</body>
PS: You don't need rectPadding. Just set the padding in the band scale:
var xScale = d3.scaleBand()
.domain(d3.range(dataset.length))
.range([0, width-padding.left-padding.right])
.padding(0.2);//some value here
I have got the same error when element height for chart is too small due to this code (20px per row)
document.getElementById('chart').style.height = (data.getNumberOfRows() * 20) + 'px';
It was fine for several data items but when I had only a few items I got error. So I fixed it by adding some min-height
<div id="chart" style="width: 100%; min-height: 100px"></div>
Hi I'm using the following code
var data = [4, 8, 15, 16, 23, 42];
var x = d3.scale.linear()
.domain([0, d3.max(data)])
.range([0, 420]);
d3.select(".chart")
.selectAll("div")
.data(data)
.enter().append("div")
.style("width", function(d) { return x(d) + "px"; })
.text(function(d) { return d; });
From http://codepen.io/mbostock/pen/Jaemg and I'm trying to add x-axis with a logarithmic format.
What I'm trying to achieve is the below image but I keep hitting dead ends. Any clue or idea on how to achieve this.
Many thanks for your help.
My first comment would be to shift to a proper svg based drawing instead of using div to create your bars. When starting a new plot, I tend to start my work off this classic example. You, though are going to want to use a log scale on your x-axis:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.bar {
fill: steelblue;
}
.bar:hover {
fill: brown;
}
.axis {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var data = [4, 8, 15, 16, 23, 42];
var margin = {
top: 20,
right: 20,
bottom: 30,
left: 40
},
width = 500 - margin.left - margin.right,
height = 200 - margin.top - margin.bottom;
var x = d3.scale.log()
.domain([0.01, 100])
.range([0, width]);
var y = d3.scale.ordinal()
.rangeRoundBands([0, height], .1)
.domain(d3.range(data.length))
var xAxis = d3.svg.axis()
.scale(x)
.orient("top")
.ticks(0, "1g")
.tickFormat(function(d){
if (d === 0.01) return "0"
else if (d === 1 || d === 10 || d === 100) return d;
else return "";
});
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,0)")
.call(xAxis);
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", 0)
.attr("width", function(d){
return x(d);
})
.attr("y", function(d, i) {
return y(i);
})
.attr("height", function(d,i) {
return y.rangeBand();
});
</script>