score:4

Accepted answer

There are two issues related to this result

1) The gradient definition is missing the .enter() method

This is necessary to create a linearGradient element for each data point. Instead of:

var gradient = svg.append("defs")
  .data(data)
  .append("linearGradient")
    .attr("id", "gradient")
    .attr("x1", "0%")
    .attr("y1", "0%")
    .attr("x2", "100%")
    .attr("y2", "00%")
    .attr("spreadMethod", "pad");

You could use:

var gradient = svg.append("defs")
  .selectAll("linearGradient") // Creates the initial selection of linear gradients
  .data(data)
  .enter() // Binds new linearGradient elements for each data point
  .append("linearGradient")
    .attr("id", "gradient")
    .attr("x1", "0%")
    .attr("y1", "0%")
    .attr("x2", "100%")
    .attr("y2", "00%")
    .attr("spreadMethod", "pad");

Now, instead of having only one linearGradient element, you have one for each color. However, you will notice the problem persists, which leads to the second issue:

2) If all the linear gradients have the same ID, the code can not differentiate between colors.

Different linearGradient elements need different IDs in order to reference the data they represent. Continuing the previous example, instead of:

var gradient = svg.append("defs")
  .selectAll("linearGradient") // Creates the initial selection of linear gradients
  .data(data)
  .enter() // Binds new linearGradient elements for each data point
  .append("linearGradient")
    .attr("id", "gradient")
    .attr("x1", "0%")
    .attr("y1", "0%")
    .attr("x2", "100%")
    .attr("y2", "00%")
    .attr("spreadMethod", "pad");

You could use:

var gradient = svg.append("defs")
  .selectAll("linearGradient") // Creates the initial selection of linear gradients
  .data(data)
  .enter() // Binds new linearGradient elements for each data point
  .append("linearGradient")
    .attr("id", d => `gradient${d.name}`) // Create a unique data-driven id for each linearGradient
    .attr("x1", "0%")
    .attr("y1", "0%")
    .attr("x2", "100%")
    .attr("y2", "00%")
    .attr("spreadMethod", "pad");

And in the bars, the code can now reference the correct linearGradient according to the data:


bars.append("rect")
...
.style("fill", d => `url(#gradient${d.name})`); // picks the gradient that match the data


Related Query

More Query from same tag