Using d3 to set the background color of a table cell in html

Hello all,
I am trying to set a background of a table cell via d3.scales
like having
const scale = d3.scaleSequential(domain, d3.interpolateReds);
and

However, the colors come out completely wrong, like here I’d interpolate reds but I’d get only blues and greens.
I was searching online but I could not quite find any examples, every example is just about svg elements.

Many thanks in advance for any links and tips you can provide for me to find out how to do this.

1 Like

Hi Oliver, how’s this example?

Let me know if your example has other specifics and you need something different!

2 Likes

thanks a million toph!
I am so humbled that you went and made a notebook for that, super helpful!
I had tried something similar with d3.selectAll(cell class), may I ask why that wouldn’t work?

Also, just out of curiosity, can someone explain why using the output of the scale in <td bgcolor=scale(value)> gives wrong colors?

Hi Toph,

So I forked this, if I wanted to select just by HTML via class or id, why does the following not work:
Set background color of table cell with D3 / Oliver / Observable ?

thank you very much!

Yeah great question, and this gets at the heart of some different ways of working with D3, on and off Observable.

If you write an HTML table and then run a script that calls d3.selectAll(".you"), like in this CodePen example, it’ll work. But in your fork on Observable, it doesn’t.

When you call d3.selectAll(".you"), it’s basically calling document.querySelectorAll(".you"), which is plain JavaScript. (Here’s some of the D3 source.) That’ll only find an element if it’s already in the document.

In this Observable cell…

myTable = {
  const node = htl.html`<table class="table">
    <tr><td>Hello</td><td class="you">Oliver</td></tr>
    <tr><td>Love,</td><td class="me">Toph</td></tr>
  </table>`;
  d3.selectAll(".you").style("background-color", scale(1));
  return node;
}

…the first statement creates an HTML table and assigns it to const node. But it’s not actually attached to the document until it’s returned in the third statement. So d3.selectAll(".you") in the second statement won’t find the table. Instead, you can do d3.select(node).select(".you").

This was a good reminder I’d wanted to write up some of the general principles of using d3.select and d3.selectAll on Observable:

2 Likes

Thank you so much for explaining and the notebook, I am going to play around with it and try to understand selections more.

1 Like

@tophtucker - This is an great notebook - thanks! We’ve really needed a good pointer explaining why d3.select is problematic.

I do have one suggestion for improvement, though: While d3.select is indeed an anti-pattern on Observable, the reality is that this issue is really not Observable specific but much more general. The real issue is that rich, interactive web pages that communicate interesting ideas have generally grown in complexity. While the old demos on bl.ocks.org generate one interactive image at a time, that’s much less common now for real work. So, we’re really talking about good programming practice here.

I think this is an important point to make because many of the people who need to hear it are exactly those folks who seem to be put off by the fact that Observable is just not as simple as plain old vanilla JS/D3 in a little web page.

Yeah totally, I debated how much to get into that. I’ve tweaked the intro:

For years a statement like that was a natural way to start a D3 project. But today, it is an anti-pattern: it tends to select things it shouldn’t, and not select things it should. The web has moved away from global selectors and toward isolated components, with new abstractions for managing the DOM. This notebook focuses on Observable, but similar points apply to React and other frameworks.

You should start by calling d3.select on a DOM element, not a selector string, and all subsequent selections should be chained off of that parent selection.

2 Likes