Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The difference between select using merge and selectAll in D3 update mode

I am currently reading "Data Visualization with D3 4.x Cookbook Second Edition".The data update mode is mentioned in chapter3.Sometimes the author uses merge() to achieve data update, but sometimes the author uses selectAll() to achieve. I have found that in some cases they are not interchangeable. I'm confused right now and I want to know, how can I determine which way I should do in data update.

In the 3.2.2,the author uses merge() to achieve data update

    function render(data) { // <- B
        var bars = d3.select("body").selectAll("div.h-bar") // <- C
                .data(data); // Update <- D

        // Enter
        bars.enter() // <- E
                .append("div") // <- F
                    .attr("class", "h-bar") // <- G

            .merge(bars) // Enter + Update <- H
                .style("width", function (d) {
                    return (d * 3) + "px"; // <- I
                })
                .text(function (d) {
                    return d; // <- J
                });


        // Exit
        bars.exit() // <- K
                .remove();
    }

In the 3.6.2,the author uses selectAll() to achieve data update

    function render(data, category) {
        var bars = d3.select("body").selectAll("div.h-bar") // <-B
                .data(data);

        // Enter
        bars.enter()
            .append("div") // <-C
                .attr("class", "h-bar")
            //.merge(bars)
                .style("width", function (d) {
                    return (d.expense * 5) + "px";}
                )
            .append("span") // <-D
                .text(function (d) {
                    return d.category;
                });

        // Update
        d3.selectAll("div.h-bar").attr("class", "h-bar");

        // Filter
        bars.filter(function (d, i) { // <-E
                return d.category == category;
            })
            .classed("selected", true);
    }

I really need your help! Thanks!

like image 254
Alden ThaggardTl Avatar asked Dec 01 '25 22:12

Alden ThaggardTl


1 Answers

That's not the update selection (that would be bars), that's just the strange way the author designed for removing the class selected from the bars.

You see, when you click those buttons, the divs with the class selected become red-ish, while the other divs stay blue. So, the author is using this...

d3.selectAll("div.h-bar").attr("class", "h-bar")

...to remove the selected class, because attr("class", "foo") will override any existing class (see my answer here).

What strikes me as strange is that author, Nick Zhu, is quite good, I can't understand why he is doing that for removing a class. He could simply do:

bars.classed("selected", function (d) {
    return d.category == category;
});

