Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

D3 sort function always passes undefined arguments

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 comparator 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 comparator(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:

Contents of path

parameters a and b are undefined

like image 228
ACK_stoverflow Avatar asked Aug 02 '12 16:08

ACK_stoverflow


1 Answers

You'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);
like image 153
jshanley Avatar answered Sep 19 '22 17:09

jshanley