Pagination with D3.js - javascript

I am using a large amount of JSON data from an API for D3 bar charts. I would like to show only 10-20 bars at a time. Is there a way to paginate using D3 or do I need to do this another way (php)? Any best practices or suggestions are welcome.

I know this is a late question, but maybe this can still help you out.
I would create pagination in d3 by creating a second array that only contains the data you want shown at a particular time. This sliced array would come from your primary data array. By controlling where the array is sliced, you control the pagination.
I've created a simple example here with a long array divided into five-bar 'pages'.
http://jsfiddle.net/zNxgn/2/

Please go thorugh this piece of code but it makes sense if you go through my block. I have only put the essential part of the code. Link: http://bl.ocks.org/pragyandas
var legendCount = data.series.length;
var legendWidth=10; var legendSpacing=6;
var netLegendHeight=(legendWidth+legendSpacing)*legendCount;
var legendPerPage,totalPages,pageNo;
if(netLegendHeight/height > 1){
legendPerPage=Math.floor(height/(legendWidth+legendSpacing));
totalPages=Math.ceil(legendCount/legendPerPage);
pageNo=1;
var startIndex=(pageNo-1)*legendPerPage;
var endIndex=startIndex+legendPerPage;
var seriesSubset=[],colorSubset=[];
for(var i=0;i<data.series.length;i++){
if(i>=startIndex && i<endIndex){
seriesSubset.push(data.series[i]);
colorSubset.push(colors[i]);
}
}
DrawLegendSubset(seriesSubset,colorSubset,legendPerPage,pageNo,totalPages);
}
function DrawLegendSubset(seriesSubset,colorSubset,legendPerPage,pageNo,totalPages){
var legend = svg.selectAll("g.legendg")
.data(seriesSubset)
.enter().append("g")
.attr('class','legendg')
.attr("transform", function (d, i) { return "translate(" + (width-40) + ","+ i*(legendWidth+legendSpacing) +")"; });
legend.append("rect")
.attr("x", 45)
.attr("width", legendWidth)
.attr("height", legendWidth)
.attr("class", "legend")
.style('fill',function(d,i){return colorSubset[i];});
legend.append("text")
.attr("x", 60)
.attr("y", 6)
.attr("dy", ".35em")
.style("text-anchor", "start")
.text(function (d) { return d.name; });
var pageText = svg.append("g")
.attr('class','pageNo')
.attr("transform", "translate(" + (width+7.5) + ","+ (legendPerPage+1)*(legendWidth+legendSpacing) +")");
pageText.append('text').text(pageNo+'/'+totalPages)
.attr('dx','.25em');
var prevtriangle = svg.append("g")
.attr('class','prev')
.attr("transform", "translate(" + (width+5) + ","+ (legendPerPage+1.5)*(legendWidth+legendSpacing) +")")
.on('click',prevLegend)
.style('cursor','pointer');
var nexttriangle = svg.append("g")
.attr('class','next')
.attr("transform", "translate(" + (width+20) + ","+ (legendPerPage+1.5)*(legendWidth+legendSpacing) +")")
.on('click',nextLegend)
.style('cursor','pointer');
nexttriangle.append('polygon')
.style('stroke','#000')
.style('fill','#000')
.attr('points','0,0, 10,0, 5,5');
prevtriangle.append('polygon')
.style('stroke','#000')
.style('fill','#000')
.attr('points','0,5, 10,5, 5,0');
if(pageNo==totalPages){
nexttriangle.style('opacity','0.5')
nexttriangle.on('click','')
.style('cursor','');
}
else if(pageNo==1){
prevtriangle.style('opacity','0.5')
prevtriangle.on('click','')
.style('cursor','');
}
}
function prevLegend(){
pageNo--;
svg.selectAll("g.legendg").remove();
svg.select('.pageNo').remove();
svg.select('.prev').remove();
svg.select('.next').remove();
var startIndex=(pageNo-1)*legendPerPage;
var endIndex=startIndex+legendPerPage;
var seriesSubset=[],colorSubset=[];
for(var i=0;i<data.series.length;i++){
if(i>=startIndex && i<endIndex){
seriesSubset.push(data.series[i]);
colorSubset.push(colors[i]);
}
}
DrawLegendSubset(seriesSubset,colorSubset,legendPerPage,pageNo,totalPages);
}
function nextLegend(){
pageNo++;
svg.selectAll("g.legendg").remove();
svg.select('.pageNo').remove();
svg.select('.prev').remove();
svg.select('.next').remove();
var startIndex=(pageNo-1)*legendPerPage;
var endIndex=startIndex+legendPerPage;
var seriesSubset=[],colorSubset=[];
for(var i=0;i<data.series.length;i++){
if(i>=startIndex && i<endIndex){
seriesSubset.push(data.series[i]);
colorSubset.push(colors[i]);
}
}
DrawLegendSubset(seriesSubset,colorSubset,legendPerPage,pageNo,totalPages);
}

