Using D3 2.4.2, I create a number of path elements like so:
for (var i = 0; i < pathIndices.length; i++) {
graph.append("svg:path")
.style("stroke", colors[pathIndices[i]])
.style("stroke-width", "2.5px")
.style("fill", "none")
.attr("class", PATH_CLASS)
.attr("id", PATH_ID_PREFIX + pathIndices[i])
.attr("d", lineFunc(data))[0];
}
They all draw to the screen as expected. Later on, I want to bring one of them to the front when the user makes some input, so I have an event handler that does this:
var pathToHighlight = selectPath(pathIndex);
var paths = d3.selectAll("." + PATH_CLASS);
paths.sort(
function(a, b) {
if (a === pathToHighlight) {
return -1;
}
else if (b === pathToHighlight) {
return 1;
}
else {
return 0;
}
}
);
Setting breakpoints in Chrome indicates that my path selections here are successful (paths
is an array of SVGPathElements). But the code does nothing, and setting breakpoints inside the sort function shows that a
and b
are always undefined. Going up into the d3 code, I see that when the internal function d3_selection_sortComparator
calls my parator with the appropriate arguments, except they're ANDed with their own undefined __data__
members, which causes undefined
to be passed in:
// a and b are correct, but a.__data__ and b.__data__ are undefined
return parator(a && a.__data__, b && b.__data__);
What am I doing wrong here? My paths draw correctly to the screen, so it seems like they should have the correct data. Right?
Edit: Images:
Using D3 2.4.2, I create a number of path elements like so:
for (var i = 0; i < pathIndices.length; i++) {
graph.append("svg:path")
.style("stroke", colors[pathIndices[i]])
.style("stroke-width", "2.5px")
.style("fill", "none")
.attr("class", PATH_CLASS)
.attr("id", PATH_ID_PREFIX + pathIndices[i])
.attr("d", lineFunc(data))[0];
}
They all draw to the screen as expected. Later on, I want to bring one of them to the front when the user makes some input, so I have an event handler that does this:
var pathToHighlight = selectPath(pathIndex);
var paths = d3.selectAll("." + PATH_CLASS);
paths.sort(
function(a, b) {
if (a === pathToHighlight) {
return -1;
}
else if (b === pathToHighlight) {
return 1;
}
else {
return 0;
}
}
);
Setting breakpoints in Chrome indicates that my path selections here are successful (paths
is an array of SVGPathElements). But the code does nothing, and setting breakpoints inside the sort function shows that a
and b
are always undefined. Going up into the d3 code, I see that when the internal function d3_selection_sortComparator
calls my parator with the appropriate arguments, except they're ANDed with their own undefined __data__
members, which causes undefined
to be passed in:
// a and b are correct, but a.__data__ and b.__data__ are undefined
return parator(a && a.__data__, b && b.__data__);
What am I doing wrong here? My paths draw correctly to the screen, so it seems like they should have the correct data. Right?
Edit: Images:
Share edited Aug 2, 2012 at 18:11 ACK_stoverflow asked Aug 2, 2012 at 16:18 ACK_stoverflowACK_stoverflow 3,3054 gold badges25 silver badges36 bronze badges 4-
So,
paths
is a native Array and does not containundefined
items? – Bergi Commented Aug 2, 2012 at 16:25 - It looks like a native Array in the Chrome debugger. There are no undefined items, just the expected svg path elements. – ACK_stoverflow Commented Aug 2, 2012 at 18:03
-
1
Afaik many objects can look like arrays in the debugger. Check its prototype chain, and test
paths.sort === [].sort
– Bergi Commented Aug 2, 2012 at 18:07 -
console.log(typeof paths);
gives meobject
. To be fair, so doesconsole.log(typeof []);
. The D3 documentation says it expects a "selection" to be returned from its selectAll(). Yourpaths.sort === [].sort
gives me false. – ACK_stoverflow Commented Aug 2, 2012 at 18:19
3 Answers
Reset to default 5 +50You're using selection.sort
when it sounds like you're expecting Array.sort
. The selection.sort
method is used for sorting a selection based on the data bound to elements a
and b
. If your elements don't have data bound to them, then this won't work. If you want to sort a selection based on something else (like testing that one element reference is equal to another), you should grab the array of elements from the selection before calling .sort()
like so:
// `paths` is a Selection
var paths = d3.selectAll("." + PATH_CLASS);
// `pathElements` is an Array
var pathElements = paths[0];
pathElements.sort(function(a,b) {
// now `a` and `b` are Element references
// ...
})
Then you would have to take the extra step of matching the DOM order to the order of the resulting array.
That said, if you just want to bring pathToHighlight
to the front, a better way to approach this is just to re-append that single element to the DOM.
If pathToHighlight
is a selection you would do this:
var el = pathToHighlight.node();
el.parentNode.appendChild(el);
or, if it's already an Element reference, you can just do:
pathToHighlight.parentNode.appendChild(pathToHighlight);
As @jshanley mentioned, the elements in your selection does not have any data bound to them (__data__ attribute is undefined). The function passed to selection.sort will receive what data is bound to the element and in your case it is undefined.
You can bind the selected elements to data with the datum() function.
Try this:
paths.datum(function () { return this };).sort(...)
This will bind the element to itself and you will be able to reference it in the sort function.
Could you try .datum()
function instead of .__data__
?
https://github./mbostock/d3/wiki/Selections#wiki-datum
selection.datum([value])
Gets or sets the bound data for each selected element