D3.js - Uncaught TypeError: .enter(...).append(...).merge is not a function

Viewed 688

I am trying to build a table with multiple nested dropdowns using d3.js. When I use a newer version of d3 my code works just fine, however for various reasons I need to use d3.js v3 and when I replace the newer version with the older version I get the following:

Uncaught TypeError: options4.enter(...).append(...).merge is not a function

I'm assuming the older version of d3 doesn't have a merge() function, however I'm at a loss as to what to substitute it with.

Here is my code:

<script src="https://d3js.org/d3.v3.min.js"></script>

var colorScheme = d3.scale.ordinal()
                .domain(["USA", "PRI", "CAN"])
                .range(["#d52922", "#d69105", "#4b7ba1"]);

var jobsCsvData = null;

d3.csv("master_info_final_v12_conversion.csv", function(data) {

data.forEach(function(d) {
    d["index"] = d["index"];
    d["name"] = d["name"];
    d["city"] = d["city"];
    d["state"] = d["state"];
    d["country"] = d["country"];
});

JobsCsvData = data;

var nestedTwo = d3.nest()
 .key(function(d) {
  return d.country;
 })
 .key(function(d) {
  return d.state;
})
.key(function(d) {
  return d.city;
})
.entries(data);

function updateLocationValues(g) {
  var filtered = filtered2[0].values.filter(function(d) {
    return d.key == g;
  });

  var options5 = d3.select("#select5")
    .selectAll("option")
    .data(filtered[0].values.map(function(d) {
    //.data(filtered3[0].values.map(function(d) {
      return d.key;
    }));

  options5.exit().remove();

Here is the merge() function giving me my problems.

  options5.enter()
    .append("option")
    .merge(options5)
    .text(function(d) {
      return d;
    });
    tabulate(data);
  }

 var options5 = d3.select("#select5")
   .selectAll("option")
   .data(filtered3[0].values.map(function(d) {
  return d.key;
  }.)).enter()
  .append("option")
  .text(function(d) {
  return d;
 });

.... {more code}

}

There's more code which I can include if this isn't sufficient. Any help would be immensely appreciated.

1 Answers

D3v3 magically included the enter selection in the update selection, doing the merge for you behind the scenes. Starting with d3v4, you needed to explicitly merge enter and update.

To code merging update and enter to d3v3, you basically just remove the merge statement and use the update selection after you enter everything. Instead of:

var update = d3.selectAll("...").data(...);

enter = update.enter().append()....

update.merge(enter).attr(....

You could simply use:

var update = d3.selectAll("...").data(...)

update.enter().append()....

update.attr(... // includes both enter and update.

Essentially, the change in d3v4 is the selections are immutable, the enter selection is no longer magically added to the update selection behind the scenes after you do the enter. However, in order to allow enter and update selection to be combined, d3 introduced the selection.merge() method which returns a new selection by combining two.

Here's a couple snippets showing d3v3 (first) vs d3v4+ (second) in what's in each selection at each step:

var original = d3.selectAll("p")
  .data([1,2,3])
  .enter()
  .append("p")
  
original.append("span").text(d=>d);
  
// update selection pre enter.
var update = d3.selectAll("p")
  .data([1,2,3,4,5]) // two new elements.
  
  update.append("span")
    .text("  in update selection pre enter");
    
// enter selection.
var enter = update.enter()
  .append("p")
  
  enter.append("span").text(d=>d + " in enter selection");
  
// update post-enter.
update.append("span").text(" | in update selection after enter");
p {
  border: 1px dotted black;
  padding: 4px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>

vs.

var original = d3.selectAll("p")
  .data([1,2,3])
  .enter()
  .append("p")
  
original.append("span").text(d=>d);
  
// update selection pre enter.
var update = d3.selectAll("p")
  .data([1,2,3,4,5]) // two new elements.
  
  update.append("span")
    .text("  in update selection pre enter");
    
// enter selection.
var enter = update.enter()
  .append("p")
  
  enter.append("span").text(d=>d + " in enter selection");
  
// update post-enter.
update.append("span").text(" | in update selection after enter");

// merge:
update.merge(enter).append("span").text(" | in merged selection ");
p {
  border: 1px dotted black;
  padding: 4px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

Related