brohymn
brohymn

Reputation: 460

Add small rectangles inside BIG rectangles with unique Ids and links in D3? after an specific ID

In my previous question I asked how could I generate unique IDs for generated rectangles here

Now, I've been trying to draw small rectangles inside those rectangles. However, id="rectup0" can only have 4 small rect with unique xlink:href attribute the rest of them can have 2 rectangles inside.

<body>
<div id="chart">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.js"></script>

    <script type="text/javascript">

      var width = 1000,
          height = 250,
          margin = 7,
          nRect = 11,
          rectWidth = (width - (nRect - 1) * margin) / nRect,

          svgContainer = d3.select('#chart').append('svg')
              .attr('width', width)
              .attr('height', height),

          svgWalkway = d3.select('#chart').append('svg')
            .attr('width', '1000')
            .attr('height', '150'),

          svgContainer2 = d3.select('#chart').append('svg')
            .attr('width', width)
            .attr('height', height);


// Rectangles scaling

      var firstRow = d3.range(nRect),
          posxScale = d3.scaleLinear()
            .domain(d3.extent(firstRow))
            .range([0, width - rectWidth]);

      var middleRow = d3.range(1),
          posxScale3 = d3.scaleLinear()
            .domain(d3.extent(middleRow))
            .range([0, width - rectWidth]);

      var secondRow = d3.range(nRect),
          posxScale2 = d3.scaleLinear()
            .domain(d3.extent(secondRow))
            .range([0, width - rectWidth]);

      //Drawing the actual objects HTML rects    
      svgContainer.selectAll('rect.up')
        .data(firstRow)
        .enter()
        .append('rect')
        .attr("id", function(d,i){ return "rectup" + i })
        .attr('x', posxScale)
        .attr('width', rectWidth)
        .attr('height', height)
        .attr('class','up');

      svgWalkway.selectAll('rect.middle')
        .data(middleRow)
        .enter()
        .append('rect')
        .attr('x', posxScale3)
        .attr('width','1000')
        .attr('height','150')
        .attr('class', 'middle');


      svgContainer2.selectAll('rect.down')
        .data(secondRow)
        .enter()
        .append('rect')
        .attr('id', function (d,i) { return "rectdown" + i })
        .attr('x', posxScale2)
        .attr('width', rectWidth)
        .attr('height', height)
        .attr('class', 'down')
        .attr('y', 0);
    </script>
</div>
</body>

I've tried drawing similar rectangles like the previous ones (using .append()) but so far that just put them next to svgContainer. I've been reading some posts regarding using grouping, tried it but didn't work. Thanks in advance

UPDATE

so basically, the whole question can be resumed as adding a rect tag right after rect id="rectup0" so I can have

<rect id="rectup0"></rect>
<rect id="redrect" x="0" width="20" height="40"></rect>

I know JQuery lets you add class attributes to other classes, but not sure about D3 , this will be something like adding a new rect tag programmatically. is that even possible?

UPDATE 2

I guess it is possible with d3.insert, with the following:

d3.select('#rectup0').insert('rect')
  .attr('id', 'redrect')
  .attr('x', 0)
  .attr('width', '20')
  .attr('height', '40');

however it gets it in the middle of the beginning and closing rect tag like this

<rect id="rectup0">
    <rect id="redrect" x="0" width="20" height="40"></rect>
</rect>

Needless to say this doesn't work because of the closing rect tag

Upvotes: 2

Views: 1196

Answers (1)

VMAtm
VMAtm

Reputation: 28355

You can do the trick with each wrapper for selected data. It accepts 3 arguments: current data d, current index i, and whole array of data arr. So if you need some kind of checks, should you append the small rect, or not, you can create a helper function:

var testIndex = function(d, i, arr) {
    return
      // test index
      i == 5 ||
      // test current data
      d == 10 ||
      // test other array members, which would be an EnterNode here
      (i > 0 && arr[i - 1]['__data__'] == 7);  
}

// do checks in one line:
var testIndex = function (d, i, arr) { return i == 4 || i == 10 || i == 7; }

var data = svgContainer.selectAll('rect.up')
  .data(firstRow);

data
  .enter()
  .each(function (d, i, arr) {
    // select DOM element to add the rects
    t = d3.select(this);

    t
      .append('rect')
      // note that here we use empty the parameters list
      .attr('id', 'rectup' + i)
      .attr('x', posxScale)
      .attr('width', rectWidth)
      .attr('height', height)
      .attr('class','up');

    if (!testIndex(d, i, arr)) {
      return;
    }

    t
      .append('rect')
      .attr('id', 'redrect' + i)
      // note that we need to define x-coord similar to first rect
      .attr('x', posxScale)
      .attr('class', 'redrect');
});

// remove your data before redraw
data
  .exit()
  .remove();

Output:

<svg width="1000" height="250">
  <rect id="rectup0" x="0" width="84.54545454545455" height="250" class="up"></rect>
  <rect id="rectup1" x="91.54545454545456" width="84.54545454545455" height="250" class="up"></rect>
  <rect id="rectup2" x="183.09090909090912" width="84.54545454545455" height="250" class="up"></rect>
  <rect id="rectup3" x="274.6363636363636" width="84.54545454545455" height="250" class="up"></rect>
  <rect id="rectup4" x="366.18181818181824" width="84.54545454545455" height="250" class="up"></rect>
  <rect id="rectup5" x="457.72727272727275" width="84.54545454545455" height="250" class="up"></rect>
  <rect id="redrect5" x="457.72727272727275" class="redrect"></rect>
  <rect id="rectup6" x="549.2727272727273" width="84.54545454545455" height="250" class="up"></rect>
  <rect id="rectup7" x="640.8181818181818" width="84.54545454545455" height="250" class="up"></rect>
  <rect id="redrect7" x="732.3636363636365" class="redrect"></rect>
  <rect id="rectup8" x="732.3636363636365" width="84.54545454545455" height="250" class="up"></rect>
  <rect id="rectup9" x="823.909090909091" width="84.54545454545455" height="250" class="up"></rect>
  <rect id="rectup10" x="915.4545454545455" width="84.54545454545455" height="250" class="up"></rect>
  <rect id="redrect10" x="915.4545454545455" class="redrect"></rect>
</svg>

The downside of such method is that you can't provide a attr parameter as JSON:

// wouldn't work
.attr({
  id: 'redrect',
  x: posxScale,
  width: '20',
  height: '40'
});

Upvotes: 1

Related Questions