How to convert Tidy Tree to straight Javascript plus d3

I’m interested in being able to leverage the code from Tidy Tree (TidyTree data viz) to produce a data viz to run in my Chrome browser.

I’m stuck though with code sections such as the following, which produce many Javascript errors:

chart = {
const root = tree(data);

let x0 = Infinity;
let x1 = -x0;
root.each(d => {
if (d.x > x1) x1 = d.x;
if (d.x < x0) x0 = d.x;
});

tree = data => {
const root = d3.hierarchy(data);
root.dx = 10;
root.dy = width / (root.height + 1);
return d3.tree().nodeSize([root.dx, root.dy])(root);
}

Of course, I understand that the order of things does not matter in Observable, but I do need to get the order right for a conversion to Javascript.

I think if I could get some help on this one, I’d be able to understand the process of conversion. Can you please provide some help? I’m new to Javascript and d3.

You can’t directly use Observable notebook code as JS, since …

So you’ll have to do some conversion. A while back, I linked to some (hopefully helpful) resources for Observable to JS porting in this post:

See particularly the two forum posts of mine I also linked there:

2 Likes

Ok - I have modified the downloaded source for Tidy Tree and placed that source in my own html file. I’m successfully loading the data from the flare-2.json data file. I get no errors, and I’ve verified proper loading of the data from flare-2.json.

However - I cannot get the tree to render in the browser (I’m using chrome). There must be some really simple step I’m missing. Here’s the modified source that’s no longer dependent on Observable:

<head>

    <script src="https://d3js.org/d3.v6.min.js"></script>

    <link rel="stylesheet" type="text/css" href="./inspector.css">

    <style>

          

        </style>

      

</head>

<body>

    <script>

var data = d3.json(“http://localhost:8000/flare-2.json”);

var width = 954;

var tree = data => {

const root = d3.hierarchy(data);

root.dx = 10;

root.dy = width / (root.height + 1);

return d3.tree().nodeSize([root.dx, root.dy])(root);

};

chart(tree,data,width);

function chart(tree,data,width)

{

const root = tree(data);

//console.log(root);

let x0 = Infinity;

let x1 = -x0;

root.each(d => {

if (d.x > x1) x1 = d.x;

if (d.x < x0) x0 = d.x;

});

const svg = d3.create(“svg”)

  .attr("viewBox", [0, 0, width, x1 - x0 + root.dx * 2]);

const g = svg.append(“g”)

  .attr("font-family", "sans-serif")

  .attr("font-size", 10)

  .attr("transform", `translate(${root.dy / 3},${root.dx - x0})`);

const link = g.append(“g”)

.attr("fill", "none")

.attr("stroke", "#555")

.attr("stroke-opacity", 0.4)

.attr("stroke-width", 1.5)

.selectAll(“path”)

.data(root.links())

.join("path")

  .attr("d", d3.linkHorizontal()

      .x(d => d.y)

      .y(d => d.x));

const node = g.append(“g”)

  .attr("stroke-linejoin", "round")

  .attr("stroke-width", 3)

.selectAll("g")

.data(root.descendants())

.join("g")

  .attr("transform", d => `translate(${d.y},${d.x})`);

node.append(“circle”)

  .attr("fill", d => d.children ? "#555" : "#999")

  .attr("r", 2.5);

node.append(“text”)

  .attr("dy", "0.31em")

  .attr("x", d => d.children ? -6 : 6)

  .attr("text-anchor", d => d.children ? "end" : "start")

  .text(d => d.data.name)

.clone(true).lower()

  .attr("stroke", "white");

return svg.node();

};

</body>

Well, it looks like one problem is that “tree” appears to be defined in the unconverted code as a function, whereas in my converted code I’m trying to define it as a var. Attempts so far to get this definition of the function tree() have so far failed, though. I keep getting an error - “root.each is not a function” when the code tries to do this part of the converted code:

root.each(d => {

if (d.x > x1) x1 = d.x;

if (d.x < x0) x0 = d.x;

});

My latest attempt at defining the tree function looks like this, and I have it defined before it is used in the function chart(tree,data,width)
{

}

function tree(width){return(

data => {

const root = d3.hierarchy(data);

root.dx = 10;

root.dy = width / (root.height + 1);

return d3.tree().nodeSize([root.dx, root.dy])(root);

}

)};

So after doing more research I see that the code is intentionally creating the svg and its child groups and populating them with the tree data from the flare-2.json file entirely as a hierarchy of elements detached from the DOM. I understand that’s more efficient - but then I don’t see any code to attach this hierarchy of elements to the DOM so that it can then be rendered in the browser.

Is that somehow handled by the Observable runtime? If that is so, then I just need to understand how to do the attach to the DOM, or else create attached elements instead of detached ones in the first place. Can someone please comment?

It’s hard to debug without looking at the notebook, is it possible to share the notebook here?

Here’s a JSFiddle where I modified the code in your second post and got it to work:

https://jsfiddle.net/1r5b29qm/2/

The two main issues were:

  1. d3.json returns a Promise, so I had to wrap a bunch of the code in a .then().

  2. As you noticed in your fourth post in this thread, the svg element was not attached to the DOM. I added a line using d3.selection.append() to attach it to the HTML body element.

Note that the quotes in the code in your post got screwed up and it was a pain to fix them when I was copying and pasting. On this forum, you can put your code inside fenced code blocks like this, and it will preserve the formatting (and even add syntax highlighting):

```html
<!--- your HTML code goes here --->
```

```js
// Or, you can put JS code in blocks like this
```
1 Like

I’m studying your working version - learning a great deal! As a newcomer, I obviously over-complicated things.

Many, many thanks, bgchen!

1 Like

I modified the JS source a bit to make it more readable and to add some additional functionality - I wanted each node to have an ID, and I wanted to display their IDs next to the node name in the visualization. This mimics what I need to do in my work task to produce a visualization of graphics objects in their native relationship hierarchy as they exist inside a special graphics design document which is used to automatically generate a C-code source pgm to display the actual graphical design on embedded hardware. Each graphic object has its own unique object ID in the hierarchy, so I modified the sample json file (flare-2.json) to mimic that, and saw that the finished viz now includes the object IDs as I desired.

This is turning out to be a lot of fun!!!

Here’s a link to the latest assets to this post so you can try them out:
https://app.box.com/s/yfa12o8txk3bvf1z4ut85nuehxvspm10

Again, thanks so much!!!

1 Like