I am trying to place the legends underneath the stacked bar chart aligned block. Can not transition it properly. Below is the code i have, at this moment the legends appear on the left top corner, what i am trying to do is the tranistion it properly underneath the stacked bar, is there any suggestion of how can i transition it so it fits the svg as well. Any suggestion will be appriciated
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>D3 Example</title>
<script src="https://d3js.org/d3.v4.min.js"></script>
<link href='https://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'>
<style>
</style>
</head>
<style>
</style>
<body>
<div class="canvas">
</div>
<script>
var data = [
{month: "Q1-2016", apples: 3840, bananas: 1920, cherries: -1960},
{month: "Q2-2016", apples: 1600, bananas: 1440, cherries: -960},
{month: "Q3-2016", apples: 640, bananas: 960, cherries: -640},
{month: "Q4-2016", apples: 320, bananas: 480, cherries: -640},
{month: "Q5-2016", apples: 3840, bananas: 1920, cherries: -1960},
{month: "Q6-2016", apples: 1600, bananas: 1440, cherries: -960},
{month: "Q7-2016", apples: 640, bananas: 960, cherries: -640},
{month: "Q8-2016", apples: 320, bananas: 480, cherries: -640},
{month: "Q9-2016", apples: 3840, bananas: 1920, cherries: -1960},
{month: "Q10-2016", apples: 1600, bananas: 1440, cherries: 960},
{month: "Q11-2016", apples: 640, bananas: 960, cherries: -640},
{month: "Q12-2016", apples: 320, bananas: 480, cherries: -640},
];
var series = d3.stack()
.keys(["apples", "bananas", "cherries"])
.offset(d3.stackOffsetDiverging)
(data);
var margin = {top: 20, right: 30, bottom: 30, left: 60},
width = 900,
height = 600,
padding = 40,
svg = d3.select(".canvas").append('svg').attr('height', height).attr('width',width);
var x = d3.scaleBand()
.domain(data.map(function(d){return d.month;}))
.rangeRound([margin.left, width-margin.right])
.padding(0.1);
var y = d3.scaleLinear()
.domain([d3.min(series, stackMin), d3.max(series, stackMax)])
.rangeRound([height - margin.bottom, margin.top]);
var colors = ["#66b3ff", "#b3d9ff", "#99ddff", "#99ffdd"];
var z = d3.scaleOrdinal(colors);
//create and call the axes
const xAxis = d3.axisBottom(x);
const yAxis = d3.axisLeft(y);
console.log(series);
svg.append('g')
.selectAll('g')
.data(series)
.enter().append('g')
.attr('fill', function (d) {
return z(d.key);
})
.selectAll('rect')
.data(function(d){ return d; })
.enter().append('rect')
.attr('width', x.bandwidth)
.attr('x', function(d){ return x(d.data.month)})
.attr("y", function(d) { return y(d[1]); })
.attr("height", function(d) { return y(d[0]) - y(d[1]); })
svg.append("g")
.attr("transform", "translate(0," + y(0) + ")")
.call(d3.axisBottom(x));
svg.append("g")
.attr("transform", "translate(" + margin.left + ",0)")
.call(d3.axisLeft(y));
var legend = svg.append('g')
.attr('class', 'legend')
.attr('transform', 'translate(' + (padding + 12) + ',0)');
legend.selectAll('rect')
.data(series)
.enter()
.append('rect')
.attr('x', 0)
.attr('y', function(d,i){
return i * 18;
})
.attr('width', 12)
.attr('height', 12)
.attr('fill', function(d,i){
return z(i);
});
legend.selectAll('text')
.data(series)
.enter()
.append('text')
.text(function(d){
return d.key;
})
.attr('x', -18)
.attr('y', function(d, i){
return i * 18;
})
.attr('text-anchor', 'start')
.attr('alignment-baseline', 'hanging');
function stackMin(serie) {
return d3.min(serie, function(d) { return d[0]; });
}
function stackMax(serie) {
return d3.max(serie, function(d) { return d[1]; });
}
</script>
</body>
</html>
There were a few things going wrong in your code, so I tried to fix some of it.
Your chart elements (i.e. the bars and axis) should be added to a group. This will allow it to be moved/translated appropriately as needed. I create a chart variable and assigned these elements to it, rather than directly to the svg. This makes the structure easier to see when you view in the console too.
The height of the legend area should be declared and accounted for in the chart. I declared this as a variable legendh and account for it in the y axis range.
If you want your legend group to appear below the chart it needs to be translated below the chart. Your earlier transform on the group mentioned .attr('transform', 'translate(' + (padding + 12) + ',0)'); making the y coordinates 0. I changed it to .attr('transform', 'translate(' + (padding + 12) + ','+ (height - legendh) + ')');. This tells the legend group to move down from the top by the height - legendh amount, thus placing it below the chart.
Lastly, the colors in the legend were not matching the colors in the chart/bars. This was because your bar fill was goverened by d.key but your legend fill was based on i. I chose to make it uniform and get the color using z(i).
Here is the working block: https://bl.ocks.org/akulmehta/80153b35ab7498d30408f92cfa50f356
Here is the working code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>D3 Example</title>
<script src="https://d3js.org/d3.v4.min.js"></script>
<link href='https://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'>
<style>
</style>
</head>
<style>
</style>
<body>
<div class="canvas">
</div>
<script>
var data = [
{month: "Q1-2016", apples: 3840, bananas: 1920, cherries: -1960},
{month: "Q2-2016", apples: 1600, bananas: 1440, cherries: -960},
{month: "Q3-2016", apples: 640, bananas: 960, cherries: -640},
{month: "Q4-2016", apples: 320, bananas: 480, cherries: -640},
{month: "Q5-2016", apples: 3840, bananas: 1920, cherries: -1960},
{month: "Q6-2016", apples: 1600, bananas: 1440, cherries: -960},
{month: "Q7-2016", apples: 640, bananas: 960, cherries: -640},
{month: "Q8-2016", apples: 320, bananas: 480, cherries: -640},
{month: "Q9-2016", apples: 3840, bananas: 1920, cherries: -1960},
{month: "Q10-2016", apples: 1600, bananas: 1440, cherries: 960},
{month: "Q11-2016", apples: 640, bananas: 960, cherries: -640},
{month: "Q12-2016", apples: 320, bananas: 480, cherries: -640},
];
var series = d3.stack()
.keys(["apples", "bananas", "cherries"])
.offset(d3.stackOffsetDiverging)
(data);
var margin = {top: 20, right: 30, bottom: 30, left: 60},
width = 900,
height = 500,
legendh = 100, //determines the height of the legend below the chart
padding = 40,
svg = d3.select(".canvas").append('svg').attr('height', height).attr('width',width);
var x = d3.scaleBand()
.domain(data.map(function(d){return d.month;}))
.rangeRound([margin.left, width-margin.right])
.padding(0.1);
var y = d3.scaleLinear()
.domain([d3.min(series, stackMin), d3.max(series, stackMax)])
.rangeRound([height - margin.bottom - legendh, margin.top]);
var colors = ["#66b3ff", "#b3d9ff", "#99ddff", "#99ffdd"];
var z = d3.scaleOrdinal(colors);
//create and call the axes
const xAxis = d3.axisBottom(x);
const yAxis = d3.axisLeft(y);
var chart = svg.append('g').attr('id','chart'); //make a chart group inside the svg
chart.append('g')
.selectAll('g')
.data(series)
.enter().append('g')
.attr('fill', function (d,i) { //because the legend is based on i this should also be based on i
return z(i);
})
.selectAll('rect')
.data(function(d){ return d; })
.enter().append('rect')
.attr('width', x.bandwidth)
.attr('x', function(d){ return x(d.data.month)})
.attr("y", function(d) { return y(d[1]); })
.attr("height", function(d) { return y(d[0]) - y(d[1]); })
chart.append("g")
.attr("transform", "translate(0," + y(0) + ")")
.call(d3.axisBottom(x));
chart.append("g")
.attr("transform", "translate(" + margin.left + ",0)")
.call(d3.axisLeft(y));
var legend = svg.append('g')
.attr('class', 'legend')
.attr('transform', 'translate(' + (padding + 12) + ','+ (height - legendh) + ')');
legend.selectAll('rect')
.data(series)
.enter()
.append('rect')
.attr('x', 0)
.attr('y', function(d,i){
return i * 18;
})
.attr('width', 12)
.attr('height', 12)
.attr('fill', function(d,i){
console.log(z(i));
return z(i);
});
legend.selectAll('text')
.data(series)
.enter()
.append('text')
.text(function(d){
return d.key;
})
.attr('x', 15)
.attr('y', function(d, i){
return i * 18;
})
.attr('text-anchor', 'start')
.attr('alignment-baseline', 'hanging');
function stackMin(serie) {
return d3.min(serie, function(d) { return d[0]; });
}
function stackMax(serie) {
return d3.max(serie, function(d) { return d[1]; });
}
</script>
</body>
</html>
D3JS Is it possible to draw Vertical set of Bubbles based on the size
I am very new to Java script framework especially visualization like D3JS. For basic chart I can get the samples from web. But these kind of manipulated charts, I dont how to create like this one. Please help.
data json:
[{Total: 750, left: 250, rigth: 500},
{Total: 75, left: 25, rigth: 50}
]
Total: Total No of Employee
Left: Total No of Female Employee
Right: Total No of Male Employee
The circles are drawn with the following snippet.
The horizontal lines and numbers are left as an exercise.
var svgWidth = 700, svgHeight = 700;
var svg = d3.select('body').append('svg').attr('width', svgWidth).attr('height', svgHeight);
var data = [
{Total: 750, left: 250, right: 500},
{Total: 75, left: 25, right: 50},
{Total: 1000, left: 750, right: 250},
{Total: 125, left: 25, right: 100}
];
var yScale = d3.scaleBand().domain(d3.range(data.length)).rangeRound([0,svgHeight]);
var radius = d3.scaleLinear().domain([0,1200]).range([10, yScale.bandwidth()*0.5]);
var bars = svg.selectAll('.bar')
.data(data)
.enter()
.append('g')
.attr('class', 'bar')
.attr('transform', (d,i) => `translate(${svgWidth * 0.5},${yScale(i)+yScale.bandwidth()*0.5})`)
;
bars.append('circle')
.attr('r', d => radius(d.Total));
bars.append('text')
.text( d => d.Total)
.attr('text-anchor', 'middle')
.attr('dy', '0.3em');
.bar circle {
fill:none;
stroke:steelblue;
stroke-width: 2;
}
<script src="https://d3js.org/d3.v5.min.js"></script>
Im new to d3js, i want to arrange the grid lines manually , i will share what result im getting and what i want. please check the screenshot ,two different images are there.
1st image is the output that im getting from the code below, but i want my result like the second image.
[<!doctype html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Line Chart</title>
<style>
.axis path,
.axis line{
fill: none;
stroke: black;
}
.line{
fill: none;
stroke: blue;
stroke-width: 2px;
}
.tick text{
font-size: 12px;
}
.tick line{
opacity: 1;
}
</style>
</head>
<body>
<script src="http://d3js.org/d3.v3.js"></script>
<script>
var margin = {top: 20, right: 100, bottom: 30, left: 100},
width = 350 ;
height = 350 ;
var dataset = \[
{x: 0, y: 0},
{x: 1, y: 0.5},
{x: 2, y: 1},
{x: 3, y: 1.5},
{x: 4, y: 2},
{x: 5, y: 2.5},
{x: 6, y: 3},
\];
var xScale = d3.scale.linear()
.domain(\[0, d3.max(dataset, function(d){ return d.x; })\])
.range(\[0, width\]);
var yScale = d3.scale.linear()
.domain(\[0, d3.max(dataset, function(d){ return d.y; })\])
.range(\[height, 0\]);
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom")
.innerTickSize(-height)
.outerTickSize(0)
.tickPadding(10);
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left")
.innerTickSize(-width)
.outerTickSize(0)
.tickPadding(10);
var line = d3.svg.line()
.x(function(d) { return xScale(d.x); })
.y(function(d) { return yScale(d.y); });
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)
svg.append("path")
.data(\[dataset\])
.attr("class", "line")
.attr("d", line);
</script>
</body>
</html>]
Just set the number of ticks in the x axis generator:
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom")
.innerTickSize(-height)
.outerTickSize(0)
.tickPadding(10)
.ticks(5);
Here is a demo:
var margin = {top: 20, right: 100, bottom: 30, left: 100},
width = 350 ;
height = 350 ;
var dataset = [
{x: 0, y: 0},
{x: 1, y: 0.5},
{x: 2, y: 1},
{x: 3, y: 1.5},
{x: 4, y: 2},
{x: 5, y: 2.5},
{x: 6, y: 3},
];
var xScale = d3.scale.linear()
.domain([0, d3.max(dataset, function(d){ return d.x; })])
.range([0, width]);
var yScale = d3.scale.linear()
.domain([0, d3.max(dataset, function(d){ return d.y; })])
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom")
.innerTickSize(-height)
.outerTickSize(0)
.tickPadding(10)
.ticks(5);
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left")
.innerTickSize(-width)
.outerTickSize(0)
.tickPadding(10);
var line = d3.svg.line()
.x(function(d) { return xScale(d.x); })
.y(function(d) { return yScale(d.y); });
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)
svg.append("path")
.data([dataset])
.attr("class", "line")
.attr("d", line);
.axis path,
.axis line{
fill: none;
stroke: black;
}
.line{
fill: none;
stroke: blue;
stroke-width: 2px;
}
.tick text{
font-size: 12px;
}
.tick line{
opacity: 1;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
I am trying to implement interactive data visualization using DC,D3 and CrossFilter JS. But I am facing three issue.
Width of the bar in the bar chart
Brush is solid black
X,Y-Axis lines are too thick.
I would like to increase width of the bar and like to have Brush on feature with transparent color.
code:
dateChart.width(750)
.margins({top: 10, right: 50, bottom: 30, left: 40})
.elasticY(true)
.dimension(timeDim)
.group(dates)
.brushOn(true)
.gap(65)
.transitionDuration(500)
.centerBar(true)
// .xUnits(function(){return 10;})
.round(d3.time.day.round)
// .renderlet(colorRenderlet)
.x(d3.time.scale()
.domain([new Date(2016, 0, 1), new Date(2016, 0, 31)])
.rangeRound([0, 1 * 90]))
.renderHorizontalGridLines(true)
.filter([new Date(2016, 0, 1), new Date(2016, 0, 1)])
.xAxis().tickFormat();
//.fluctuationChart.yAxis().ticks(5);
If I create a simple scatter plot using d3.js, I can record the mouseover events on the circle elements by printing 'hey' to the console:
http://jsfiddle.net/pkerpedjiev/opmhaz0n/
<!DOCTYPE html>
<meta charset="utf-8">
<div class="chart" style="position: aboslute; left: 0px: top: 0px; width: 300px; height: 200px;" ></div>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var xVals = [4, 8, 15, 16, 23, 42,72];
var yVals = [13, 49, 34, 2, 22, 23, 44];
var data = xVals.map(function(d,i) { return [d, yVals[i]]; });
var width=300;
var height=200;
console.log('data:', data);
/*
var div = d3.select(".chart").append("div")
.style("position", "absolute")
.style("width", width + "px")
.style("height", height + "px")
.style("left", 0 + "px")
.style("top", 0 + "px")
.style("opacity", 0.2);
*/
var svg = d3.select(".chart")
.append("svg")
svg.selectAll('circle')
.data(data)
.enter()
.append("circle")
.attr('cx', function(d) { return d[0]; })
.attr('cy', function(d) { return d[1]; })
.attr('r', 4)
.attr('fill', 'black')
.on('mouseover', function(d) { console.log('hey'); });
</script>
If I add a div behind the svg, however, the mouseover event doesn't get recorded:
http://jsfiddle.net/pkerpedjiev/Lxgbycr8/1/
<!DOCTYPE html>
<meta charset="utf-8">
<div class="chart" style="position: aboslute; left: 0px: top: 0px; width: 300px; height: 200px;" ></div>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var xVals = [4, 8, 15, 16, 23, 42,72];
var yVals = [13, 49, 34, 2, 22, 23, 44];
var data = xVals.map(function(d,i) { return [d, yVals[i]]; });
var width=300;
var height=200;
console.log('data:', data);
var div = d3.select(".chart").append("div")
.style("position", "absolute")
.style("width", width + "px")
.style("height", height + "px")
.style("left", 0 + "px")
.style("top", 0 + "px")
.style("opacity", 0.2);
var svg = d3.select(".chart")
.append("svg")
svg.selectAll('circle')
.data(data)
.enter()
.append("circle")
.attr('cx', function(d) { return d[0]; })
.attr('cy', function(d) { return d[1]; })
.attr('r', 4)
.attr('fill', 'black')
.on('mouseover', function(d) { console.log('hey'); });
</script>
Is there a way to register the 'mouseover' event when there's a div in the background behind the svg?
The positioning of the elements is interfering with what you may expect for mouse events here, in particular you need to set position to absolute for the SVG as well to make it appear in front of the div.
If you want the SVG to "catch" events only on certain elements, set pointer-events to none on the SVG and to all on the elements you want to receive the events.