score:1

Accepted answer

Nodes don't have to be grouped together physically to be able to group them to apply a label or highlight them. At present, the code you are using produces a g element for each parent node, with a rect that provides the colour fill and a blank text element. The child nodes are then overlaid on this parent node with a black stroke along the edges and a text node with the country name.

If you want to be able to highlight all the elements that have the same parent and put a header on the element, you can do it a couple of ways:

  • alter existing elements to show a header

  • create an overlay with the same dimensions as the parent node to display a header

Altering existing elements

To be able to do this, we first need to be able to identify parent nodes and their children. One way to do this is by giving nodes classes according to their position in the hierarchy and their data content. Here's one possible way to do this:

var cells = innercanvas
.selectAll(".newcell")
.data(treemap)
.enter()
.append("g")
.attr("class", function (d,i) {
  return 'newcell _' + i       // i provides a unique identifier for each node 
  + ' cell-level-' + d.depth   // cell-level-0 for root, cell-level-1, cell-level-2, etc 
  + ( d.name ? ' ' + safe_name(d.name) : '' ) // if d.name exists, use the 'safe' version
  + ( ! d.children
    ? ' leaf'                  // d has no children => it's a leaf node
    : (d.depth === 0
      ? ' root'                // d.depth = 0 => it's the root node
      : ' internal '));        // has children, depth > 0 => internal node
})

// strips non-alphanumeric characters out of `name` strings, replaces with _
function safe_name (txt) {
  return txt.replace(/\W/g, '_');
}

The SVG g elements now look like this:

treemap SVG elements with extra class goodness

Now we can easily access the parent node for any country name c.name by using

d3.select('.internal.' + safe_name(c.name))

or (e.g.) the text elements of the leaf nodes for the country using

d3.selectAll('.leaf.' + safe_name(c.name) + ' text')

We can then use CSS to show or hide text elements, rect strokes, etc.; e.g.

var bool = false;
var toggle = d3.select('#toggle')
  .on('click', toggleSvg);
  
function toggleSvg() {
    bool = !bool;
    d3.selectAll('.Cyprus')
      .classed('highlightAll', bool);
}
.newcell text {
  font-family: Arial, sans-serif;
  font-size: 10px;
}

.newcell.leaf rect {
  stroke: #000;
  stroke-width: 0.5px;
}

.oversize {
  display: none;
}

.internal text {
  opacity: 0
}

.internal.highlightAll text {
  opacity: 1
}

.highlightAll.leaf rect {
  opacity: 0.1
}