Related

(D3.js) x-axis disappears when I set the y-axis domain to have a minimum greater than 0

Explanation
I have a dot plot graph which is working pretty well. However, I've been tasked with changing the y-axis so that it starts at a number closer to the minimum displayed number. Previously it was starting at 0, which meant that viewing a set of massive numbers was pretty pointless as they all looked to be on the same level.
I'm no D3 expert but I've included what I believe to be the relevant parts of the code below. I'm more than happy to include different parts if necessary.
Any idea what may be causing this?
Code
self.xMap = function (d) {
return self.xScale(self.xValue(d));
}
self.xValue = function (d) {
return d.x;
}
self.xScale = d3.scale.linear().range([0, width]);
self.xAxis = d3.svg.axis().scale(self.xScale).orient("bottom");
self.yMap = function (d) {
return self.yScale(self.yValue(d));
}
self.yValue = function (d) {
return d.y;
}
self.yScale = d3.scale.linear().range([height, 0])
self.yAxis = d3.svg.axis().scale(self.yScale).orient("left");
var svg = d3.select("div.canvas_wrapper").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 xExtent = d3.extent(flattenedData, function (d) { return +d.x; }),
xRange = xExtent[1] - xExtent[0];
self.xScale.domain(xValues);
var min = d3.min(flattenedData, function (ld) { return +ld.y });
/* Code that I have taken out
if (min > 0) {
min = 0;
}*/
var yMax = d3.max(flattenedData, function (d) { return +d.y; })
var yRange = yMax - min;
self.yScale.domain([min - (yRange * .05), yMax + (yRange * .05)]);
Additional Code
I think this could be quite useful, too. It comes after the previous code.
var xAxisGroup = svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.attr("transform", "translate(0," + (self.yScale(0)) + ")")
.call(self.xAxis)
.selectAll("text")
.attr("y", 10)
.attr("x", -10)
.attr("dy", ".35em")
.attr("transform", "rotate(315)")
.style("text-anchor", "end");
I found the problem, it was this line of code.
.attr("transform", "translate(0," + (self.yScale(0)) + ")")
It was sending the x-axis down out of visible sight.
Using just the code below worked nicely.
.attr("transform", "translate(0," + height + ")")

How do append a <text> for every element of the data in a D3 svg

