简体   繁体   中英

D3: data update “enter” selection is empty

I'm trying to use the data, update, enter pattern in D3 to update some data. I've been following this example: https://bl.ocks.org/mbostock/3808218

I've got two sets of data, one with an extra value:

const data = [
  {
    weekId: "w-1",
    start: 100
  },
  {
    weekId: "w-2",
    start: 200
  }
];

const updatedData = [
  {
    weekId: "w-1",
    start: 100
  },
  {
    weekId: "w-2",
    start: 200
  },
  {
    weekId: "w-3",
    start: 300
  }
];

Some code that creates a set of green blocks

// Create initial data
this.visualisation
  .selectAll()
  .data(data, d => d.weekId)
  .enter()
  .append("rect")
  .attr("class", "spline-group")
  .attr("x", w => w.start)
  .attr("y", 20)
  .attr("width", 22)
  .attr("height", 22)
  .attr("fill", "green");

I then do a selection and call data :

// https://bl.ocks.org/mbostock/3808218
// DATA JOIN
// Join new data with old elements, if any.
const dataGroups = d3
  .selectAll("rect.spline-group")
  .data(updatedData, d => d.weekId);

I update the green blocks to blue (Works fine!):

// UPDATE
// Update old elements as needed.
dataGroups.attr("fill", "blue");

Here's where I have a problem, I enter on the new data to try append the new rect (white this time so I can see it's new):

// ENTER
// Create new elements as needed.
const newItems = dataGroups
  .enter()
  .append("rect")
  .attr("class", "spline-group")
  .attr("x", w => w.start)
  .attr("y", 30)
  .attr("width", 20)
  .attr("height", 20)
  .attr("fill", "white");

That newItems selection is always empty, whatever I seem to try. What's going wrong?

Working example:

 const data = [ { weekId: "w-1", start: 100 }, { weekId: "w-2", start: 200 } ]; const updatedData = [ { weekId: "w-1", start: 100 }, { weekId: "w-2", start: 200 }, { weekId: "w-3", start: 300 } ]; // Create initial data d3.select("svg") .selectAll() .data(data, d => d.weekId) .enter() .append("rect") .attr("class", "spline-group") .attr("x", w => w.start) .attr("y", 20) .attr("width", 22) .attr("height", 22) .attr("fill", "green"); // https://bl.ocks.org/mbostock/3808218 // DATA JOIN // Join new data with old elements, if any. const dataGroups = d3 .selectAll("rect.spline-group") .data(updatedData, d => d.weekId); // UPDATE // Update old elements as needed. dataGroups.attr("fill", "blue"); // ENTER // Create new elements as needed. const newItems = dataGroups .enter() .append("rect") .attr("class", "spline-group") .attr("x", w => w.start) .attr("y", 30) .attr("width", 20) .attr("height", 20) .attr("fill", "white"); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script> <svg> </svg> 

You are entering the new rectangle, you can log .size() on the enter selection and see one element is added:

 const data = [ { weekId: "w-1", start: 100 }, { weekId: "w-2", start: 200 } ]; const updatedData = [ { weekId: "w-1", start: 100 }, { weekId: "w-2", start: 200 }, { weekId: "w-3", start: 300 } ]; // Create initial data d3.select("svg") .selectAll() .data(data, d => d.weekId) .enter() .append("rect") .attr("class", "spline-group") .attr("x", w => w.start) .attr("y", 20) .attr("width", 22) .attr("height", 22) .attr("fill", "green"); // https://bl.ocks.org/mbostock/3808218 // DATA JOIN // Join new data with old elements, if any. const dataGroups = d3 .selectAll("rect.spline-group") .data(updatedData, d => d.weekId); // UPDATE // Update old elements as needed. dataGroups.attr("fill", "blue"); // ENTER // Create new elements as needed. const newItems = dataGroups .enter() .append("rect") .attr("class", "spline-group") .attr("x", w => w.start) .attr("y", 30) .attr("width", 20) .attr("height", 20) .attr("fill", "white"); console.log("new items: " + newItems.size()); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script> <svg> </svg> 

So if you are entering the new element, to where are you appending it? Let's look at where your rectangle is:

在此处输入图片说明

You did not specify where you wanted the rectangle. When entering the first rectangles you use:

d3.select("svg").selectAll("rect")

And your entered elements are created as children of the svg, on the second enter cycle, you simply use d3.selectAll("rect") . You didn't specify where you wanted to enter the additional element, so it was appended to the document.

To make sure your enter new elements at the right place, just select the parent container first, as you did when entering the first two elements:

const dataGroups = d3.select("svg") // select SVG
    .selectAll("rect.spline-group") // then select all rects.
    .data(updatedData, d => d.weekId);

dataGroups.attr("fill", "blue");

const newItems = dataGroups
   .enter()
   .append("rect")

 const data = [ { weekId: "w-1", start: 100 }, { weekId: "w-2", start: 200 } ]; const updatedData = [ { weekId: "w-1", start: 100 }, { weekId: "w-2", start: 200 }, { weekId: "w-3", start: 300 } ]; // Create initial data d3.select("svg") .selectAll() .data(data, d => d.weekId) .enter() .append("rect") .attr("class", "spline-group") .attr("x", w => w.start) .attr("y", 20) .attr("width", 22) .attr("height", 22) .attr("fill", "green"); // https://bl.ocks.org/mbostock/3808218 // DATA JOIN // Join new data with old elements, if any. const dataGroups = d3.select("svg") .selectAll("rect.spline-group") .data(updatedData, d => d.weekId); // UPDATE // Update old elements as needed. dataGroups.attr("fill", "blue"); // ENTER // Create new elements as needed. const newItems = dataGroups .enter() .append("rect") .attr("class", "spline-group") .attr("x", w => w.start) .attr("y", 30) .attr("width", 20) .attr("height", 20) .attr("fill", "crimson"); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script> <svg width="500"> </svg> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM