最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - D3 sort function always passes undefined arguments - Stack Overflow

programmeradmin3浏览0评论

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 contain undefined 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 me object. To be fair, so does console.log(typeof []);. The D3 documentation says it expects a "selection" to be returned from its selectAll(). Your paths.sort === [].sort gives me false. – ACK_stoverflow Commented Aug 2, 2012 at 18:19
Add a ment  | 

3 Answers 3

Reset to default 5 +50

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);

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

发布评论

评论列表(0)

  1. 暂无评论