Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

d3.js: enter(), update, exit() with group element in between

My DOM structure looks like this, when I rendered my visualization with D3.js and enter, update, exit pattern:

g
  rect
  ...
g
  rect
  ...
g
  rect
  ...

I am working with multiple elements and nested selections in my groups, but for simplicity I will demonstrate this with the rects. The DOM gets done by:

group = d3.select('.svg-content')
    .selectAll('g')
    .data(items, function (item) { return item.Id; });

groupEnter = group.enter()
    .append('svg:g')
    .attr('class','group-content');

// enter
groupEnter.append('svg:rect')
    .style('fill', '#000')
    .attr('x', function (item) { return item.x })
    .attr('y', function (item) { return item.y; })
    .attr('width', function (item) { return item.width; })
    .attr('height', function (item) { return item.height; });

// update
group.select('rect') 
    .attr('x', function (item) { return item.x })
    .attr('width', function (item) { return item.width; });

// remove
group.exit().remove();

This works!

Now I want to achieve the following:

g
  g
    rect
  ...
g
  g
    rect
  ...
g
  g
    rect
  ...

I want to encapsule the rect in another group element.

group = d3.select('.svg-content')
    .selectAll('g')
    .data(items, function (item) { return item.Id; });

groupEnter = group.enter()
    .append('svg:g')
    .attr('class','group-content');

// enter
groupEnter
    .append('svg:g') // NEW
    .attr('class','rect-content') // NEW
    .append('svg:rect')
    .style('fill', '#000')
    .attr('x', function (item) { return item.x })
    .attr('y', function (item) { return item.y; })
    .attr('width', function (item) { return item.width; })
    .attr('height', function (item) { return item.height; });

// update
group
    .select('.rect-content') // NEW
    .select('rect') 
    .attr('x', function (item) { return item.x })
    .attr('width', function (item) { return item.width; });

// remove
group.exit().remove(); // NOTE: without this, it works!

What is wrong with this code? Without the remove block it works, but I need this to handle new/removed items. How to make it right?

like image 688
Robin Wieruch Avatar asked Dec 16 '14 08:12

Robin Wieruch


People also ask

What is enter () and exit () in D3 JS?

enter() function creates the initial join of the data with our DOM elements. Thus selecting only the elements that were not in the DOM yet. merge() function will select the DOM elements that did not exist in the DOM before and the ones that did. exit() function will select the DOM elements that are left from the join.

What is update and exit in D3 JS?

d3js - enter(), update() and exit() This is a simple example of enter/update/exit in d3js. 2 arrays - of differing length - are used to generate alternating sequences of bubbles. . enter() creates the initial join of data to elements, creating one circle element for every data element in the array.

What is enter in D3 JS?

enter() function in D3. js is used to create the missing elements and return the enter selection. Syntax: selection. enter();

What do the select () and selectAll () functions in D3 do?

d3. select selects the first matching element whilst d3. selectAll selects all matching elements. Both functions take a string as its only argument.


1 Answers

The problem is that you're selecting plain g elements to bind the data to (.selectAll('g').data(...)). This works fine when there's only one level of those elements, but as .selectAll() works recursively, it will select many more elements than you would think when you have the nested structure.

That is, the selection contains many more elements, which "consume" the bound data. So the data doesn't end up being matched to the correct elements.

To fix, simply make the selector more specific:

group = d3.select('.svg-content')
    .selectAll('g.group-content')
    .data(...);

Complete demo here.

like image 189
Lars Kotthoff Avatar answered Nov 14 '22 23:11

Lars Kotthoff