Get d3.svg.symbol() center - javascript

I'm using the function d3.svg.symbol() to plot symbols on a scatterplot. I'd like to have some tooltip show up on mouseover on the symbols. To place them accordingly, I need to get the center of the symbols, but don't really know how to do this. The code I use to generate the symbols is:
var symbols = svg.append("g")
.attr("id", "circles")
.selectAll("path")
.data(dataset)
.enter()
.append("path")
.attr("transform", function (d) { return "translate(" + x(d[SelX]) + "," + y(d[SelY]) + ")"; })
.attr("d", d3.svg.symbol()
.size(50)
.type(function (d) { if (d.Spaziatura == "Proportional") { return "circle";} else { return "diamond"; }; }))
.attr("fill", function (d) {
if (d.Grazie == "Sans") { return colore(parseFloat(d[SelCol])); }
else { return colore2(parseFloat(d[SelCol])); };
})
.attr("id", function (d) { return d.FamilyName;})
.attr("opacity", 1)
.attr("visibility", "visible")
Then the mouseover event:
.on("mouseover", function (d) {
//Get this symbbol's x/y values, then augment for the tooltip
var centroid = symbols.centroid(d);
var xPosition = centroid[0];
var yPosition = centroid[1];
//Update the tooltip position and value
svg .append("text")
.attr("class", "tooltip")
.attr("x", xPosition)
.attr("y", yPosition - (height/20))
//and then other stuff happens
I tried to reuse the centroid function I used for a map, but it doesn't work. I just need to get the center of the symbol's path to get this working, so any help on this is really appreciated, thanks!

You can get the center of the symbol like this (in the mouse handler):
var bibox = this.getBBox(),
t = d3.transform(d3.select(this).attr("transform")),
centroidX = t.translate[0] + (bibox.x + bibox.width)/2,
centroidY = t.translate[1] + (bibox.y + bibox.height)/2;
Demo here.

Related

d3 x.invert returning Invalid Date from d3.pointer. d3 v6

I probably should be sticking with d3 v4 or lower it seems. Anyway, I'm trying to create a typical Area Chart to display stock data. But I'm running into a problem with a mouseover function where I try to get the nearest data point based off mouse location. Similar to what happens when you hover on a graph like on google or yahoo finance. But the main issue is a problem with my X variable I guess.
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
var bisect = d3.bisector(function (d) {
return d.date;
}).left;
// Create the circle that travels along the curve of chart
var area = d3
.area()
.x(function (d) {
return x(d.date);
})
.y0(height)
.y1(function (d) {
return y(d.close);
});
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 focus = svg
.append("g")
.append("circle")
.style("fill", "none")
.attr("stroke", "black")
.attr("r", 8.5)
.style("opacity", 0);
function mouseover() {
focus.style("opacity", 1);
}
// format the data
var stockDateObj = {};
for (const [key, value] of Object.entries(data)) {
var stockDateObj = {};
stockDateObj.date = parseTime(key);
stockDateObj.close = Number(value["4. close"]);
setD3Data.push(stockDateObj);
}
setD3Data = setD3Data.sort(function (x, y) {
return d3.ascending(x.date, y.date);
});
this.setState({ currD3Data: setD3Data });
x.domain(
d3.extent(this.state.currD3Data, function (d) {
return d.date;
})
);
y.domain([
0,
d3.max(this.state.currD3Data, function (d) {
return d.close;
}),
]);
function mousemove(e) {
// recover coordinate we need
console.log(d3.pointer(e, svg.node()));
var x0 = x.invert(d3.pointer(e, svg.node()));
console.log(x0);
var i = bisect(this.state.currD3Data, x0, 1);
console.log(i);
var selectedData = this.state.currD3Data[i];
console.log(selectedData);
focus.attr("cx", x(selectedData.date)).attr("cy", y(selectedData.close));
}
function mouseout() {
focus.style("opacity", 0);
}
mouseover = mouseover.bind(this);
mousemove = mousemove.bind(this);
mouseout = mouseout.bind(this);
svg
.append("rect")
.style("fill", "none")
.style("pointer-events", "all")
.attr("width", width)
.attr("height", height)
.on("mouseover", mouseover)
.on("mousemove", mousemove)
.on("mouseout", mouseout);
The data above shows the creation of the x domain and the svg element that handles the mouseover functionality.
I don't really know what else to do considering I'm using d3 v6.
But this my error when i console.log(x0):
Invalid Date

Legend and axes not working properly in d3 multi-line chart

I adapted a multi-line chart which has a legend and axis and displays correctly on the bl.ocks.org site (http://bl.ocks.org/Matthew-Weber/5645518). The legend reorganizes itself when you select a different type from the drop down field. On my adaptation when the legend reorganizes itself the items start to overlap each other when some types are selected. Also the axes draw on top of each other. The original code uses tipsy but I have not checked it.
// original author's code http://bl.ocks.org/Matthew-Weber/5645518;
//set the margins
var margin = {
top: 50,
right: 160,
bottom: 80,
left: 50
},
width = 900 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
//set dek and head to be as wide as SVG
d3.select('#dek')
.style('width', width + 'px');
d3.select('#headline')
.style('width', width + 'px');
//write out your source text here
var sourcetext = "xxx";
// set the type of number here, n is a number with a comma, .2% will get you a percent, .2f will get you 2 decimal points
var NumbType = d3.format(",");
// color array
var bluescale4 = ["red", "blue", "green", "orange", "purple"];
//color function pulls from array of colors stored in color.js
var color = d3.scale.ordinal().range(bluescale4);
//defines a function to be used to append the title to the tooltip.
var maketip = function(d) {
var tip = '<p class="tip3">' + d.name + '<p class="tip1">' + NumbType(d.value) + '</p> <p class="tip3">' + formatDate(d.date) + '</p>';
return tip;
}
//define your year format here, first for the x scale, then if the date is displayed in tooltips
var parseDate = d3.time.format("%Y-%m-%d").parse;
var formatDate = d3.time.format("%b %d, '%y");
//create an SVG
var svg = d3.select("#graphic").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 + ")");
//make a rectangle so there is something to click on
svg.append("svg:rect")
.attr("width", width)
.attr("height", height)
.attr("class", "plot"); //#fff
// force data to update when menu is changed
var menu = d3.select("#menu select")
.on("change", change);
//suck in the data, store it in a value called formatted, run the redraw function
d3.csv("/sites/default/d3_files/d3-provinces/statistics-april-15-2.csv", function(data) {
formatted = data;
redraw();
});
d3.select(window)
.on("keydown", function() {
altKey = d3.event.altKey;
})
.on("keyup", function() {
altKey = false;
});
var altKey;
// set terms of transition that will take place
// when a new type (Death etc.)indicator is chosen
function change() {
d3.transition()
.duration(altKey ? 7500 : 1500)
.each(redraw);
} // end change
// REDRAW all the meat goes in the redraw function
function redraw() {
// create data nests based on type indicator (series)
var nested = d3.nest()
.key(function(d) {
return d.type;
})
.map(formatted)
// get value from menu selection
// the option values are set in HTML and correspond
//to the [type] value we used to nest the data
var series = menu.property("value");
// only retrieve data from the selected series, using the nest we just created
var data = nested[series];
// for object constancy we will need to set "keys", one for each type of data (column name) exclude all others.
color.domain(d3.keys(data[0]).filter(function(key) {
return (key !== "date" && key !== "type");
}));
var linedata = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {
name: name,
date: parseDate(d.date),
value: parseFloat(d[name], 10)
};
})
};
});
//make an empty variable to stash the last values into so we can sort the legend // do we need to sort it?
var lastvalues = [];
//setup the x and y scales
var x = d3.time.scale()
.domain([
d3.min(linedata, function(c) {
return d3.min(c.values, function(v) {
return v.date;
});
}),
d3.max(linedata, function(c) {
return d3.max(c.values, function(v) {
return v.date;
});
})
])
.range([0, width]);
var y = d3.scale.linear()
.domain([
d3.min(linedata, function(c) {
return d3.min(c.values, function(v) {
return v.value;
});
}),
d3.max(linedata, function(c) {
return d3.max(c.values, function(v) {
return v.value;
});
})
])
.range([height, 0]);
//will draw the line
var line = d3.svg.line()
.x(function(d) {
return x(d.date);
})
.y(function(d) {
return y(d.value);
});
//create and draw the x axis - need to clear the existing axis
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickPadding(8)
.ticks(10);
//create and draw the y axis
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickSize(0 - width)
.tickPadding(8);
svg.append("svg:g")
.attr("class", "x axis");
svg.append("svg:g")
.attr("class", "y axis")
.attr("transform", "translate(" + (0) + ",0)")
.call(yAxis);
//bind the data
var thegraph = svg.selectAll(".thegraph")
.data(linedata)
//append a g tag for each line and set of tooltip circles and give it a unique ID based on the column name of the data
var thegraphEnter = thegraph.enter().append("g")
.attr("class", "thegraph")
.attr('id', function(d) {
return d.name + "-line";
})
.style("stroke-width", 2.5)
.on("mouseover", function(d) {
d3.select(this) //on mouseover of each line, give it a nice thick stroke // works
.style("stroke-width", '6px');
var selectthegraphs = $('.thegraph').not(this); //select all the rest of the lines, except the one you are hovering on and drop their opacity
d3.selectAll(selectthegraphs)
.style("opacity", 0.2);
var getname = document.getElementById(d.name); //use get element cause the ID names have spaces in them - not working
var selectlegend = $('.legend').not(getname); //grab all the legend items that match the line you are on, except the one you are hovering on
d3.selectAll(selectlegend) // drop opacity on other legend names
.style("opacity", .2);
d3.select(getname)
.attr("class", "legend-select"); //change the class on the legend name that corresponds to hovered line to be bolder
}) // end of mouseover
.on("mouseout", function(d) { //undo everything on the mouseout
d3.select(this)
.style("stroke-width", '2.5px');
var selectthegraphs = $('.thegraph').not(this);
d3.selectAll(selectthegraphs)
.style("opacity", 1);
var getname = document.getElementById(d.name);
var getname2 = $('.legend[fakeclass="fakelegend"]')
var selectlegend = $('.legend').not(getname2).not(getname);
d3.selectAll(selectlegend)
.style("opacity", 1);
d3.select(getname)
.attr("class", "legend");
});
//actually append the line to the graph
thegraphEnter.append("path")
.attr("class", "line")
.style("stroke", function(d) {
return color(d.name);
})
.attr("d", function(d) {
return line(d.values[0]);
})
.transition()
.duration(2000)
.attrTween('d', function(d) {
var interpolate = d3.scale.quantile()
.domain([0, 1])
.range(d3.range(1, d.values.length + 1));
return function(t) {
return line(d.values.slice(0, interpolate(t)));
};
});
//then append some 'nearly' invisible circles at each data point
thegraph.selectAll("circle")
.data(function(d) {
return (d.values);
})
.enter()
.append("circle")
.attr("class", "tipcircle")
.attr("cx", function(d, i) {
return x(d.date)
})
.attr("cy", function(d, i) {
return y(d.value)
})
.attr("r", 3) // was 12
.style('opacity', .2)
.attr("title", maketip);
//append the legend
var legend = svg.selectAll('.legend')
.data(linedata);
var legendEnter = legend
.enter()
.append('g')
.attr('class', 'legend')
.attr('id', function(d) {
return d.name;
})
.on('click', function(d) { //onclick function to toggle off the lines
if ($(this).css("opacity") == 1) {
//uses the opacity of the item clicked on to determine whether to turn the line on or off
var elemented = document.getElementById(this.id + "-line"); //grab the line that has the same ID as this point along w/ "-line"
//use get element cause ID has spaces
d3.select(elemented)
.transition()
.duration(1000)
.style("opacity", 0)
.style("display", 'none');
d3.select(this)
.attr('fakeclass', 'fakelegend')
.transition()
.duration(1000)
.style("opacity", .2);
} else {
var elemented = document.getElementById(this.id + "-line");
d3.select(elemented)
.style("display", "block")
.transition()
.duration(1000)
.style("opacity", 1);
d3.select(this)
.attr('fakeclass', 'legend')
.transition()
.duration(1000)
.style("opacity", 1);
}
});
//create a scale to pass the legend items through // this is broken for some types
var legendscale = d3.scale.ordinal()
.domain(lastvalues)
.range([0, 30, 60, 90, 120, 150, 180, 210]);
//actually add the circles to the created legend container
legendEnter.append('circle')
.attr('cx', width + 20) // cx=width+50 made circle overlap text
.attr('cy', function(d) {
var newScale = (legendscale(d.values[d.values.length - 1].value) + 20);
return newScale;
})
.attr('r', 7)
.style('fill', function(d) {
return color(d.name);
});
//add the legend text
legendEnter.append('text')
.attr('x', width + 35) // is this an issue?
.attr('y', function(d) {
return legendscale(d.values[d.values.length - 1].value);
})
.text(function(d) {
return d.name;
});
// set variable for updating visualization
var thegraphUpdate = d3.transition(thegraph);
// change values of path and then the circles to those of the new series
thegraphUpdate.select("path")
.attr("d", function(d, i) {
lastvalues[i] = d.values[d.values.length - 1].value;
lastvalues.sort(function(a, b) {
return b - a
});
legendscale.domain(lastvalues);
return line(d.values);
// }
});
thegraphUpdate.selectAll("circle")
.attr("title", maketip) // displays HTML but not circle
.attr("cy", function(d, i) {
return y(d.value)
})
.attr("cx", function(d, i) {
return x(d.date)
});
// and now for legend items
var legendUpdate = d3.transition(legend);
legendUpdate.select("circle")
.attr('cy', function(d, i) {
return legendscale(d.values[d.values.length - 1].value);
});
legendUpdate.select("text")
.attr('y', function(d) {
return legendscale(d.values[d.values.length - 1].value);
});
d3.transition(svg).select(".x.axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
//make my tooltips work
$('circle').tipsy({
opacity: .9,
gravity: 'n',
html: true
});
//end of the redraw function
}
svg.append("svg:text")
.attr("text-anchor", "start")
.attr("x", 0 - margin.left)
.attr("y", height + margin.bottom - 10)
.text(sourcetext)
.attr("class", "source");
My adapted code (including a lot of console.log messages) is in jsfiddle https://jsfiddle.net/pwarwick43/13fpn567/2/
I am beginning to think the problem might be with the version of d3 or jquery. Anyone got suggestions about this?