[Sorry the title was quite badly formulated. I would change it if I could.]
I'm searching for a way to append text elements from a array or arrays in the data.
EDIT: I can already do a 1 level enter .data(mydata).enter(). What I'm trying here is a second level of enter. Like if mydata was an object which contained an array mydata.sourceLinks.
cf. the coments in this small code snippet:
var c = svg.append("g")
.selectAll(".node")
.data(d.nodes)
.enter()
.append("g")
.attr("class", "node")
.attr("transform", function(i) {
return "translate(" + i.x + "," + i.y + ")"
})
c.append("text")
.attr("x", -200)
.attr("y", 30)
.attr("text-anchor", "start")
.attr("font-size","10px")
.text(function(d){
// d.sourceLinks is an array of elements
// console.log(d.sourceLinks[0].target.name);
// Here I would like to apped('text') for each of the elements in the array
// and write d.sourceLinks[i].target.name in this <text>
})
;
I tried a lot of different things with .data(d).enter() but it never worked and I got lot's of errors.
I also tried to insert html instead of text where I could insert linebreaks (that's ultimately what I'm trying to achieve).
I also tried
c.append("foreignobject")
.filter(function(i) { // left nodes
return i.x < width / 2;
})
.attr('class','sublabel')
.attr("x", -200)
.attr("y", 30)
.attr("width", 200)
.attr("height", 200)
.append("body")
.attr("xmlns","http://www.w3.org/1999/xhtml")
.append("div");
but this never showed up anywhere in my page.
Your question was not exactly clear, until I see your comment. So, if you want to deal with data that is an array of arrays, you can have several "enter" selections in nested elements, since the child inherits the data from the parent.
Suppose that we have this array of arrays:
var data = [
["colours", "green", "blue"],
["shapes", "square", "triangle"],
["languages", "javascript", "c++"]
];
We will bind the data to groups, as you did. Then, for each group, we will bind the individual array to the text elements. That's the important thing in the data function:
.data(d => d)
That makes the child selection receiving an individual array of the parent selection.
Check the snippet:
var data = [
["colours", "green", "blue"],
["shapes", "square", "triangle"],
["languages", "javascript", "c++"]
];
var svg = d3.select("body")
.append("svg")
.attr("width", 500)
.attr("height", 200);
var groups = svg.selectAll("groups")
.data(data)
.enter()
.append("g")
.attr("transform", (d, i) => "translate(" + (50 + i * 100) + ",0)");
var texts = groups.selectAll("texts")
.data(d => d)
.enter()
.append("text")
.attr("y", (d, i) => 10 + i * 20)
.text(d => d);
<script src="https://d3js.org/d3.v4.min.js"></script>
Now, regarding your code. if d.nodes is an array of arrays, these are the changes:
var c = svg.append("g")
.selectAll(".node")
.data(d.nodes)
.enter()
.append("g")
.attr("class", "node")
.attr("transform", function(i) {
return "translate(" + i.x + "," + i.y + ")"
});//this selection remains the same
var myTexts = c.selectAll("myText")//a new selection using 'c'
.data(function(d){ return d;})//we bind each inner array
.enter()//we have a nested enter selection
.append("text")
.attr("x", -200)
.attr("y", 30)
.attr("text-anchor", "start")
.attr("font-size", "10px")
.text(function(d) {
return d;//change here according to your needs
});
You should use enter like this :
var data = ["aaa", "abc", "abd"];
var svg = d3.select("body").append("svg")
.attr("width", 200)
.attr("height", 200);
svg.selectAll("text")
.data(data)
.enter()
.append("text")
.attr("x", function(d,i) {
return 20 + 50 * i;
})
.attr("y", 100)
.text(function(d) { return d; });
See this fiddle : https://jsfiddle.net/t3eyqu7z/

Adding labels to both ends of <rect> in a bar chart

