Home > Back-end >  A very simple d3 data join adds the new elements in the wrong place
A very simple d3 data join adds the new elements in the wrong place

Time:09-30

<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.0.4/d3.min.js" integrity="sha512-T 1zstV6Llwh/zH uoc1rJ7Y8tf9N DiC0T3aL0 0blupn5NkBT52Avsa0l XBnftn/14EtxpsztAWsmiAaqfQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script>
    var data = {
        "Domains": [ "Domain 1", "Domain 2", "Domain 3" ]
    };
    d3.selectAll('#domains ul li')
        .data(data.Domains)
        .join("li")
        .text(function(d) {
            return d;
        });
</script>
</head>
<body>
<h2>Domains</h2>
<div id="domains"><ul></ul></div>
</body>

Here is my very simple code. I am expecting it to create <li> elements inside the <ul> element, one for each Domain. But it creates them between the head and the body! The resulting html (as gleaned from Chrome inspector) is:

<html><head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.0.4/d3.min.js" integrity="sha512-T 1zstV6Llwh/zH uoc1rJ7Y8tf9N DiC0T3aL0 0blupn5NkBT52Avsa0l XBnftn/14EtxpsztAWsmiAaqfQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script>
    var data = {
        "Domains": [ "Domain 1", "Domain 2", "Domain 3" ]
    };
    d3.selectAll('#domains ul li')
        .data(data.Domains)
        .join("li")
        .text(function(d) {
            return d;
        });
</script>
</head><li>Domain 1</li><li>Domain 2</li><li>Domain 3</li>
<body>
<h2>Domains</h2>
<div id="domains"><ul></ul></div>

</body></html>

Clearly I am doing something stupid - can anyone point out what it is?

I then modified my script as follows:

<script>
    var data = {
        "Domains": [ "Domain 1", "Domain 2", "Domain 3" ]
    };
    d3.select('#domains ul')
        .selectAll('li')
        .data(data.Domains)
        .join("li")
        .text(function(d) {
            return d;
        });
</script>

In this instance, no <li> items are added anywhere, and the text function is not even called.

Help?

CodePudding user response:

What you select with d3.selectAll has no bearing on where an element is inserted in the DOM. What you want to use is selection.selectAll

Joins and enters append elements to a parent: you haven't specified a parent, so the elements are just entered into the page as the parent is the document itself by default if using d3.selectAll.

Instead select your parent element, the ul and then using that selection, do the join: this means any entered elements will be appended to the parent element we selected, the ul:

d3.select("#domains ul") // return a selection of a parent element
  .selectAll("li")       // select children we have or want to have
  .data(data.Domains)
  .join("li")            // enter/update/exit children li elements
  ...

Often you'll see patterns where the selection of the parent is saved with a variable.

But we have one more problem, your code is in the document head, it runs before the rest of the document loads, so you won't be able to select the ul. If you place the script at the end of the body, you'll avoid that, or alternatively you can use the document.ready method. This is also why your li elements are added in between the head and the body: append adds things as the last child, but these elements can't be appended after the body as the body hasn't been loaded yet

Here's a working example:

  • Related