Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get the parent selection in d3?

Tags:

d3.js

To create this DOM:

<g>
    <rect></rect>
    <circle></circle>
</g>

from .enter() selection, I tried:

someUpdate.enter()
  .append('g')
    .attr('class', 'my-group')
    .append('rect')
      .attr('class', 'my-rect')
    // I'd like to get the .parent() here
    .append('cicle')
      .attr('class', 'my-circle')

This doesn't work since .append('rect') changes the selection to rect.

Breaking this to:

const update = someUpdate.enter()
  .append('g')
    .attr('class', 'my-group')

update
  .append('rect')
    .attr('class', 'my-rect')

update
  .append('cicle')
    .attr('class', 'my-circle')

works.

But, I wonder if there is a cleaner way?

like image 624
Misha Moroshko Avatar asked Jan 20 '17 07:01

Misha Moroshko


People also ask

How do I select parent nodes?

Approach: Write a recursive function that takes the current node and its parent as the arguments (root node is passed with -1 as its parent). If the current node is equal to the required node then print its parent and return else call the function recursively for its children and the current node as the parent.

What is or are the main selection in D3?

Selection methods come in two forms: select and selectAll: the former selects only the first matching element, while the latter selects all matching elements in document order. The top-level selection methods, d3.

How does D3 select work?

select() function in D3. js is used to select the first element that matches the specified selector string. If any element is not matched then it returns the empty selection. If multiple elements are matched with the selector then only the first matching element will be selected.


2 Answers

There are no methods in D3 for traversing the DOM like e.g. jQuery's .parent(). Hence, the way you broke this down into separate statements will be the correct approach.

On the other hand, it is not completely impossible to do it the way you first suggested. Just yesterday I posted an answer to "D3.js - what is selection.call() returning?" explaining how selection.call() will return exactly the selection it was called upon to allow for method chaining. Keeping that in mind you could something like the following:

d3.select("svg").selectAll("g")
  .data([1])
  .enter().append('g')
    .call((parent) => parent.append('rect')
                        .attr("fill", "red")
                        .attr("width", 100).attr("height", 100))
    .call((parent) => parent.append('circle')
                        .attr("fill", "blue").attr("r", 50));
<script src="https://d3js.org/d3.v4.js"></script>
<svg></svg>

Both functions invoked by .call() will be passed the same selection of previously entered <g> elements, which happens to be the parent element in this case.

Although it is possible to do it this way, the solution has its drawbacks. First, it will look somewhat strange and awkward to any seasoned D3 developer, which might complicate matters if you want to share or discuss your code with others. Second, even though I named the parameter parent, which it is in this particular case, it is still not really an equivalent to jQuery's .parent() method. It will just pass in and return the very same selection be it a parent selection or something else.

like image 108
altocumulus Avatar answered Oct 26 '22 08:10

altocumulus


Agree with the others that your second code snippet is the correct way to do what you want but I want to play to, how about:

d3.select("svg").selectAll("g")
    .data([1])
    .enter()
    .append('g')
    .each(function() {
        var p = d3.select(this);
        p.append('rect')
          .attr("fill", "red")
          .attr("width", 100).attr("height", 100);
        p.append('circle')
            .attr("fill", "blue").attr("r", 50);
    });
<script src="https://d3js.org/d3.v4.js"></script>
<svg></svg>
like image 38
Mark Avatar answered Oct 26 '22 08:10

Mark