Using .select to section off your SVG

When you’re working in D3, a lot of stuff is going to be broken a lot of the time. Instead of sitting there moaning and randomly changing numbers and symbols in your code, you really need to use the Web Inspector and Console.

The Web Inspector lets you view the HTML that’s on the page. Big whoop! You wrote that HTML, right? No surprises there?

Bzzt - wrong! The D3 code you wrote actively changes the code on the page - adding elements, changing attributes, and generally mixing things around.

By using the web spector you can see what exactly your D3 code has been up to and where your mistakes might have come from (e.g. you’ve moved an element off of the page).

Finding the trouble

So you open up the Web Inspector and see some HTML like this:

<svg>
  <rect></rect>
  <rect></rect>
  <rect></rect>
</svg>

<svg>
  <circle></circle>
  <circle></circle>
  <circle></circle>
</svg>

Seems sensible enough, but there’s a big big big problem in its future. While your homework display fine and all, let’s think about what might happen.

You did something like the following to make your rectangles and circles:

var circles = d3.selectAll("circles");
circles.data(some_data);

var rectangles = d3.selectAll("rect");
rectangles.data(other_data);

And it worked fine, but… what if instead of a bar graph and some circles I asked you to make two bar graphs? Your HTML might look like this

<svg>
  <rect></rect>
  <rect></rect>
  <rect></rect>
</svg>

<svg>
  <rect></rect>
  <rect></rect>
  <rect></rect>
</svg>

And your JavaScript might look like this

var rectangles1 = d3.selectAll("rect");
rectangles1.data(some_data);

var rectangles2 = d3.selectAll("rect");
rectangles2.data(other_data);

Spend like four seconds trying to figure out what the problem is going to be with that JavaScript. What’s the difference between the first d3.selectAll and the second one?

…absolutely nothing! They both grab all the rectangles on the page. And that’s trouble.

You wanted the first selectAll to grab the ones inside of the first svg, and the second selectAll to grab the ones inside the second svg, but nope! Because d3 is willy-nilly grabbing all of the rect elements on the page, the first set of rectangles and the second set of rectangles are now all tangled together.

As a result, you won’t be able to manipulate the two charts separately, even though they’re in separate svg elements!

You’ll accidentally be binding data to both of your bar graphs at the same time, and everything will go crazy and break. Don’t worry too much, though, there are a few ways to deal with this.

Fixing the problem

There are two methods to solve the problem - one involves pre-writing a little bit of HTML, the other involves adding your svg element on the fly.

Method 1. Separate your svg elements with classes

How can d3 tell your svg elements apart? Same as CSS - you’ll need to use classes or ids and the appropriate selector.

<svg class='cat-graph'>
  <rect></rect>
  <rect></rect>
  <rect></rect>
</svg>

<svg class='dog-graph'>
  <rect></rect>
  <rect></rect>
  <rect></rect>
</svg>

D3 isn’t greedy - along with selectAll it also has a plain old select. Luckily for us D3 is cool and uses the exact same selectors as CSS - a period for a class.

var cat_svg = d3.select(".cat-graph");
var dog_svg = d3.select(".dog-graph");

You’ll use select to grab the specific svg you’re looking for, then selectAll to grab the elements inside of it.

var cat_rectangles = cat_svg.selectAll('rect');
cat_rectangles.data(some_data);

var dog_rectangles = dog_svg.selectAll('rect');
dog_rectangles.data(other_data);

And now your cat_rectangles and your dog_rectangles are separate and never the twain shall meet!

Method 2. Manually append your SVG inside of a specific div

A more advanced way of doing this involves manually appending the SVG. This method is actually a little more popular, but I think in class we’ll be using Method 1.

Step 1: In HTML you make the div (or divs) that is going to hold your graphic.

<div class="cat-holder"></div>
<div class="dog-holder"></div>

Step 2: Then you select that div, and manually append your svg to the HTML using .append.

var cat_svg = d3.select('.cat-holder').append('svg');
var dog_svg = d3.select('.dog-holder').append('svg');

Step 3: Then you bind your data and .enter().append('rect') to put a rectangles into the svg for every data point. I don’t think we’ve talked about enter and append yet, so this will make more sense later on!

var cat_rectangles = cat_svg.selectAll('rect')
                              .data(some_dat)
                              .enter()
                              .append('rect');
                              
var dog_rectangles = dog_svg.selectAll('rect')
                            .data(other_data)
                            .enter()
                            .append('rect');

You can do this even when you don’t have a million graphs on a page (it’s considered good practice). In practice, it might look like this:

HTML:

<div class="chart"></div>

JavaScript:

var datapoints = [1, 2, 3];

// Add an svg inside of .chart,
// set it to be sized 400x400
var svg = d3.select('.chart')
            .append('svg')
            .attr('height', 400)
            .attr('width', 400);                

// Space the circles out inside of the
// circle, 100 pixels apart
var circles = svg.selectAll('circle')
                  .data(datapoints)
                  .enter()
                  .append('circle')
                  .attr('r', 3)
                  .attr('cy' 40)
                  .attr('cx', function(d) { d * 100 });

Want to hear when I release new things?
My infrequent and sporadic newsletter can help with that.