Cubecubed 💖 d3.js
Tags: javascript, tutorial, d3, webdev
Published: 2022-07-21 14:33:45

[d3.js](https://d3js.org) is a JavaScript library used to manipulate the DOM through cake-simply syntax. You can think of it as the next-gen version of jQuery, but not quite. d3.js specifically focuses on data visualization. In addition, it helps you work with animations played by the tweening way (aka not WebAPI's `requestAnimationFrame`).

[Cubecubed](https://github.com/imaphatduc/cubecubed) intensively uses d3 in all SVG-related cubicon classes. In this article, I will show you the basic things you need to know about d3, and in the end explain how Cubecubed uses it to abstract the SVG rendering process in classes.

So, let's start with the basic d3 methods.

![d3 Basic Methods Illustration](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/al7awnlbcoynrh1568j5.jpg)

## `append()`, `select()`, `attr()` and `style()`

In `index.html`, we define these tags inside the `<body>` element:

```html
<h1>Hello</h1>
<h1>World</h1>
```

There is another way to achieve this entirely in JavaScript. We don't need to edit the HTML file then.

```js
d3.select("body")
  .append("h1")
  .text("Hello");

d3.select("body")
  .append("h1")
  .text("World");
```

When we call `select("body")` method, d3 will find the first body element and return a d3 selection object of it.

Next, we select the first `<h1>` like so:

```js
d3.select('h1');
```

Then, if we apply an attribute to the element, we can simply chain the code above with `attr()` method.

```js
d3.select('h1')
  .attr('class', 'hello');
```

The rendered HTML on the browser will be like so:

```html
<h1 class="hello">Hello</h1>
<h1>World</h1>
```

How can we apply CSS styles to the element? You guess it.

```js
d3.select('h1')
  .attr('class', 'hello')
  .style('color', 'red');
```

![Data Visualization](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/52xn1f9v3ytu82iy2i8h.jpg)

## The magic of data join

Consider this HTML structure:

```html
<h1>red</h1>
<h1>green</h1>
<h1>blue</h1>
```

How can we add the corresponding color to each of the h1 element? One way to do this is adding classes, `select()` the elements and apply colors to each of them. Something like this will work:

```html
<h1 class="red">red</h1>
<h1 class="green">green</h1>
<h1 class="blue">blue</h1>
```

```js
d3.select(".red")
  .style("color", "red");

d3.select(".green")
  .style("color", "green");

d3.select(".blue")
  .style("color", "blue");
```

What are the downsides of this approach? We have to add custom classes to the three h1 elements, and then violate the DRY principle by rewriting the classes when applying colors. Imagine the larger problem where we have hundreds of elements, I don't know about you, but to me I won't bother add hundreds of classes and then write hundreds of `select()` methods.

Maybe you have different approach from mine, but there is an awesome method built into d3 that perfectly works with this situation. That is the `data()` method.

First, let's define an array of color strings.

```js
const colors = ["red", "green", "blue"];
```

After that, we bibidi-babidi-booo with these lines of code.

```js
d3.selectAll("h1")
  .data(colors)
  .style((d, i) => d);
```

And boom, everything just works!

What is going on here? First, we select all h1 elements, then call the `data()` method on them. From this point, every next method in the current chain can take in an anonymous function with `d` and `i` as parameters. These two parameters are exactly like those are in JavaScript `map()` method: `d` is the current item, and `i` is its index in the array.

With just three lines of code, we don't need to add any classes or call any method a hundred times. The reasonable result is that the array (along with objects are the `data` in d3) needs to be updated to a hundred times then, which is no big deal if you have an active data store running.

This is the nature of the term "data visualization" of d3. With more and more data passed into the `data()` method, we can even create many SVG elements to easily visialize the data with graphs or charts.


![d3 Network Graph](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7w4xngtzjojxj7hxzswj.png)


![Smooth Transition Illustration](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jwb0nw1wirl88golabec.jpg)

## `transition()` kickstart

The next thing that makes d3 a beast is its `transition()` method. I used this in all types of SVG animation in Cubecubed.

No more talking, let's dive into the code right now.

First, we append an SVG [`<circle>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/circle) element into the body. The returned selection should be assigned to a variable.

```js
const circleSelection = d3.select("body")
  .append("circle")
  .attr("cx", 50)
  .attr("cy", 50)
  .attr("r", 50)
  .attr("fill", "none")
  .attr("stroke", "#ffc777");
```

To translate the rendered circle, we call the `transition()` on the selection variable.

```js
circleSelection
  .transition()
  .delay(1000)
  .duration(2000)
  .attr("transform", "translate(50, 50)");
```

In the code above, we set the delay time (the amount of time before the animation can be played - in milliseconds, so 1000ms = 1s), along with the duration of the animation. Specifically, the animation waits 1 second, then slowly tweens the SVG `transform` attributes until it reaches the value of `translate(50, 50)`. The tween takes 2 seconds to finish itself.

The reason why we tween the `translate` attribute but not the `cx` and `cy` is that it appears in all SVG elements, while the two latter ones are defined specifically for `<circle>`. In Cubecubed, cubicon types have different base elements, from `<circle>` to `<line>` and so on. By that point, I want to utilize the `Translate` animation class for all of them.

## Cubecubed's principle for creating new cubicon types

All cubicon types should be derived from `Cubicon` abstract class. There are two mandatory properties: `g_cubiconWrapper` and `def_cubiconBase` that you need to assign d3 selections to in the constructor. The former is an SVG `<g>` element that wraps around the latter, which is any SVG element that directly renders itself on the screen.

All `append()` method should be put inside the constructor. If you place it in the `render()` method, the HTML structure will be filled with the appended elements every time the users call the method, which results in a mess.

## Conclusion

That's all you need to know about d3 library and how Cubecubed renders its cubicons.