Creating and updating multiple graph using d3 and observablehq

Hi,
I’m trying to use d3js to build multiple graph where graphs are generated with observablehq.

      <select name="langpair" id="langpair">
        <option value="ende">English German</option>
        <option value="enes">English Spanish</option>
        <option value="jazh">Japanese Chinese</option>
      </select>
      <div id="confidence_interval"></div>
      const selection = document.getElementById("langpair");
      selection.onchange = async (e) => {
        const selected = e.currentTarget.value;
        await make_graphs(
          load(`/${selected}.json`),
          "#confidence_interval",
        );
      };

      // Initial chart
      selection.dispatchEvent(new Event("change"));
function make_graphs(data, selector) {
  // selector is a id of a div in my index.html
    d3.select(selector)
      .selectAll("figure")
      .data(data, d => d.metric)
      .enter()
      .append(pwps_chart_helper);
}

Here how I generate one graph but the details of what and how are not too important. I simply provide an example code that is close to what I actually do.

function pwps_chart_helper(datum) {
  const { metric: metric_name, values: data } = datum;

  const chart = Plot.plot({
    inset: 6,
    width: 512,
    height: 128,
    grid: false,
    marginBottom: 30,
    marginLeft: 50,
    x: { label: "score", tickRotate: 90 },
    y: { label: null },
    color: { scheme: "warm" },
    title: metric_name,
    marks: [
      Plot.dot(data, {
        x: "score",
        y: "type",
        stroke: "type",
        strokeWidth: 4,
        strokeLinecap: "round",
      }),
    ],
  });

  return chart;
}

This actually generates what I want and by that I mean, several graphs, one for each metric. My problem is when I try to update the graphs with new data, it adds new graphs instead of replacing the current graphs. I’m currently calling make_graphs(new_data, selector) when the data changes.

Question: What code changes do I need to make in order to be able to update the graphs data without appending more graphs? Note that it is fine if the graphs are updated/replaced with new graphs without any transition. It could/would be nice to have transitions between graphs but this is not required.

You’re not actually making use of Observable’s reactivity because you apply changes via side effects. Document selectors should practically never be used, and you’ll only need event listeners for advanced use cases.

What you’ll want is Observable’s viewof macro, so that the chart cell gets invalidated (i.e., cleared and executed again) when the input values change:

Thanks for your reply @mootari. I should’ve been clearer in that I’m not using a notebook. I want to generate graphs in a simple index.html.

Would it work to first dispose of your existing charts? Something like this:

function make_graphs(data, selector) {
  // selector is an id of a div in my index.html
  d3.select(selector)
    .selectAll("figure")
    .remove(); 

  d3.select(selector)
    .selectAll("figure")
    .data(data, d => d.metric)
    .enter()
    .append(pwps_chart_helper);
}

Thanks @aaronkyle I was doing something like docuemnt.getElementById(selector).contentText = "" but your solution is nicer.

1 Like