Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

167
Views
instant values for d3 enter selection and transitioned for update

I'm trying to avoid repeating code for the enter and update selections in d3. I have successfully used merge to get the right selection of all items that are present with the new data (after I have used append to add the new ones). Now I want to set some attributes such as r, cy and cx. I can do that on the merged selection but if I put them behind a transition the items that are just appearing animate in from 0,0 and I want them to just appear in the right place.

What I'm hoping for is some way to write all the attribute setting lines into one function and one time pass in the enter() selection and the next time pass in the transitioned merge so that I only have to write the lines for setting cx once. Here's what I have already followed by my guess at pseudo code

 const valueCircles = this.chartGroup.selectAll('.valueCircle')
        .data(values);

    valueCircles.enter()
        .append('circle')
        .attr('stroke-width', '1px')
        .attr('stroke', '#ffffff')
        .attr('opacity', '1')
        .attr('class', 'dots valueCircle')
        .merge(valueCircles)
        .transition()
        .duration(100)
        .attr('r', (d) => {
            if (d.y < 0.5 ) {
                return 5;
            } else {
                return 15;
            }
        })
        .attr('cx', (d) => {
            return timescale(new Date(d.x));
        })
        .attr('cy', (d) => {
                return valueScale(d.y)
        })

This is what I think I want to achieve

function setTheDynamicValues(aSelection) {
    aSelection
        .attr('cx', (d) => {
            return timescale(new Date(d.x));
        })
        .attr('cy', (d) => {
                return valueScale(d.y)
        })
}

function updateGraph(newData) {
   const valueCircles = this.chartGroup.selectAll('.valueCircle')
        .data(newData);

   setTheDynamicValues(valueCircles.enter());
   setTheDynamicValues(valueCircles.transition().duration(100));
}

Another way of describing this would be to make the duration of the transition 0 for the entering elements and 100 for existing elements so that new ones appear correct and existing ones have a visible transition.

about 4 years ago · Juan Pablo Isaza
1 answers
Answer question

0

First of all, D3 selections are immutable: that merge of yours is not doing anything. This would be the proper pattern:

//here, pay attention to "let" instead of const
let valueCircles = this.chartGroup.selectAll('.valueCircle')
    .data(values);

//our enter selection is called "valuesCirclesEnter"
const valueCirclesEnter = valueCircles.enter()
    .append('circle')
    etc...

//now, you merge the selections
valueCircles = valueCirclesEnter.merge(valueCircles);

From that point on, valueCircles contains both your updating and entering elements.

Now, back to your question:

An idiomatic D3 indeed has a lot of repetition, and that's a point lots of people complain about D3. But we can avoid some repetition with methods like selection.call. Also, since you want a transition for the update selection but none for the enter selection, you can simply drop the merge.

Here is a basic exemple, using a bit of your code:

const data1 = [{
  x: 100,
  y: 100
}, {
  x: 230,
  y: 20
}];
const data2 = [{
  x: 10,
  y: 10
}, {
  x: 50,
  y: 120
}, {
  x: 190,
  y: 100
}, {
  x: 140,
  y: 30
}, {
  x: 270,
  y: 140
}];
const svg = d3.select("svg");
draw(data1);
setTimeout(() => draw(data2), 1000);

function draw(values) {
  const valueCircles = svg.selectAll('.valueCircle')
    .data(values);

  valueCircles.enter()
    .append('circle')
    .attr("class", "valueCircle")
    .attr("r", 10)
    .call(positionCircles);

  valueCircles.transition()
    .duration(1000)
    .call(positionCircles)
};

function positionCircles(selection) {
  selection.attr("cx", d => d.x)
    .attr("cy", d => d.y)
}
<script src="https://d3js.org/d3.v7.min.js"></script>
<svg></svg>

In the snippet above, I'm positioning both the enter and update selections using a function called positionCircles; however, while for the enter selection I'm passing a simple selection, for the update selection I'm passing a transitioning selection. But the function is the same:

function positionCircles(selection) {
  selection.attr("cx", d => d.x)
    .attr("cy", d => d.y)
}
about 4 years ago · Juan Pablo Isaza Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!