D3.js: Dragging everything in group ('g') by element contained in the group using origin() function

I am not sure what's going on, but I have 2 very simple examples set up to show what I am asking.
Both examples have a 'g' that contains a 'rect' and 'text'.
In the 1st example, I am setting up drag on the 'g' itself, i.e., if you mousedown anywhere in that group and drag, it will drag the entire thing (both 'rect' and 'text') around the viewpoint.
http://jsfiddle.net/wup4d0nx/
var chart = d3.select("body").append("svg")
.attr("height", 500)
.attr("width", 500)
.style("background", "lightgrey");
var group = chart.selectAll("g")
.data(["Hello"])
.enter()
.append("g")
.attr("id", function (d) { return d;});
var rect = group.append("rect")
.attr("stroke", "red")
.attr("fill", "blue")
.attr("width", 200)
.attr("height", 200)
.attr("x", 10)
.attr("y", 10);
var label = group.append("text")
.attr("x", 40)
.attr("y", 40)
.attr("font-size", "22px")
.attr("text-anchor", "start")
.text(function (d) { return d;});
// Set up dragging for the entire group
var dragMove = function (d) {
var x = d3.event.x;
var y = d3.event.y;
d3.select(this).attr("transform", "translate(" + x + "," + y + ")");
};
var drag = d3.behavior.drag()
.origin(function (data) {
var element = d3.select("#" + data);
return {
x: d3.transform(element.attr("transform")).translate[0],
y: d3.transform(element.attr("transform")).translate[1]
};
})
.on("drag", dragMove);
group.call(drag);
In the 2nd example, which doesn't work and is what I am interested in, I want ONLY THE TEXT to be something the user can grab to drag the entire group around.
I tried many attempts. Some don't work at all, some work but flicker like the example I provide here:
http://jsfiddle.net/9xeo7ehf/
var chart = d3.select("body").append("svg")
.attr("height", 500)
.attr("width", 500)
.style("background", "lightgrey");
var group = chart.selectAll("g")
.data(["Hello"])
.enter()
.append("g")
.attr("id", function (d) { return d;});
var rect = group.append("rect")
.attr("stroke", "red")
.attr("fill", "blue")
.attr("width", 200)
.attr("height", 200)
.attr("x", 10)
.attr("y", 10);
var label = group.append("text")
.attr("x", 40)
.attr("y", 40)
.attr("font-size", "22px")
.attr("text-anchor", "start")
.text(function (d) { return d;});
// Set up dragging for the entire group USING THE LABEL ONLY TO DRAG
var dragMove = function (d) {
var x = d3.event.x;
var y = d3.event.y;
d3.select(this.parentNode).attr("transform", "translate(" + x + "," + y + ")");
};
var drag = d3.behavior.drag()
.origin(function (data) {
var element = d3.select("#" + data);
return {
x: d3.transform(element.attr("transform")).translate[0],
y: d3.transform(element.attr("transform")).translate[1]
};
})
.on("drag", dragMove);
label.call(drag);
What's going on with this that it flickers and what am I doing wrong?
Any help would be greatly appreciated!
Thanks!
I'm not sure exactly why it is flickering (as I am not too familiar with D3), but one way to get it to stop is to use the source event for D3:
// 50 is the offset x/y position you set for your text
var x = d3.event.sourceEvent.pageX - 50;
var y = d3.event.sourceEvent.pageY - 50;
Edit: While the above code works, it causes the box to initially "jump" to the coordinates of the text, A better fix would be to take your first example and just filter our events that aren't executed on the text element. Try putting the following at the top of the dragMove method:
if(d3.event.sourceEvent.target.nodeName !== 'text') {
return;
}
Try d3.event.sourceEvent.stopPropagation(); inside on-drag function

