Interested in corporate training?

Save Tens of Thousands of Dollars and Hundreds of Hours of Aggravation for Your Team...


Click here to learn more about D3.js Enterprise training...

Dynamic SVG Coordinate Space


The Goal

In this section, we will cover how to make the SVG Coordinate Space dynamic so that our Data Visualization is visible regardless of the data.

We will the make the SVG Coordinate Space scale up and/or down to fit our data.


Three SVG Rectangle Example

We start with three rectangles

(similar to our example in the Using JSON to Simplify Code section).

 1var jsonRectangles = [
 2  { "x_axis": 10, "y_axis": 10, "height": 20, "width":20, "color" : "green" },
 3  { "x_axis": 40, "y_axis": 40, "height": 20, "width":20, "color" : "purple" },
 4  { "x_axis": 70, "y_axis": 70, "height": 20, "width":20, "color" : "red" }];
 5
 6var svgContainer = d3.select("body").append("svg")
 7                                    .attr("width", 100)
 8                                    .attr("height", 100);
 9
10var rectangles = svgContainer.selectAll("rect")
11                             .data(jsonRectangles)
12                             .enter()
13                             .append("rect");
14
15var rectangleAttributes = rectangles
16                          .attr("x", function (d) { return d.x_axis; })
17                          .attr("y", function (d) { return d.y_axis; })
18                          .attr("height", function (d) { return d.height; })
19                          .attr("width", function (d) { return d.width; })
20                          .style("fill", function(d) { return d.color; });

Which gives us:

Three SVG Rectangles drawn with D3.js

Which is great.

The SVG Viewport (container)

1var svgContainer = d3.select("body").append("svg")
2                                    .attr("width", 100)
3                                    .attr("height", 100);

has a width of 100 units and a height of 100 units.

Which means that the lower right most point of the red rectangle lands at (90,90), which is still inside of our view port.

What if our purple rectangle x-coordinate, suddenly quadrupled from 40 to 160?

 1//Going from
 2{ "x_axis": 40, "y_axis": 40, "height": 20, "width":20, "color" : "purple" }
 3
 4//to
 5{ "x_axis": 160, "y_axis": 40, "height": 20, "width":20, "color" : "purple" }
 6
 7//so that our jsonRectangles becomes
 8var jsonRectangles = [
 9{ "x_axis": 10, "y_axis": 10, "height": 20, "width":20, "color" : "green" },
10{ "x_axis": 160, "y_axis": 40, "height": 20, "width":20, "color" : "purple" },
11{ "x_axis": 70, "y_axis": 70, "height": 20, "width":20, "color" : "red" }];

This would mean that the purple rectangle would have x,y coordinates of (160,40).

This coordinate is out of our viewport which has a width of 100 units and a height of 100 units.

Suddenly our Data Visualization would look like this:

Three SVG Rectangles drawn with D3.js, one disappeared

As you can imagine, This is not good!.


Manually Adjusting SVG Container Space

To get our new data to fit inside of our SVG container, we would have to increase the width of the container to accommodate the new x-coordinate for the purple triangle.

Since the new purple rectangle is defined as follows:

1{ "x_axis": 160, "y_axis": 40, "height": 20, "width":20, "color" : "purple" }

It will have coordinates (160,40) at the top left and (180,60) at the bottom right.

So we could just re-write the SVG Viewport (container) to have a width that was much bigger than 180, say 200...

1var svgContainer = d3.select("body").append("svg")
2                                    .attr("width", 200)
3                                    .attr("height", 100);

Suddenly our Data Visualization would look like this:

Three SVG Rectangles drawn with D3.js, bigger SVG viewport

Which is great, until the x-coordinate increases again. Or, the y-coordinate increases, or ... etc.


Dynamically Adjusting SVG Container Space

As you can guess, what we really want to do is to dynamically change the width and height attributes of the SVG Container/Viewport according to our data.

We are going to use some basic JavaScript to loop through our array of JSON objects to get the max x-coordinate and the max y-coordinate.

The max x-coordinate and max y-coordinate will be the bottom right hand point of the rectangle.

 1//New jsonRectangles Data (with purple rectangle x-coordinate now 160)
 2var jsonRectangles = [
 3  { "x_axis": 10, "y_axis": 10, "height": 20, "width":20, "color" : "green" },
 4  { "x_axis": 160, "y_axis": 40, "height": 20, "width":20, "color" : "purple" },
 5  { "x_axis": 70, "y_axis": 70, "height": 20, "width":20, "color" : "red" }];
 6
 7var max_x = 0; //This will be updated to be the max x-coordinate
 8var max_y = 0; //This will be updated to be the max y-coordinate
 9