.highlightAll.leaf text {
  opacity: 0
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<p><a id="toggle" href='#'>Toggle Cyprus highlighting</a></p>

<svg width="186" height="77">
      <g transform="translate(-30.1234, -258.33)">
        <g class="newcell _2331 cell-level-1 Cyprus internal ">
          <rect x="30.123480134121516" y="258.33086334171067" width="185.81893466750355" height="76.6094615363257" style="fill: rgb(100, 200, 75);"></rect>
          <title>Cyprus</title>
          <text x="123.0329474678733" y="296.63559410987354" dy=".35em" text-anchor="middle">Cyprus</text>
        </g>
        <g class="newcell _2332 cell-level-2 Cyprus leaf">
          <rect x="40.12348013412152" y="268.33086334171067" width="31.51365155392795" height="23.97574841366901" style="fill: none;"></rect>
          <title>Cyprus</title>
          <text x="55.88030591108549" y="280.3187375485452" dy=".35em" text-anchor="middle" class="oversize">Cyprus</text>
        </g>
        <g class="newcell _2333 cell-level-2 Cyprus leaf">
          <rect x="40.12348013412152" y="292.30661175537968" width="31.51365155392795" height="32.633713122656687" style="fill: none;"></rect>
          <title>Cyprus</title>
          <text x="55.88030591108549" y="308.62346831670802" dy=".35em" text-anchor="middle" class="oversize">Cyprus</text>
        </g>
        <g class="newcell _2334 cell-level-2 Cyprus leaf">
          <rect x="71.637131688049465" y="268.33086334171067" width="55.48181226963859" height="56.60946153632569" style="fill: none;"></rect>
          <title>Cyprus</title>
          <text x="99.37803782286876" y="296.63559410987354" dy=".35em" text-anchor="middle">Cyprus</text>
        </g>
        <g class="newcell _2335 cell-level-2 Cyprus leaf">
          <rect x="127.11894395768805" y="268.33086334171067" width="78.823470843937" height="56.60946153632569" style="fill: none;"></rect>
          <title>Cyprus</title>
          <text x="166.53067937965655" y="296.63559410987354" dy=".35em" text-anchor="middle">Cyprus</text>
        </g>
      </g>
    </svg>

Creating an overlay

This will duplicate an existing parent node on top of the treemap.

Add a new g element to the treemap with rect and text nodes as children:

var highlightG = canvas.append('g')
    .attr("transform", "translate(" + cfg.margin.left + "," + cfg.margin.top + ")")
    .append('g')
    .classed('highlighter', true)
    .attr('opacity',0);
highlightG.append('rect');
highlightG.append('text');

We can use the data from an existing parent node to set the appropriate position and size for the rect element and the content for the text element.

var d = d3.select('.internal.Cyprus').datum();
highlightG.select('rect')
  .attr("x", d.x)
  .attr("y", d.y)
  .attr("width", d.dx)
  .attr("height", d.dy)

highlightG
  .select("text")
  .attr("x", d.x + d.dx / 2 )
  .attr("y", d.y + d.dy / 2 )
  .attr('dy', '.35em')
  .attr("text-anchor", "middle")
  .text(function () {
    return d.name;
  })

d3.select('.internal.Cyprus').property('__data__', {
depth: 1,
dx: 185.81893466750355,
dy: 76.6094615363257,
name: "Cyprus",
value: 446770,
x: 30.123480134121516,
y: 258.33086334171067
});
var opacity = 1;
var highlightG = d3.select('svg')
  .append('g')
  .attr("transform", "translate(-30.1234, -258.33)")
  .append('g')
  .classed('highlighter', true)
  .attr('opacity', 0);
highlightG.append('rect');
highlightG.append('text');

d3.select('#toggle')
  .on('click', function() {
    var d = d3.select('.internal.Cyprus').datum();
    highlightG.attr('opacity', opacity);
    highlightG.select('rect')
      .attr("x", d.x)
      .attr("y", d.y)
      .attr("width", d.dx)
      .attr("height", d.dy)

    highlightG
      .select("text")
      .attr("x", d.x + d.dx / 2)
      .attr("y", d.y + d.dy / 2)
      .attr('dy', '.35em')
      .attr("text-anchor", "middle")
      .text(function() {
        return d.name;
      })
    opacity = opacity ? 0 : 1;
  });
.newcell text {
  font-family: Arial, sans-serif;
  font-size: 10px;
}

.newcell.leaf rect {
  stroke: #000;
  stroke-width: 0.5px;
}

.oversize {
  display: none;
}

.internal text {
  opacity: 0
}

.highlighter rect {
  fill: #000;
  fill-opacity: 0.7;
  stroke: deepskyblue;
  stroke-width: 5px;
}
.highlighter text {
  fill: deepskyblue;
  opacity: 1
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<p><a id="toggle" href='#'>Activate Cyprus overlay</a></p>

<svg width="186" height="77">
      <g transform="translate(-30.1234, -258.33)">
        <g class="newcell _2331 cell-level-1 Cyprus internal ">
          <rect x="30.123480134121516" y="258.33086334171067" width="185.81893466750355" height="76.6094615363257" style="fill: rgb(100, 200, 75);"></rect>
          <title>Cyprus</title>
          <text x="123.0329474678733" y="296.63559410987354" dy=".35em" text-anchor="middle">Cyprus</text>
        </g>
        <g class="newcell _2332 cell-level-2 Cyprus leaf">
          <rect x="40.12348013412152" y="268.33086334171067" width="31.51365155392795" height="23.97574841366901" style="fill: none;"></rect>
          <title>Cyprus</title>
          <text x="55.88030591108549" y="280.3187375485452" dy=".35em" text-anchor="middle" class="oversize">Cyprus</text>
        </g>
        <g class="newcell _2333 cell-level-2 Cyprus leaf">
          <rect x="40.12348013412152" y="292.30661175537968" width="31.51365155392795" height="32.633713122656687" style="fill: none;"></rect>
          <title>Cyprus</title>
          <text x="55.88030591108549" y="308.62346831670802" dy=".35em" text-anchor="middle" class="oversize">Cyprus</text>
        </g>
        <g class="newcell _2334 cell-level-2 Cyprus leaf">
          <rect x="71.637131688049465" y="268.33086334171067" width="55.48181226963859" height="56.60946153632569" style="fill: none;"></rect>
          <title>Cyprus</title>
          <text x="99.37803782286876" y="296.63559410987354" dy=".35em" text-anchor="middle">Cyprus</text>
        </g>
        <g class="newcell _2335 cell-level-2 Cyprus leaf">
          <rect x="127.11894395768805" y="268.33086334171067" width="78.823470843937" height="56.60946153632569" style="fill: none;"></rect>
          <title>Cyprus</title>
          <text x="166.53067937965655" y="296.63559410987354" dy=".35em" text-anchor="middle">Cyprus</text>
        </g>
      </g>
    </svg>

I've created a .block with more comprehensive versions of these options.

You should be able to find some combination of these that you can use for your purposes.


Related Query