Multiple d3 charts in the same row

The code below draws 1 pie chart and a legend on the left side of the screen. Right now, I am trying to draw another pie chart with legend right next to the one on the left (same row). I've tried using multiple divs in the html to make this work, but I want a more pure d3 solution in which the duplication happens in the d3 code rather than in the html or css.
var w = 200;
var h = 200;
var r = h / 2;
var color = d3.scale.category20c();
var vis = d3.select(divId).append("svg:svg").data([descArray]).attr("width",w).attr("height", h).append("svg:g").attr("transform", "translate(" + r + "," + r + ")");
var pie = d3.layout.pie().value(function (d, i) {
return countArray[i];
});
// declare an arc generator function
var arc = d3.svg.arc().outerRadius(r);
// select paths, use arc generator to draw
var arcs = vis.selectAll("g.slice").data(pie).enter().append("svg:g").attr("class", "slice");
arcs.append("svg:path")
.on("click", function(d) {//clicking on individual arcs
arcs.selectAll("path").style("opacity", 1);//resets all arcs' opacity to 1
d3.select(this).style("opacity", 0.5);//sets clicked arc's opacity down
alert(d.data + " " + d.value);
})
.style("fill", function(d,i) { return color(i); })
.transition().delay(function(d, i) { return i * 100; }).duration(1000)
.attrTween('d', function(d) {
var i = d3.interpolate(d.startAngle+0.7, d.endAngle);
return function(t) {
d.endAngle = i(t);
return arc(d);
}
})
.attr("fill", function (d, i) {
return color(i);
});
var legend = d3.select(divId).append("svg")
.attr("class", "legend")
.attr("width", r * 4)
.attr("height", r * 4)
.selectAll("g")
.data(color.domain().slice().reverse())
.enter().append("g")
.attr("transform", function(d, i) { return "translate(230," + i * 27 + ")"; });
legend.append("rect")
.on("click", function(d) {
alert(d.data + " " + d.value);
})
.attr("width", 18)
.attr("height", 18)
.style("fill", function (d, i) {
return color(i);
})
put them in seperate divs but in the same SVG element
Presuming vis is your svgElement:
var firstChart = vis.append(div). // then put your first chart here
var secondChart = vis.append(div). // then put your second chart here

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