I'm drawing a line graph out of little circle bullets. However, the data has holes in it, which are represented by null's in my array. Naturally, wherever there's no data, there shouldn't be circles. But d3's append() method adds them anyway. How do I avoid this?
Here's a jsFiddle mockup reproducing my problem exactly.
I'm interested in NOT having that series of circles that lie on the X axis of my graph, since those are all nulls.
Relevant code from the jsfiddle link:
svg.selectAll('circle').data(values).enter()
.append('circle')// <-- I don't want to do this for null's
.attr('fill', '#c00')
.attr('r', 3)
.attr('cx', xi)
.attr('cy', yFlipped)
append() function is used to append a new element to the HTML tag name as given in the parameters to the end of the element. If the type that is given is a function then it must be evaluated for each element that is in the selection.
What is D3? D3 is a JavaScript library and framework for creating visualizations. D3 creates visualizations by binding the data and graphical elements to the Document Object Model. D3 associates (binding) the data (stuff you want to visualize) with the DOM.
One option is to represent your data differently, so that you aren't dependent on the index to compute the x-coordinate. For example, if you represented each datum as an object (e.g., {x: 0, y: 0.2840042}
) then you could compute the x-coordinate as x(d.x)
rather than x(i)
.
Another option would be to set the radius to zero when the value is null, so the circles are hidden: circle.attr("r", function(d) { return d == null ? 0 : 3; })
. Or, you could hide the circles: circle.style("display", function(d) { return d == null ? "none" : null; })
.
You could also remove the null elements after appending them: circle.filter(function(d) { return d == null; }).remove()
. That would work for the initial creation, but I wouldn't recommend it because the index would change if you reselected the elements later.
The simplest option is to filter the nulls out of the data being passed in to .data(…)
, augmenting the data to maintain the index:
svg.selectAll('circle')
.data(values
.map(function(v, idx) { return v == null? null : { idx: idx, value: v })
.filter(function(v) { return v != null })
)
.enter()
.append('circle')
.attr('fill', '#c00')
.attr('r', 3)
.attr('cx', function(d) { return d.idx * 10 }) // or whatever
.attr('cy', function(d) { return d.value.y }) // or whatever
Note that you can also follow this pattern individual sub-elements, even if they aren't naturally lists. For example, consider a situation where you want to conditionally add a second circle:
var circles = [
{ color: 'red', cx: 30, cy: 30, subCircleColor: 'blue' },
{ color: 'blue', cx: 60, cy: 60, subCircleColor: 'green' },
{ color: 'green', cx: 90, cy: 90 },
];
// Create a group which will hold the circles, since the result will
// be:
// <g class="circles">
// <circle color="{{ color }}" ... />
// <circle class="sub-circle" color="{{ subCircleColor }}" ... />
// </g>
var circlesGroups = svg.selectAll("g.circles")
.data(circles)
.enter()
.append("g").attr({"class": "circles"})
// Add the first circle to the group
circlesGroups
.append("circle").attr({
"fill": function(d) { return d.color },
"r": 20,
"cx": function(d) { return d.cx },
"cy": function(d) { return d.cy },
})
// If there is a subCircleColor, add the second circle to the group
circlesGroups.selectAll("circle.sub-circle")
.data(function(d) {
if (d.subCircleColor)
return [d];
return [];
})
.enter()
.append("circle").attr({
"class": "sub-circle",
"fill": function(d) { return d.subCircleColor; },
"r": 10,
"cx": function(d) { return d.cx },
"cy": function(d) { return d.cy },
})
Fiddle: http://jsfiddle.net/3d6e648k/
Try this pattern, which can either delete or hide your circles.
// Step 1: hides all circles which are "null"
d3.selectAll(".yourItem")
.data(data)
.enter()
.append("circle")
.attr("visibility", function(d,i){
if(yourFunction(d) == null) return "hidden";
})
// Step 2: optional, deletes all circles which are "hidden"
d3.selectAll("circle[visibility=hidden]").remove();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With