Related
I am trying to create a scrollable list. I have looked at other tutorials on here but it does not seem to be working. Essentially I am appending an SVG to a div. Inside this SVG is a D3JS stacked Bar Graph. to the right of this bar graph I am appending a 'g' element with an svg inside. I have set a height for this right SVG. Inside this I have populated a list that would extend beyond the height of the SVG. I have set the CSS for this svg to 'overflow-y: scroll'.
In spite of all of this I can not get this svg to scroll. Instead it just grows to the size of the list and extends past to intended bounds. Please See code below.
var barSVG = d3.select("#documents_reviewed_bar_chart").append("svg")
.classed('barChart-docs-reviewed', true)
.attr('id', 'barSVG')
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr('id', 'gElement')
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var count = 0;
var graph = barSVG.append('g')
.attr('id', 'graphElement')
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "Date"; }));
data.forEach(function(d) {
var myDate = d.Date; //add to stock code
var y0 = 0;
d.people = color.domain().map(function(name) { return {myDate:myDate, name: name, y0: y0, y1: y0 += +d[name]}; });
d.total = d.people[d.people.length - 1].y1;
count = isNaN(d.total) ? count : count + d.total
});
x.domain(data.map(function(d) { return d.Date; }));
y.domain([0, d3.max(data, function(d) { return d.total; })]);
graph.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", "rotate(-65)" )
.style("cursor", "pointer")
.on('click', renderHorizontalChart);
graph.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("Population");
graph.append('text')
.text('Total: ' + count)
.attr('x', 20)
.attr('y', -10)
var state = graph.selectAll(".state")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + "0" + ",0)"; });
//.attr("transform", function(d) { return "translate(" + x(d.Date) + ",0)"; })
state.selectAll("rect")
.data(function(d) {
return d.people;
})
.enter().append("rect")
.attr("width", x.rangeBand())
.attr("y", height)
.attr("x",function(d) { //add to stock code
return x(d.myDate)
})
.attr("height", 0 )
.style("fill", function(d) { return color(d.name); })
.transition()
.duration(1000)
.attr("y", function(d) { return y(d.y1); })
.attr("height", function(d) { return y(d.y0) - y(d.y1); })
.attr("class", function(d) {
classLabel = d.name.replace(/,\s/g, ''); //remove spaces
return "class" + classLabel;
});
state.selectAll("rect")
.on("mouseover", function(d){
var delta = d.y1 - d.y0;
var xPos = parseFloat(d3.select(this).attr("x"));
var yPos = parseFloat(d3.select(this).attr("y"));
var height = parseFloat(d3.select(this).attr("height"))
d3.select(this).attr("stroke","black").attr("stroke-width",2);
tooltip.style("visibility", "visible");
tooltip.style("top", (event.pageY-10)+"px").style("left",(event.pageX+10)+"px");
tooltip.style('background', 'black')
tooltip.style('color', 'white')
tooltip.style('border-radius', '3px')
tooltip.style('padding', '5px')
tooltip.style('opacity', '0.8')
tooltip.style('font-size', '10px;')
tooltip.text(d.name +": "+ delta)
})
.on("mouseout",function(){
tooltip.style("visibility", "hidden");
graph.select(".tooltip").remove();
d3.select(this).attr("stroke","pink").attr("stroke-width",0.2);
})
var itemsAmount = 0
var rightSVG = barSVG.append('svg').classed('rightSVG', true)
.attr('height', '390')
.attr('id', 'rightSVG')
var legendSVG = rightSVG.append('svg').classed('legendSVG', true)
.attr('id', 'legendSVG')
var legend = legendSVG.selectAll(".legend")
.data(color.domain().slice().reverse())
.enter().append("g")
//.attr("class", "legend")
.attr("class", function (d) {
itemsAmount = itemsAmount + 1
legendClassArray.push(d.replace(/,\s/g, '')); //remove spaces
return "legend";
})
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
//reverse order to match order in which bars are stacked
legendClassArray = legendClassArray.reverse();
legend.append("rect")
.attr("x", width - 0)
.attr("width", 18)
.attr("height", 18)
.style("fill", color)
.attr("id", function (d, i) {
return "id#" + d.replace(/,\s/g, '');
})
.on("mouseover",function(){
if (active_link === "0") d3.select(this).style("cursor", "pointer");
else {
if (active_link.split("class").pop() === this.id.split("id#").pop()) {
d3.select(this).style("cursor", "pointer");
} else d3.select(this).style("cursor", "auto");
}
})
.on("click",function(d){
if (!this.id.includes('active')) { //nothing selected, turn on this selection
d3.select(this)
.attr('id', function(){
return this.id + 'active'
})
.style("stroke", "black")
.style("stroke-width", 2);
active_link = this.id.split("id#").pop();
plotSingle(this);
} else { //deactivate
d3.select(this)
.classed("active", false)
.attr('id', function() {
return this.id.replace('active', '')
})
.style("stroke", "none")
.style("stroke-width", 0);
plotSingle(this);
}
});
legend.append("text")
.attr("x", width - 6)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
legendSVG.append("text")
.classed('queryButton', true)
.attr("x", width - 6)
.attr("y", height)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text('Run Query')
.on('click', function(){
if (newArr.length > 0) {
d3.select('#barSVG').remove();
runScript(newArr)
}
});
legendSVG.append("text")
.classed('queryButton', true)
.attr("x", width - 6)
.attr("y", height + 18)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text('Reset')
The Specific SVG that I want to be scrollable is the 'rightSVG'.
As you can see in the image, the names are cut off. There should be a scrollable legend where I am able to see 29 data items.
Also, I have added the below CSS:
#documents_reviewed_bar_chart, #gElement{
max-height: 390;
overflow-y: scroll;
}
Short answer: you can't have a scrollable SVG inside another SVG. The overflow-y: scroll applies to HTML elements, not to SVG elements.
Alternative (and hacky) answer: technically, what you want is possible, but you'll have to wrap your inner SVG in an HTML element, which has to be inside a foreignObject.
This alternative is suboptimal, makes little sense and doesn't work on IE. However, just for the sake of curiosity, this is how you can do it:
var outerSvg = d3.select("body")
.append("svg")
.attr("width", 500)
.attr("height", 200)
.style("background-color", "darkkhaki");
var foreign = outerSvg.append("foreignObject")
.attr("x", 300)
.attr("y", 10)
.attr("width", 150)
.attr("height", 180)
.append("xhtml:div")
.style("max-height", "180px")
.style("overflow-y", "scroll");
var innerSvg = foreign.append("svg")
.attr("width", 133)
.attr("height", 1000)
.style("background-color", "powderblue");
var color = d3.scaleOrdinal(d3.schemeCategory20);
var texts = innerSvg.selectAll("foo")
.data(d3.range(65))
.enter()
.append("text")
.attr("x", 40)
.attr("y", (d,i)=> 20 + 15*i)
.text("foo bar baz")
var rects = innerSvg.selectAll("foo")
.data(d3.range(65))
.enter()
.append("rect")
.attr("x", 10)
.attr("y", (d,i)=> 8 + 15*i)
.attr("width", 20)
.attr("height", 13)
.attr("fill", (d,i)=>color(i));
<script src="https://d3js.org/d3.v4.min.js"></script>
The outer SVG is light brown (or khaki). The inner SVG, at the right, is blue, and it's inside a <div> with overflow-y: scroll;.
I am using D3 charting library to create charts with Angular-cli. D3 version is 4.2.2. I create a multi-line chart and here is I am trying to add legend to the chart. Following code is my code look like.
import {Directive, ElementRef, HostListener, Renderer} from '#angular/core';
import * as D3 from 'd3';
#Directive({
selector: 'bar-graph'
})
export class BarGraphDirective {
private htmlElement:HTMLElement;
constructor(private elementRef:ElementRef, private renderer: Renderer) {
this.htmlElement = this.elementRef.nativeElement;
console.log(this.htmlElement);
console.log(D3);
let d3:any = D3;
var data = [{
"date": "2016-10-01",
"sales": 110,
"searches": 67
}, ...];
// set the dimensions and margins of the graph
var margin = {
top: 20,
right: 80,
bottom: 30,
left: 50
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// parse the date / time
var parseDate = d3.timeParse("%Y-%m-%d");
var formatTime = d3.timeFormat("%e %B");
// set the ranges
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
var sales = function (d) {
return d["sales"];
}
var searches = function (d) {
return d.searches;
}
// define the line
var line = d3.line()
.x(function (d) {
return x(d.date);
})
.y(function (d) {
return y(d.sales);
});
var svg = d3.select(this.htmlElement).append("svg")
.attr("class", "bar-graph")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var color = d3.scaleOrdinal(d3.schemeCategory10);
var tooltip = d3.select("body").append("div")
.style("opacity", 0);
// format the data
data.forEach(function (d) {
d.date = parseDate(d.date);
});
x.domain(d3.extent(data, function (d) {
return d.date;
}));
y.domain([0, d3.max(data, function (d) {
return d.sales > d.searches ? d.sales : d.searches;
})]);
// Add the line path.
svg.append("path")
.attr("class", "line")
.style("fill", "none")
.attr("d", line(data))
.style("stroke", "orange")
.style("stroke-width", "2px");
// change line to look at searches
line.y(function (d) {
return y(d.searches);
});
// Add the second line path.
svg.append("path")
.attr("class", "line")
.style("fill", "none")
.attr("d", line(data))
.style("stroke", "steelblue")
.style("stroke-width", "2px");
// Add sales to the scatterplot
svg.selectAll(".sales-circle")
.data(data)
.enter().append("circle")
.attr('class', 'sales-circle')
.attr("r", 4)
.attr("cx", function (d) {
return x(d.date);
})
.attr("cy", function (d) {
return y(d.sales);
})
.style("fill", "orange");
// Add searches to the scatterplot
svg.selectAll(".searches-circle")
.data(data)
.enter().append("circle")
.attr("r", 4)
.attr('class', 'searches-circle')
.attr("cx", function (d) {
return x(d.date);
})
.attr("cy", function (d) {
return y(d.searches);
})
.style("fill", "steelblue")
.on("mouseover", function (d) {
tooltip.transition()
.duration(200)
.style("opacity", .9);
tooltip.html(formatTime(d["date"]) + "<br/> Searches: " + d["searches"])
.style("left", (d3.event.pageX + 5) + "px")
.style("top", (d3.event.pageY - 28) + "px")
.classed("tooltip", true);
})
.on("mouseout", function (d) {
tooltip.transition()
.duration(500)
.style("opacity", 0);
});
// draw legend
var legend = svg.selectAll("g")
.data(data)
.enter().append("g")
.attr("class", "legend");
// draw legend colored rectangles
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
// draw legend text
legend.append("text")
.style("font", "14px open-sans")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text("Sales");
// Add the X Axis
svg.append("g")
.style("font", "14px open-sans")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x).tickFormat(d3.timeFormat("%d/%m")));
// Add the Y Axis
svg.append("g")
.style("font", "14px open-sans")
.call(d3.axisLeft(y));
// Add Axis labels
svg.append("text")
.style("font", "14px open-sans")
.attr("text-anchor", "middle")
.attr("transform", "translate(" + (-margin.left / 2) + "," + (height / 2) + ")rotate(-90)")
.text("Sales / Searches");
svg.append("text")
.style("font", "14px open-sans")
.attr("text-anchor", "middle")
.attr("transform", "translate(" + (width / 2) + "," + (height + (margin.bottom)) + ")")
.text("Date");
}
}
My chart looks like below. It shows only one item in the legend. How to add both items (sales & searches) to the legend.
Any suggestion are highly appreciated.
Thanks You
Add this in the code for adding new rect and text:
legend.append("rect")
.attr("x", width - 18)
.attr("y", 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", "steelblue");
// draw legend text
legend.append("text")
.style("font", "14px open-sans")
.attr("x", width - 24)
.attr("y", 18)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text("Searches");
I have a bar chart that updates based on the results selected in a drop-down menu. When I change the selcetion, I get negaitve "y" values. It seems that my domain does not get updated with the new data. When I hard code the domain, my "y" are what I expect them to be. Anyone knows why ? Any other other comments (formatting, etc) welcomed.
var new_data;
//Create SVG margins and patting for the interior
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 600 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
//Create Scale
var xScale = d3
.scale
.ordinal()
.rangeRoundBands([margin.left, width], .1);
;
var yScale = d3
.scale
.linear()
.range([height, 0])
;
var xAxis = d3
.svg
.axis()
.scale(xScale)
.orient("bottom")
.tickPadding([5])
;
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left")
.ticks(10)
;
//Create SVG with the above specs
var svg = d3.select("#container")
.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 + ")")
;
svg
.append("g")
.attr("class", "y axis")
.append("text") // just for the title (ticks are automatic)
.attr("transform", "rotate(-90)") // rotate the text!
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("frequency")
;
var temp = svg
.append("g")
.attr("class", "domx")
;
d3.csv("data3.csv", function(error, csv_data) {
// Filter the dataset to only get dept_1
var new_data = csv_data.filter(function(d) {
return d['dept'] == 'dept_1';
});
// function to handle histogram.
function histoGram(new_data){
//Create Scales
xScale
.domain(new_data.map(function(d) {return d.Pos;}))
;
yScale
// .domain([0, d3.max(new_data, function(d) { return d.Value; })])
.domain([0, d3.max(new_data, function(d) { return d.Value; })])
// .domain([0, 20])
;
svg
.select(".x.axis")
.transition()
.duration(1500)
.call(xAxis)
;
svg
.select(".y.axis")
.transition()
.duration(1500)
.call(yAxis)
;
// Data Join
var MyGroups = temp
.selectAll("g")
.data(new_data);
;
var MyGroupsEnter = MyGroups
.enter()
.append("g")
;
//Update
MyGroups
.attr("class", "update")
;
//Enter
MyGroupsEnter
.append("rect")
.attr("class", "enter")
.attr("x", function(d) { return xScale(d.Pos); })
.attr("y", function(d) { return (yScale(d.Value));})
.attr("width", xScale.rangeBand())
.attr("height", function(d) { return (height - yScale(d.Value)); })
.text(function(d) { return d.Value; })
.attr("fill", function(d) {return "rgb(0, 0, 0)";})
.style("fill-opacity", 0.2)
;
MyGroupsEnter
.append("text")
.attr("class", "text")
.text(function(d) { return d.Value; })
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "black")
.attr("text-anchor", "middle")
.attr("x", function(d) { return xScale(d.Pos) + xScale.rangeBand()/2; })
.attr("y", function(d) { return yScale(d.Value) - 10; })
;
//Enter + Update
MyGroups
.transition()
.duration(1500)
.select("rect")
.attr("x", function(d) { return xScale(d.Pos); })
.attr("width", xScale.rangeBand())
.attr("y", function(d) { return (yScale(d.Value));})
.attr("height", function(d) { return (height - yScale(d.Value)); })
.text(function(d) { return d.Value; })
.style("fill-opacity", 1) // set the fill opacity
.attr("fill", function(d) {return "rgb(0, 0, " + (d.Value * 30) + ")";})
;
MyGroups
.transition()
.duration(1500)
.select("text")
.attr("class", "text")
.text(function(d) { return d.Value; })
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "black")
.attr("text-anchor", "middle")
.attr("x", function(d) { return xScale(d.Pos) + xScale.rangeBand()/2; })
.attr("y", function(d) { return yScale(d.Value) - 8; })
;
MyGroups
.exit()
.transition()
.duration(1500)
.remove()
;
}
histoGram(new_data);
var options = ["dept_1","dept_2","dept_3"];
var dropDown = d3
.select("#sel_button")
.append("select")
.attr("name", "options-list")
.attr("id", "id-name");
var options = dropDown
.selectAll("option")
.data(options)
.enter()
.append("option");
options
.text(function (d) { return d; })
.attr("value", function (d) { return d; });
d3.select("#id-name")
.on("change", function() {
var value = d3.select(this).property("value");
var new_data2 = csv_data.filter(function(d) {
return d['dept'] == value;
});
histoGram(new_data2);
});
});
Here is the data:
dept,Pos,Value
dept_1,d1_p1,1
dept_1,d1_p10,10
dept_1,d1_p11,11
dept_1,d1_p12,12
dept_2,d2_p1,1.5
dept_2,d2_p2,3
dept_2,d2_p3,4.5
dept_2,d2_p4,6
dept_2,d2_p5,7.5
dept_2,d2_p6,9
dept_2,d2_p7,10.5
dept_2,d2_p8,12
dept_2,d2_p9,13.5
dept_2,d2_p10,15
dept_2,d2_p11,16.5
dept_2,d2_p12,17.5
dept_2,d2_p13,18.5
dept_3,d3_p1,5
dept_3,d3_p2,7
dept_3,d3_p3,10
Firgured out what was my problem. I hadn't defined the format of the values. The max function was returning the maximum number out of character values (9). I added the following piece of code prior to the domain function and everything now works fines.
csv_data.forEach(function(d) {
d.dept = d.dept;
d.Pos = d.Pos;
d.Value = +d.Value;
});
Data being inputted correctly, but shown as undefined when logged:
d3.json('linedata.00.json', function(error, data){
console.log(data)
Error shows up on this line:
data.mydata.forEach(function (kv) {
var labelName = kv.label;
var colorName = kv.color
kv.values.forEach(function (d) {
d.id = d.id;
d.color = colorName;
d.val = +d.val;
d.label = labelName;
});
});
JSON Code as follows:
{
"mydata":[
{
"id":"line01",
"color":"#de6868",
"label":"internal",
"values":[
{"week":"Week 1","val":37},
{"week":"Week 2","val":38},
{"week":"Week 3","val":33},
{"week":"Week 4","val":32},
{"week":"Week 5","val":40},
{"week":"Week 6","val":27},
{"week":"Week 7","val":30},
{"week":"Week 8","val":37},
{"week":"Week 9","val":42},
{"week":"Week 10","val":36},
{"week":"Week 11","val":35},
{"week":"Week 12","val":37},
{"week":"Week 13","val":33}
]
},
The question is. Why am I getting the error when everything is (seemingly) in the right place and how do I fix it?
Callback in it's entirety:
d3.json('linedata.00.json', function(error, data){
console.log(data)
data.mydata.forEach(function (kv) {
var labelName = kv.label;
var colorName = kv.color
kv.values.forEach(function (d) {
d.id = d.id;
d.color = colorName;
d.val = +d.val;
d.label = labelName;
});
});
color.domain(data.mydata.map(function (d) { return d.label; }));
myColor = function(d){return d.color};
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function(d) {
return "<span style='color:red'>" + d3.format("$,")(d.val) + "</span>";
})
svg.call(tip);
var dataNest = d3.nest()
.key(function(d) {return d.label;})
.entries(data.mydata);
function make_y_axis() { // function for the y grid2 lines
return d3.svg.axis()
.scale(y)
.orient("left")
.ticks(5)
}
console.log(yAxis.ticks());
var minY = d3.min(data.mydata, function (kv) { return d3.min(kv.values, function (d) { return d.val; }) });
var maxY = d3.max(data.mydata, function (kv) { return d3.max(kv.values, function (d) {return d.val; }) });
var maxX = d3.max(data.mydata, function (kv){return d3.max(kv.values, function(d){ return d.week + 1;});});
console.log(maxX);
var padding = width/(data.mydata[0].values.length + 1)/2;
x.domain(data.mydata[0].values.map(function (d) { return d.week; })).rangePoints([padding, width-padding]);
y.domain([0,(1.2 * maxY)]);
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");
svg.append("g") // Draw the y grid2 lines
.attr("class", "grid2")
.call(make_y_axis()
.tickSize(-width, 0, 0)
.tickFormat("")
);
svg.selectAll(".grid2")
.selectAll(".tick")
.append("rect")
.attr("width", width)
.attr("height", height / (yAxis.ticks()[0]-1) -3)
.attr("class", function(d, i) {
return ((i) % 2) == 1 ? "zebraGrey" : "zebraNone";
})
.attr("stroke", "none");
var city = svg.selectAll(".branch")
.data(data.mydata)
.enter().append("g")
.attr("class", "branch");
city.append("path")
.attr("class", "line")
.attr("id", function(d){return d.id})
.attr("data-legend",function(d){return d.label;})
.attr("d", function (d) {
return line(d.values);
})
.style("stroke", myColor)
.style("fill", "none")
.style("stroke-width", 3);
svg.selectAll("g.dot")
.data(data.mydata)
.enter().append("g")
.attr("class", "dot")
.selectAll("circle")
.data(function(d) {return d.values; })
.enter().append("circle")
// .style("stroke", function (d){return d.color;})
.attr("r", 2)
.attr("cx", function(d,i) { return x(d.week); })
.attr("cy", function(d,i) { return y(d.val); })
.style("stroke", myColor)
.style("fill", myColor)
.style("stroke-width", 3)
// Tooltip stuff after this
.on("mouseover", tip.show)
.on("mouseout", tip.hide);
// add legend
var legend = svg.append("g")
.attr("class", "legend")
.attr("height", 100)
.attr("width", 100)
.attr('transform', 'translate('+ -1 * .37 * width + ',' + height +')');
legend.selectAll('rect')
.data(data.mydata)
.enter()
.append("rect")
.attr("x", function(d, i){return i * 150 + 480})
.attr("y", 40)
.attr("width", 10)
.attr("height", 10)
.style("fill", myColor)
.style("stroke", "none");
legend.selectAll('text')
.data(data.mydata)
.enter()
.append("text")
.attr("class", "legText")
.attr("x", function(d, i){ return i * 150 + 500;})
.attr("y", 50)
.style("fill", myColor)
.style("stroke", "none")
.text(function(d){return d.label});
});
Thanks everyone for looking into this. The code was right, I had two misplaced comas in the JSON itself that were causing the troubles. Thanks though for the concern.
actually I am working on D3js charts . I have implemented pagination in legends but in the end there are two legends which are repeating.
how should i fix this??
I have provided the fiddle for my current code here. jsfiddle link
Thanks in advance for any help
var data=[
{
"age":"<5",
"population":2704659
},
{
"age":"5-10",
"population":4499890
},
{
"age":"10-13",
"population":6736433
},
{
"age":"14-16",
"population":2159981
},
{
"age":"16-18",
"population":3853788
},
{
"age":"18-22",
"population":8848383
},
{
"age":"22-30",
"population":8384390
},
{
"age":"30-44",
"population":14106543
},
{
"age":"45-64",
"population":8819342
},
{
"age":"≥65",
"population":800000
}
]
var width = 1060,
height = 600,
radius = 175,
color = d3.scale.category10(),
legendNo=4, // number of legends to display at a time
legendCount=0; //To store number of legends
//creating svg element and appending to body
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")"); //transform it to center of body
//creating start and end angle for each arc
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d.population; });
//creating the arcs based on pie layout
var arc = d3.svg.arc() //inner arc with color
.outerRadius(radius)
.innerRadius(radius-70);
//calculate the total to display in hole
var total=0;
data.forEach(function(d) {
d.population;
total +=parseInt(d.population);
legendCount++;
});
//creating svg element for center text
var center_group = svg.append("svg:g")
.attr("class", "center_group")
.attr("transform", "translate(" + (width/2) + "," + (height/2) + ")");
//selecting all inner arcs and appending data
var g = svg.selectAll("arc")
.data(pie(data))
.enter().append("g")
.attr("class", "arc")
//giving colour to each inner arc and execute onClick function
g.append("path")
.attr("d", arc)
.style("fill", function(d) { return color(d.data.age); })
//display text in the inner arcs
g.append("text")
.attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; })
.attr("dy", ".35em")
.style("text-anchor", "middle")
.style("font-size", "14px")
.text(function(d) { return d.data.age; });
count = 0;
var p=0;
var viewdata = data.slice(p,p+legendNo);
var hidedata;
var temp;
//selecting all legend elements
var legend = svg.selectAll(".legend")
.data(viewdata).enter()
.append("g").attr("class", "legend")
//.attr("width", )
.attr("id", function() {
return count++;
})
.attr("transform", function(d, i) {
return "translate("+(-(width/2-30)+ i * 50)+"," + (height/2-50)+ ")";
})
//appending coloured rectangles to legend
svg.selectAll("rect")
.data(viewdata)
.enter().append("rect")
.attr("x", width/2-150)
.attr("y",5)
.attr("dy", "3.00em")
.attr("dx", "1.75em")
.attr("width", 23).attr("height", 23)
.attr("transform", function(d, i) {
return "translate("+(-(width/2-30)+ i * 50)+"," + (height/2-50)+ ")";
})
.style("fill", function(d) {
return color(d.age);
});
var prev=svg.append("svg:text")
.attr("id","prev")
.attr("x", width-1260)
.attr("y",height-385)
.attr("dy", "2.90em")
.attr("dx", "1.75em")
//.attr("stroke", "black")
//.style("fill","white")
// .style("text-anchor", "middle")
.style("font-size", "20px")
//.attr("width", 45).attr("height", 25)
.text("<|")
.on("click",onPrevClick)
var next=svg.append("svg:text")
.attr("id","next")
.attr("x", width-950)
.attr("y",height-385)
.attr("dy", "2.90em")
.attr("dx", "1.75em")
.style("font-size", "20px")
//.attr("stroke", "black")
//.style("fill","white")
//.attr("width", 45).attr("height", 25)
.text("|>")
.on("click",onNextClick)
function onNextClick()
{p+=legendNo;
if(p>=legendCount){
p-=legendNo;
viewdata = data.slice(p,legendCount);
//temp=legendNo-(legendCount-p);
//hidedata =data.slice(p-temp-2,p-2);
}
else{
viewdata = data.slice(p,p+legendNo);
//hidedata =data.slice(0,0);
}
svg.selectAll("rect")
.data(viewdata)
.attr("x", width/2-150)
.attr("y",5)
.attr("dy", "3.00em")
.attr("dx", "1.75em")
.attr("width", 23).attr("height", 23)
.attr("transform", function(d, i) {
return "translate("+(-(width/2-30)+ i * 50)+"," + (height/2-50)+ ")";
})
.style("fill", function(d) {
return color(d.age);
});
legend.select("text").attr("x", width/2-150)
.data(viewdata)
.attr("y", 15)
.attr("dy", "3.00em")
.attr("dx", "1.75em")
//.attr("transform", function(d, i) {return "rotate("+45*i+","+d.age+",200)";})
.attr("text-anchor", "middle").text(function(d) {
return d.age;
});
}
function onPrevClick(){
p-=legendNo;
if(p<=0){
p=0;
}
viewdata = data.slice(p,p+legendNo);
svg.selectAll("rect")
.data(viewdata)
.attr("x", width/2-150)
.attr("y",5)
.attr("dy", "3.00em")
.attr("dx", "1.75em")
.attr("width", 23).attr("height", 23)
.attr("transform", function(d, i) {
return "translate("+(-(width/2-30)+ i * 50)+"," + (height/2-50)+ ")";
})
.style("fill", function(d) {
return color(d.age);
});
legend.select("text").attr("x", width/2-150)
.data(viewdata)
.attr("y", 15)
.attr("dy", "3.00em")
.attr("dx", "1.75em")
//.attr("transform", function(d, i) {return "rotate("+45*i+","+d.age+",200)";})
.attr("text-anchor", "middle").text(function(d) {
return d.age;
});
}
// giving text to legends
legend.append("text").attr("x", width/2-150)
.data(viewdata)
.attr("y", 15)
.attr("dy", "3.00em")
.attr("dx", "1.75em")
//.attr("transform", function(d, i) {return "rotate("+45*i+","+d.age+",200)";})
.attr("text-anchor", "middle").text(function(d) {
return d.age;
});
//displaying legend title
var legendTitle = svg.append("svg:text")
.attr("x", -(width/2-200))
.attr("y", height/2-25)
.style("font-size", "14px")
.style("text-decoration", "underline")
.text("Age Group");
Working demo: http://jsfiddle.net/m6cx7/6/
You need to handle enter and exit of the legends on each Next and Prev click:
var rects = svg.selectAll("rect")
.data(viewdata);
rects.enter()
.append('rect');
rects.exit()
.remove();
rects
.attr("x", width/2-150)
.attr("y",5)
// ...
var legends = svg.selectAll(".legend")
.data(viewdata);
legends.enter()
.append('g')
.classed('legend', true)
.attr("id", function() {
return count++;
})
.attr("transform", function(d, i) {
return "translate("+(-(width/2-30)+ i * 50)+"," + (height/2-50)+ ")";
})
.append('text');
legends.exit()
.remove();
legends
.select('text')
.attr('x', width/2-150)
// ...