d3 node sub structure/styling based on data attribute

orange :

I'd like to create multiple functions which "style" a rectangle differently based on the data (i.e. call different .attr() or .style() functions or even appending other elements based on the data).

I managed to use .call(myDecorator) to dispatch the styling to a separate function, but here I am a little struck. In rectDecorator, I wish to call different sub-selections based on d.type. At the moment (.style('fill'...).style(...).style(...)) shows just one of those possible ways to decorate the rectangle, but ideally, I'd like to "branch" to different chained commands based on d.type. Any idea how I could accomplish that?

    function rectDecorator(selection) {
      // call different selection.xyz functions to style rect differently based on `d.type`
      return selection
        .style('fill', function(d) {
          return d.color;
        })
        .style('fill-opacity', 1)
        .style('stroke-width', 0);
    }


    d3.select('svg')
      .selectAll('g')
      .data(data)
      .join(
        enter => enter
          .append('g')
          .append('rect')
            .attr('x', ...)
            .attr('y', ...)
            .attr('height', 20)
            .attr('width', 40)
            .call(rectDecorator)
        update => update(),
        exit => exit.remove()
altocumulus :

There are multiple possible solutions to this, although, at some point, you will have to iterate through the selection to check each element's individual type to find the specific decorator. You could set up a Map whereby mapping the types to their corresponding decorators.

const decorators = new Map([
  ["type1", selection => selection.attr("attr1", "...")],
  ["type2", selection => selection.attr("attr2", "...")]
]);

Afterwards, you can easily loop through the selection using .each() and get the suitable decorator from the map based on the datum's type property:

.each(function(d) {
  d3.select(this).call(decorators.get(d.type));
})

For a working demo have a look at the following snippet:

const decorators = new Map([
    ["red", selection => selection.attr("fill", "red")],
    ["green", selection => selection.attr("fill", "none").attr("stroke", "green")]
  ]);

const data = [{ type: "green" }, { type: "red" }];

d3.select('body').append("svg")
  .selectAll('g')
  .data(data)
  .join(
    enter => enter
    .append('g')
    .append('rect')
      .attr('x', (_, i) => i * 50 + 50)
      .attr('y', 50)
      .attr('height', 20)
      .attr('width', 40)
      .each(function(d) {
        d3.select(this).call(decorators.get(d.type));
      })
  );
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.15.0/d3.js"></script>

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=12252&siteId=1