I'm using D3 to present some data as a horizontal bar chart. Values will typically range between -10 and +10 on 8 different scales. I have the bars rendering as I want, but I can't work out how to add lables for each of the extreems of the axes.
so far I have:
but I want to achieve something like:
In other words a label for each extreme of each scale.
I have found lots of examples that add data labels to the bars them selves (e.g. the value), but I want to some how force the array of strings to be rendered at the extremes of the container.
At the moment, I am rendering the data from an array, and I have the labels stored in 2 other arrays e.g.
var data = [10, 5, -5, -10, 2, -2, 8, -8];
var leftLabels = ["label 1","label 2", ...];
var rightLabels = ["label 1", "label 2", ...];
Any ideas or links to examples most welcome.
I am not an expert in d3.js, but I think this can be easily done. There are different ways to go about it. I have created a pen for your use case.
I will paste the important part of the code below. In your chart, you will have to certainly make some adjustments to suit your needs. Feel free to play around with the values until you feel they are stable.
// Your array containing labels for left and right values
var leftSideData = ["left1", "left2", "left3", "left4", "left5", "left6", "left7", "left8"];
var rightSideData = ["right1", "right2", "right3", "right4", "right5", "right6", "right7", "right8"];
var left = svg.selectAll(".leftData")
.data(leftSideData)
.enter().append("g")
.attr("class", "leftVal")
.attr("transform", function(d, i) {
return "translate(0," + i * 57 + ")";
});
left.append("text")
.attr("x", 0)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) {
return d;
});
var right = svg.selectAll(".rightData")
.data(rightSideData)
.enter().append("g")
.attr("class", "rightVal")
.attr("transform", function(d, i) {
return "translate(0," + i * 57 + ")";
});
right.append("text")
.attr("x", width + 30)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) {
return d;
});
I won't say this is perfect, but I hope you get an idea about how to approach it. All the best!!
It's funny, just by asking the q on SE I find it helps me reformulate the problem.. and then some time later a new try yields a result. Anyone else find that?
I managed to make it work by changing the way the SVG was created. So I now have the following structure:
<SVG>
><g> (one for each bar)
>><text>
>><rect>
>><text>
><other stuff like axies>
It turns out that <text> elements cannot be added to <rect> elements (well they can, be added but they won't render).
the code is:
var data = [10,2,4,-10,...etc...];
var leftLabels = ["left 1","left 1", ...etc...];
var rightLabels = ["right 1","right 2", ...etc...];
//chart dimentions
var margin = { top: 20, right: 30, bottom: 40, left: 30 },
width = 600 - margin.left - margin.right,
barHeight = 30,
height = barHeight * data.length;
//chart bar scaling
var x = d3.scale.linear()
.range([100, width-100]);
var y = d3.scale.ordinal()
.rangeRoundBands([0, height], 0.1);
var chart = d3.select(".chartsvg")
.attr("width", width + margin.left + margin.right)
.attr("height", barHeight * data.length + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
x.domain([d3.min(data), d3.max(data)]);
//append a g for each data item
var bar = chart.selectAll(".bar")
.data(data)
.enter()
.append("g");
//in each bar add a rect for the bar chart bar
bar.append("rect")
.attr("class", function (d) { return "bar--" + (d < 0 ? "negative" : "positive"); })
.attr("x", function (d) { return x(Math.min(0, d)); })
.attr("y", function (d, i) { return i* barHeight; })
.attr("width", function (d) { return Math.abs(x(d) - x(0)); })
.attr("height", barHeight-1);
//append the labels to each g using the label data
bar.append("text")
.data(rightLabels)
.attr("x", width)
.attr("y", function (d, i) { return (i * barHeight)+barHeight/2; })
.attr("dy", ".5em")
.attr("fill","steelblue")
.attr("text-anchor","end")
.text(function (d) { return d; });
bar.append("text")
.data(leftLabels)
.attr("x", 0)
.attr("y", function (d, i) { return (i * barHeight) + barHeight / 2; })
.attr("dy", ".5em")
.attr("fill","darkorange")
.attr("text-anchor", "start")
.text(function (d) { return d; });
//then append axis etc...
Formatting: something else to note. It turns out that to color the text in the label you need to use "stroke" and "fill" attributes. These are broadly equiv to the HTML "color" attribute on text.

D3.js How to identify a single bar uniquely within a group bar chart

I have created group bar chart by using D3.js. Each group has 2 bars. When any bar is clicked it must show some data using custom alert box. Now the bar can click and it shows data.
var state = svg.selectAll(".TestSuite")
.data(data)
.enter().append("g")
.attr("class", "TestSuite")
.on("click", function(d,i) {
if(i==0){
Alert.render(d3.select(this).data()[0].FalseStatements);
}else{
Alert.render(d3.select(this).data()[0].TrueStatements);
}
})
.attr("transform", function (d) {
return "translate(" + x0(d.TestSuite) + ",0)";
});
But data is vary according to clicked bars. So how to identify each single bar within a single group uniquely.
Here "if condition" that I used does not do the thing I want.How do I correct it?
Thank you.
(Suppose one group of bar consists two bars, one shows true count and other shows false count for a particular scenario. When we click the bar which shows true count then it should appear "TrueStatements" which is already have in data.using d3.select(this).data()[0].TrueStatements can do this. And also when someone click the bar which shows false count then it should appear "FalseStatements" which is already have in data.using d3.select(this).data()[0].FalseStatements can do this. My question is how do we identify the bar which shows true count and the bar which shows false count uniquely for do this task.)
EDITED:
How I get the data for bar chart(This is inside a for loop)
originalDataSetForBarChart.push({
TestSuite: "TS"+treeIndex,
Pass: trueAppear,
Fail: falseAppear,
FalseStatements : falseStatement,
TrueStatements : trueStatement
});
Bar chart code
var margin = {
top: 20,
right: 10,
bottom: 30,
left: 40
},
width = 890 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
var x0 = d3.scale.ordinal()
.rangeRoundBands([0, width], .5);
var x1 = d3.scale.ordinal();
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.ordinal()
.range(["#4169E1", "#800080"]);
var xAxis = d3.svg.axis()
.scale(x0)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(""));
var w = width + margin.left + margin.right;
var h = height + margin.top + margin.bottom;
var svg = d3.select(".chart1").append("svg")
.attr("width", w)
.attr("height", h)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
//svg.call(tip);
var xg = svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")");
var yg = svg.append("g")
.attr("class", "y axis");
yg.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Count");
I append bars to this chart inside a setInterval function using following method.
function update() {
startTime_barChart = new Date().getTime();
for (var i = 0; i < data.length; i++) {
var testSuite = d3.keys(data[i]).filter(function (key) {
return key !== "TestSuite";
});
}
data.forEach(function (d) {
d.trueFalseCount = testSuite.map(function (name) {
return {
name: name,
value: +d[name]
};
});
});
x0.domain(data.map(function (d) {
return d.TestSuite;
}));
x1.domain(testSuite).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data, function (d) {
return d3.max(d.trueFalseCount, function (d) {
return d.value;
});
})]);
//making the x axis/y axis
xg.call(xAxis);
yg.call(yAxis);
//removing all the rectangles
svg.selectAll(".TestSuite").remove();
var tip_word;
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function(d) {
tip_word= "<strong style='color:white'>"+
"Pass count :"+
"</strong>"+
" <span style='color:white'>" + d.True +
"</span></br>"+
"<strong style='color:white'>"+
"Fail count :"+
"</strong>"+
" <span style='color:white'>" + d.False +
"</span>";
return word;
});
var state = svg.selectAll(".TestSuite")
.data(data)
.enter().append("g")
.attr("class", "TestSuite")
.on("click", function(d,i) {
if(i%2 == 0){//How to set this condition
Alert.render(d3.select(this).data()[0].FalseStatements);
}else{
Alert.render(d3.select(this).data()[0].TrueStatements);
}
})
.attr("transform", function (d) {
return "translate(" + x0(d.TestSuite) + ",0)";
});
svg.call(tip);
state.selectAll("rect")
.data(function (d) {
return d.trueFalseCount;})
.enter().append("rect")
.attr("width", x1.rangeBand())
.attr("x", function (d) {
return x1(d.name);
})
.attr("y", function (d) {
return y(d.value);
})
.attr("height", function (d) {
return height - y(d.value);
})
.style("fill", function (d) {
return color(d.name);
});
if(barChartLegentController==1){
var legend = svg.selectAll(".legend")
.data(testSuite.slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
barChartLegentController=2;
}
endTime_barChart = new Date().getTime();
var totalbar = (endTime_barChart-startTime_barChart)/1000;
//alert('Total bar time : '+ totalbar+' seconds');
}
I'm not sure I fully understand what you are asking yet but the best way to identifying any element/entity is with an id, something like the following:
d3.select(this).attr(id, function(d, i) {return 'bar_' + i});
Add this inside the iterative function where you are creating your bars. In this way you will be able to select them from anywhere in your code with a d3.select('#bar_1).
If you only want to identify each bar it would be something like this:
var state = svg.selectAll(".TestSuite")
.data(data)
.enter().append("g")
.attr("id", function(d,i) {return 'bar_' + i})
.attr("class", "TestSuite")
.on("click", function(d,i) {
if(i==0){
Alert.render(d3.select(this).data()[0].FalseStatements);
}else{
Alert.render(d3.select(this).data()[0].TrueStatements);
}
})
.attr("transform", function (d) {
return "translate(" + x0(d.TestSuite) + ",0)";
});
In the case that you would like to identify each bar with an Id related to its contents (true or false statements) I would suggest something like the following:
var state = svg.selectAll(".TestSuite")
.data(data)
.enter().append("g")
.attr("class", "TestSuite")
.on("click", function(d,i) {
var barId;
if(i==0){
barId = 'falseBar_' + i;
Alert.render(d3.select(this).data()[0].FalseStatements);
}else{
barId = 'trueBar_' + i;
Alert.render(d3.select(this).data()[0].TrueStatements);
}
d3.select(this).attr('id', barId);
})
.attr("transform", function (d) {
return "translate(" + x0(d.TestSuite) + ",0)";
});
In any case, this will assign an unique Id to every bar (i.e. "bar_25" or "falseBar_14") to each bar, giving you an ideal way to identify each bar.
EDIT: After OP showed me the actual code they are working with, the following are my suggestions for a solution (which are actually on the same lines as the code above).
The code you should actually be tinkering with is the one below the code you posted. It is where the actual bars are rendered:
state.selectAll("rect")
.data(function(d) { return d.ages; })
.enter().append("rect")
.attr("width", x1.rangeBand())
.attr("x", function(d) { return x1(d.name); })
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); })
.style("fill", function(d) { return color(d.name); });
My suggestion to add an id attribute to each bar would be the following:
state.selectAll("rect")
.data(function(d) { return d.ages; })
.enter().append("rect")
.attr("width", x1.rangeBand())
.attr("id", function(d, i) {return 'bar_' + i}) // <-- Edited line
.attr("x", function(d) { return x1(d.name); })
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); })
.style("fill", function(d) { return color(d.name); });
It is important that you understand why this, and not the code block you provided initially, is the pertinent one. As you well said, the first block renders each group of bars (hence the append("g") which stands for svg group). The second block starts with a append("rect") which means svg rectangle. This and other lines (i.e. style("fill")..., attr("x")... and attr("y")...) clearly give away that this block is the one dealing with the actual bars and not the groups.

