2

I need to change the order of all the shapes created by d3 in a svg container. But I'm unable to do it. The following script in JSFiddle, when executed locally, gives "a and b not defined". How should it be corrected?

var svg = d3.select("body")
.append("svg")
.attr("width", 100)
.attr("height", 100);

svg.append("rect")
.attr({x:0,y:0,height:10,width:10,fill:'yellow'})
.attr("sort",3);
svg.append("rect")
.attr({x:0,y:20,height:10,width:10,fill:'red'})
.attr("sort",2);
svg.append("rect")
.attr({x:0,y:40,height:10,width:10,fill:'blue'})
.attr("sort",1);

d3.selectAll("rect").sort(function(a,b){return a.attr("sort")-b.attr("sort")});

d3.selectAll("rect")
.each(function(d,i){alert(d3.select(this).attr("sort"))});
tic
  • 4,009
  • 15
  • 45
  • 86
  • Better to try to sort the dataset, and then call selection.sort() to reorder the elements. – Seemant Kulleen Apr 07 '16 at 23:27
  • 2
    `a` and `b` inside `sort` are not references to selections (and hence they don't have `.attr()` method), they're references to data that's bound to those selections/elements. In your case, both `a` and `b` are `null` because you didn't bind to data. Binding to data involves calling d3's `data()` method, and working with `enter()` to `append()` them. Having said all that, sorting only changes their order in the DOM, not on screen (that's what x and y are for). Is that your intention? – meetamit Apr 07 '16 at 23:44
  • Yes, my intention is to change the DOM order, to alter their back-front priority (z-order: https://stackoverflow.com/questions/13595175/updating-svg-element-z-index-with-d3). – tic Apr 08 '16 at 18:49

1 Answers1

4

You need to sort like this:

var rects = d3.selectAll("rect")[0].sort(function(a, b) {
  return d3.select(a).attr("sort") - d3.select(b).attr("sort")
}); //here rects will store the correct sorted order rectangle DOMs.

instead of doing like this:

d3.selectAll("rect").sort(function(a,b){return a.attr("sort")-b.attr("sort")});

Now to change the rectangle as per the sort do like this:

rects.forEach(function(rect, i){
    d3.select(rect).attr("y", (i*20));//change the attribute of y
})

working code here

NOTE: I am just answering this as per the info you have provided but the correct approach would be to do it the data way suggested by @meetamit in the comment.

Cyril Cherian
  • 32,177
  • 7
  • 46
  • 55
  • My example was a toy example. My aim is to alter the back-front (z-order) priority of shape elements, and I suppose that the simplest way to do this is sort the shape selection. I've completed your code to reach my goal. I suppose it is the right thing to do. The initial order is blue-red-yellow; the final one is yellow-red-blue: https://jsfiddle.net/b09zbcu8/1/ – tic Apr 08 '16 at 18:56
  • Just another question: what does [0] mean in d3.selectAll("rect")[0]? – tic Apr 08 '16 at 19:04
  • `d3.selectAll("rect")` gives an array of array element selections. so in order to get all the elements in the selection we do ` d3.selectAll("rect")[0]` here 0 is the first element. – Cyril Cherian Apr 09 '16 at 00:59
  • I thought the sort method applies to an array. On the countrary here it refers to the first element of an array... I wonder why it is so... – tic Apr 09 '16 at 05:06
  • 1
    no it does not refer to the first element of the array but it refers to an array of element selection. – Cyril Cherian Apr 09 '16 at 10:32