10//We loop through our jsonRectangles array
11for (var i = 0; i < jsonRectangles.length; i++) {
12
13  var temp_x, temp_y;
14
15  // To get the farthest right hand point, we need to add the x-coordinate and the width
16  var temp_x = jsonRectangles[i].x_axis + jsonRectangles[i].width;
17
18  // To get the farthest bottom point, we need to add the y-coordinate and the height
19  var temp_y = jsonRectangles[i].y_axis + jsonRectangles[i].height;
20
21  /**
22  * If the temporary x-coordinate is bigger than the max_x,
23  * make the max_x equal to the temp_x
24  * otherwise, do nothing.
25  */
26  if ( temp_x >= max_x ) {
27    max_x = temp_x;
28  }
29
30  /**
31  * If the temporary y-coordinate is bigger than the max_y,
32  * make the max_y equal to the temp_y
33  * otherwise, do nothing.
34  */
35  if ( temp_y >= max_y ) {
36    max_y = temp_y;
37  }
38
39}//End of the loop
40
41max_x;
42//returns 180
43
44max_y;
45//returns 90

If you run this in the JavaScript Console, you will get that the max_x is 180 and the max_y is 90.

If the data changes, this max_x and max_y will always have maximum values of our data.

Now we have to update our SVG Container Viewport:

 1//From
 2var svgContainer = d3.select("body").append("svg")
 3                                    .attr("width", 200)
 4                                    .attr("height", 100);
 5
 6//to (using the new max_x and max_y variables)
 7var svgContainer = d3.select("body").append("svg")
 8                                    .attr("width", max_x + 20)
 9                                    .attr("height", max_y + 20);
10//Note - we add 20 units to the max_x and max_y to give the elements some stylistic room

Notice the note - we add some space to the max_x and max_y to give the elements some stylistic room.

In this way, our SVG Container will now always display the right dimensions so that the data fits correctly inside of it.


The Finished Product

Now that we've figured that out, the full code with the updated jsonRectangles data now reads (javascript comments removed):

 1var jsonRectangles = [
 2  { "x_axis": 10, "y_axis": 10, "height": 20, "width":20, "color" : "green" },
 3  { "x_axis": 160, "y_axis": 40, "height": 20, "width":20, "color" : "purple" },
 4  { "x_axis": 70, "y_axis": 70, "height": 20, "width":20, "color" : "red" }];
 5
 6var max_x = 0;
 7var max_y = 0;
 8
 9for (var i = 0; i < jsonRectangles.length; i++) {
10  var temp_x, temp_y;
11  var temp_x = jsonRectangles[i].x_axis + jsonRectangles[i].width;
12  var temp_y = jsonRectangles[i].y_axis + jsonRectangles[i].height;
13
14  if ( temp_x >= max_x ) { max_x = temp_x; }
15
16  if ( temp_y >= max_y ) { max_y = temp_y; }
17}
18
19var svgContainer = d3.select("body").append("svg")
20                                    .attr("width", max_x)
21                                    .attr("height", max_y)
22
23var rectangles = svgContainer.selectAll("rect")
24                             .data(jsonRectangles)
25                             .enter()
26                             .append("rect");
27
28var rectangleAttributes = rectangles
29                          .attr("x", function (d) { return d.x_axis; })
30                          .attr("y", function (d) { return d.y_axis; })
31                          .attr("height", function (d) { return d.height; })
32                          .attr("width", function (d) { return d.width; })
33                          .style("fill", function(d) { return d.color; });

Which gives us:

Three SVG Rectangles drawn with D3.js inside dynamic SVG viewport

There we go!

All of the rectangles are there.

The SVG Viewport has the right dimensions (max_x + 20, max_y + 20).

And the SVG Viewport was generated dynamically so we didn't have to manually update the width and height.

Using a JavaScript FOR loop we were able to dynamically resize our SVG Viewport Container to fit our data.

So if/when the data changes again, this viewport will be ready to contain all of the Data Visualization.

If you found this D3 Tutorial helpful, you'll enjoy these FREE videos: