score:11

Accepted answer

The problem is that your Illustrator file already specifies fill colours on the individual <path> elements, and your id values are for parent <g> elements. Child elements inherit styles from parents, but only if the child doesn't have values of its own.

There are a couple things you could do to change it:

  1. Change the Illustrator file so that the paths have no fill. Then they will inherit a fill colour set on the parent.

  2. Select the paths directly, using d3.selectAll("g#id path") or d3.select("g#id").selectAll("path"); either version will select all <path> elements that are descendents of the <g> elment with id "id". Then you can set the fill attribute directly to over-write the value from Illustrator.


As discussed in the comments to the main question, if you want to take this a step further and actually join the data to the elements for future reference (e.g., in an event handler), the easiest way is to loop through your dataset, select each element, then use the .datum(newData) method to attach the data to each element:

dataset.forEach(function(d){ //d is of form [id,value]
    d3.select("g#"+d[0]) //select the group matching the id
      .datum(d) //attach this data for future reference
      .selectAll("path, polygon") //grab the shapes
      .datum(d) //attach the data directly to *each* shape for future reference
      .attr("fill", colour(d[1]) ); //colour based on the data
});

http://jsfiddle.net/ybAj5/6/

If you want to be able to select all the top-level <g> elements in the future, I would suggest also giving them a class, so you can select them with, for example, d3.select("g.region"). For example:

dataset.forEach(function(d){ //d is of form [id,value]
    d3.select("g#"+d[0]) //select the group matching the id
      .datum(d) //attach this data for future reference
      .classed("region", true) //add a class, without erasing any existing classes
      .selectAll("path, polygon") //grab the shapes
      .datum(d) //attach the data directly to *each* shape for future reference
      .attr("fill", colour(d[1]) ); //colour based on the data
});

d3.selectAll("g.region")
  .on("click", function(d,i) {
         infoBox.html("<strong>" + d[0] + ": </strong>" + d[1] ); 
          //print the associated data to the page
  });

Example implementation: http://jsfiddle.net/ybAj5/7/

Although using dataset.forEach doesn't seem to be using the full capability of d3, it is actually much simpler than trying to attach the whole dataset at once -- especially since there is such variability in the structure of the regions, some of which have nested <g> elements:

//Option two: select all elements at once and create a datajoin
d3.selectAll("g[id]") //select only g elements that have id values
    .datum(function(){
        var id=d3.select(this).attr("id"); 
        return [id, null]; })
        //create an initial [id, value] dataset based on the id attribute, 
        //with null value for now
    .data(dataset, function(d){return d[0];}) 
       //use the first entry in [id,value] as the key
       //to match the dataset with the placeholder data we just created for each
    .selectAll("path, polygon") //grab the shapes
    .datum(function(){
        return d3.select(this.parentNode).datum() ||
        d3.select(this.parentNode.parentNode).datum();
    }) //use the parent's data if it exists, else the grandparent's data
    .attr("fill", function(d){return d?colour(d[1]):"lightgray";});
         //set the colour based on the data, if there is a valid data element
         //else use gray.

This fiddle shows the above code in action, but again I would recommend using the forEach approach.


Related Query

More Query from same tag