Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

D3 selection.join() using classes

Tags:

d3.js

I'm trying to use the new D3 selection.join() paradigm to draw a randomly positioned circle within every svg. While I can get this to work using ids in the selector, I haven't been successful using classes for the selector. Since I don't have data associated with each circle or svg, I'm just synthesizing an array whose length is the number of selected elements. I've commented out returning the enter and update selectors, since enabling them results in the exception:

TypeError: r.compareDocumentPosition is not a function
at Pt.order (https://cdnjs.cloudflare.com/ajax/libs/d3/5.15.1/d3.min.js:2:14333)
at Pt.join (https://cdnjs.cloudflare.com/ajax/libs/d3/5.15.1/d3.min.js:2:13931)
at addCircle (c:\github\xxxxxxx\xxxxx\wwwroot\testd3.html:48:19)
at HTMLButtonElement.onclick (c:\github\xxxxx\xxxxx\wwwroot\testd3.html:20:36) 

const randomColor = () => {
  return "hsl(" + Math.random() * 360 + ",100%,50%)";
}

const addDiv = () => {
  d3.select("div").append("svg")
    .attr("width", 100)
    .attr("height", 100)
    .style("background", randomColor())
    .classed("mysvg", true);
}

const addCircle = () => {
  var svgs = d3.selectAll(".mysvg");
  var nodes = svgs.nodes();
  console.log('nodes: ' + nodes.length);

  // add a random colored circle to each SVG.
  svgs.select("circle")
    // .data([1, 2], d=> d)
    .data(d3.range(0, nodes.length), d => d)
    .join(
      enter => {
        enter
          .append('circle')
          .attr("cx", d => 50 + Math.random() * 50 * d)
          .attr("cy", d => 50 + Math.random() * 50 * d)
          .attr("r", 10)
          .style("fill", randomColor());
        console.log('enter: ' + enter.nodes().length);
        // return enter;
      },
      update => {
        console.log('update: ' + update.nodes().length);
        // return update;
      },
      exit => {
        console.log('exit: ' + exit.nodes().length);
        exit.remove();
      }
    );
}
.mysvg {}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.15.1/d3.min.js"></script>

<div>
  <button onclick="addDiv()"> addDiv </button>
  <button onclick="addCircle()"> addCircle </button>
  <svg class="mysvg" style="background: lightblue" width=100 height=100>
  </svg>
</div>
like image 864
Jay Borseth Avatar asked Jun 18 '26 20:06

Jay Borseth


1 Answers

Following this comment from gitHub: "... you are returning the enter selection from the join’s enter method; you need to return a materialized selection instead ...", it can be concluded that the enter selection cannot be returned directly, so I found that an option is to use a temporary selection, originating from .append():

// it works
.join(
  enter => enter
    .append("path")
    .attr("class", "item")
    .attr("fill", "red")
    .attr("d", arc)
)

// it does not work
.join(
  enter => { 
    enter
      .append("path")
      .attr("class", "item")
      .attr("fill", "red")
      .attr("d", arc);

    return enter;
  }
)

// it works!
.join(
  enter => { 
    let sel = enter
      .append("path")
      .attr("class", "item")
      .attr("fill", "red")
      .attr("d", arc);

    return sel;
  }
)

So, with the last alternative, it is possible to do custom changes to selection, append new svg sub-items, etc.

like image 127
ragan Avatar answered Jun 23 '26 02:06

ragan