Tooltips for multiple line graphs in D3

I am new to D3 and my requirement is to get multiple line graphs and provide tooltips for them.
I could get the multiple line graphs to appear but i am going wrong in getting multiple tooltip points.
I am new to javascript as well. So any help will be much appreciated.
Here is my code.
<script>
function showData(obj, d) {
var coord = d3.mouse(obj);
var infobox = d3.select(".infobox");
// now we just position the infobox roughly where our mouse is
infobox.style("left", (coord[0] + 200) + "px" );
infobox.style("top", (coord[1] - 130) + "px");
$(".infobox").html(d);
$(".infobox").show();
}
function hideData() {
$(".infobox").hide();
}
var xx,yy;
function xx(e) {
return x(e.date); };
function yy(e) {
return y(e.returns); };
var draw = function() {
var margin = {top:100,left:200,right:200,bottom:100},
width = 1150 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var parseDate = d3.time.format("%Y-%m-%d").parse;
x = d3.time.scale().range([0,width]);
y = d3.scale.linear().range([height,0]);
//values of the axis is plotted here
var xAxis = d3.svg.axis().scale(x).orient("bottom");
var yAxis = d3.svg.axis().scale(y).orient("left");
var svg = d3.select("#chart").append("svg")
.attr("width" , width + margin.left + margin.right)
.attr("height" , height + margin.top + margin.bottom)
.attr("pointer-events" , "all")
.append("g")
//this is the line that positions the graph
.attr("transform" , "translate(" + margin.left + "," + margin.top +") ");
var activeReturns = new Array();
var passiveReturns = new Array();
var D3Obj = new Array();
var line = d3.svg.line()
.interpolate("basis")
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.returns);});
d3.json("d3.v3/sample1.json",function(error,result) {
result.data.forEach(function(d){
var arObj = new Object();
arObj.date = parseDate(d.date);
arObj.returns = +d.returns;
var prObj = new Object();
prObj.date = parseDate(d.date);
prObj.returns = +d.ticker_performance;
activeReturns.push(arObj);
passiveReturns.push(prObj);
});
D3Obj.push(activeReturns);
D3Obj.push(passiveReturns);
// this is where i tell that the line graph to be done
x.domain(d3.extent(D3Obj[0], function(d) {return d.date ;} ));
y.domain(d3.extent(D3Obj[0], function(d) {return d.returns ;} ));
svg.append("g")
.attr("class" , "x axis")
.call(xAxis)
.attr("transform","translate(0 ,"+ height + ")")
svg.append("g")
.attr("class" , "y axis")
//this is where yaxis line is added
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Price ($)");
svg.selectAll(".line")
.data(D3Obj)
.enter().append("path")
.attr("class","line")
.attr("d",line)
//this is where i am adding the tooltips
//tooltip for 1st line
svg.selectAll("circle")
.data(D3Obj[0])
.enter().append("circle")
.attr("fill", "red")
.attr("r", 2)
.attr("cx", xx)
.attr("cy", yy)
.on("mouseover", function(d) { showData(this, d.returns);})
.on("mouseout", function(){ hideData();});
//tooltip for 2nd line - this is where i think i am going wrong.
svg.selectAll("circle")
.data(D3Obj[1])
.enter().append("circle")
.attr("fill", "steelblue")
.attr("r", 2)
.attr("cx", xx)
.attr("cy", yy)
.on("mouseover", function(d) { showData(this, d.returns);})
.on("mouseout", function(){ hideData();});
});
$("#chart").append("<div class='infobox' style='display:none;'>Test</div>");
};
</script>
When you are creating the second point, nothing actually happens. The .data() function will try to match the data elements you pass to what you have selected (in this case one circle) and will succeed here. This means that your enter selection is empty and nothing happens.
The d3 way is to pass in all the data you want to use to create elements at once and handle accordingly in the functions to set attributes etc. That is, your code should look something like
svg.selectAll("circle")
.data(D3Obj)
.enter().append("circle")
.attr("fill", function(d, i) { if(i == 0) { return "red"; } else { return "steelblue"; } })
.attr("r", 2)
.attr("cx", xx)
.attr("cy", yy)
.on("mouseover", function(d) { showData(this, d.returns);})
.on("mouseout", function(){ hideData();});
This will create two circles and attach the corresponding listeners to them.

Categories

Resources