And here is the proof that it works (I'm copying his code from his GitHub repo):

body {
  font-family: "helvetica";
}

button {
  margin: 0 7px 0 0;
  background-color: #f5f5f5;
  border: 1px solid #dedede;
  border-top: 1px solid #eee;
  border-left: 1px solid #eee;
  font-size: 12px;
  line-height: 130%;
  text-decoration: none;
  font-weight: bold;
  color: #565656;
  cursor: pointer;
}

.box {
  width: 200px;
  height: 200px;
  margin: 40px;
  float: left;
  text-align: center;
  border: #969696 solid thin;
  padding: 5px;
}

.red {
  background-color: #e9967a;
  color: #f0f8ff;
}

.blue {
  background-color: #add8e6;
  color: #f0f8ff;
}

.cell {
  min-width: 40px;
  min-height: 20px;
  margin: 5px;
  float: left;
  text-align: center;
  border: #969696 solid thin;
  padding: 5px;
}

.fixed-cell {
  min-width: 40px;
  min-height: 20px;
  margin: 5px;
  position: fixed;
  text-align: center;
  border: #969696 solid thin;
  padding: 5px;
}

.h-bar {
  min-height: 15px;
  min-width: 10px;
  background-color: steelblue;
  margin-bottom: 2px;
  font-size: 11px;
  color: #f0f8ff;
  text-align: right;
  padding-right: 2px;
}

.v-bar {
  min-height: 1px;
  min-width: 30px;
  background-color: #4682b4;
  margin-right: 2px;
  font-size: 10px;
  color: #f0f8ff;
  text-align: center;
  width: 10px;
  display: inline-block;
}

.baseline {
  height: 1px;
  background-color: black;
}

.clear {
  clear: both;
}

.selected {
  background-color: #f08080;
}

.control-group {
  padding-top: 10px;
  margin: 10px;
}

.table {
  width: 70%;
}

.table td,
th {
  padding: 5px;
}

.table-header {
  background-color: #00AFEF;
  font-weight: bold;
}

.table-row-odd {
  background-color: #f0f8ff;
}

.table-row-odd {
  background-color: #d3d3d3;
}

.code {
  display: inline-block;
  font-style: italic;
  background-color: #d3d3d3;
  border: #969696 solid thin;
  padding: 10px;
  margin-top: 10px;
  margin-bottom: 10px;
}

.countdown {
  width: 150px;
  height: 150px;
  font-size: 5em;
  font-weight: bold;
}

.axis .grid-line {
  stroke: black;
  shape-rendering: crispEdges;
  stroke-opacity: .2;
}

.line {
  fill: none;
  stroke: steelblue;
  stroke-width: 2;
}

.dot {
  fill: #fff;
  stroke: steelblue;
}

.area {
  stroke: none;
  fill: steelblue;
  fill-opacity: .2;
}

.pie text {
  fill: white;
  font-weight: bold;
}

._0 {
  stroke: none;
  fill: darkred;
  fill-opacity: .7;
}

._1 {
  stroke: none;
  fill: red;
  fill-opacity: .7;
}

._2 {
  stroke: none;
  fill: blue;
  fill-opacity: .7;
}

._3 {
  stroke: none;
  fill: green;
  fill-opacity: .7;
}

._4 {
  stroke: none;
  fill: yellow;
  fill-opacity: .7;
}

._5 {
  stroke: none;
  fill: blueviolet;
  fill-opacity: .7;
}

.bubble {
  fill-opacity: .3;
}

.bar {
  stroke: none;
  fill: steelblue;
}
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <title>Data Filter</title>
  <script src="https://d3js.org/d3.v7.min.js"></script>
</head>

<body>

  <script type="text/javascript">
    var data = [ // <-A
      {
        expense: 10,
        category: "Retail"
      },
      {
        expense: 15,
        category: "Gas"
      },
      {
        expense: 30,
        category: "Retail"
      },
      {
        expense: 50,
        category: "Dining"
      },
      {
        expense: 80,
        category: "Gas"
      },
      {
        expense: 65,
        category: "Retail"
      },
      {
        expense: 55,
        category: "Gas"
      },
      {
        expense: 30,
        category: "Dining"
      },
      {
        expense: 20,
        category: "Retail"
      },
      {
        expense: 10,
        category: "Dining"
      },
      {
        expense: 8,
        category: "Gas"
      }
    ];

    function render(data, category) {
      var bars = d3.select("body").selectAll("div.h-bar") // <-B
        .data(data);

      // Enter
      bars.enter()
        .append("div") // <-C
        .attr("class", "h-bar")
        .style("width", function(d) {
          return (d.expense * 5) + "px";
        })
        .append("span") // <-D
        .text(function(d) {
          return d.category;
        });

      // Update, COMMENTED OUT!
      //d3.selectAll("div.h-bar").attr("class", "h-bar");

      // Filter
      bars.classed("selected", function(d, i) { // <-E
        return d.category == category;
      });
    }

    render(data);

    function select(category) {
      render(data, category);
    }
  </script>

  <div class="control-group">
    <button onclick="select('Retail')">
        Retail
    </button>
    <button onclick="select('Gas')">
        Gas
    </button>
    <button onclick="select('Dining')">
        Dining
    </button>
    <button onclick="select()">
        Clear
    </button>
  </div>

</body>

</html>
like image 86
Gerardo Furtado Avatar answered Dec 04 '25 13:12

Gerardo Furtado



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!