Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Why d3.exit is not working as desired in d3.join

I am working with this following code and can’t seem to figure out why d3.exit is not working as desired like this here.

For example, the the first 3 ranks between 2000 and 2046 are as below.

2000, A-B-C
2046, C-E-B

When I select 2046 after 2000, I expect the old data (A) to move out as per the exit code belw. But it is not happening.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

exit.style('fill', 'red')
        .transition().duration(1000)
        .attr('transform', (d, i) => `translate(${ 100 },${ d.Rank * 50 })`)
        .remove() 
//desired permutation length 
const length = 5;

//build array from the above length
const perm = Array.from(Array(length).keys()).map((d) => d + 1);

//generate corresponding alphabets for name
const name = perm.map((x) => String.fromCharCode(x - 1 + 65));

//permutation function - https://stackoverflow.com/questions/9960908/permutations-in-javascript/24622772#24622772
function permute(permutation) {
    var length = permutation.length,
        result = [permutation.slice()],
        c = new Array(length).fill(0),
        i = 1,
        k, p;

    while (i < length) {
        if (c[i] < i) {
            k = i % 2 && c[i];
            p = permutation[i];
            permutation[i] = permutation[k];
            permutation[k] = p;
            ++c[i];
            i = 1;
            result.push(permutation.slice());
        } else {
            c[i] = 0;
            ++i;
        }
    }
    return result;
};

//generate permutations
const permut = permute(perm);

//generate year based on permutation
const year = permut.map((x, i) => i + 2000);

//generate a yearly constant based on year to generate final value as per the rank {year-name}
const constant = year.map(d => Math.round(d * Math.random()));

const src =
    year.map((y, i) => {
        return name.map((d, j) => {
            return {
                Name: d,
                Year: y,
                Rank: permut[i][j],
                Const: constant[i],
                Value: Math.round(constant[i] / permut[i][j])
            };
        });
    }).flat();

//console.log(src);

////////////////////////////////////////////////////////////
//////////////////////// 0 BUILD HTML DROPDOWN /////////////
////////////////////////////////////////////////////////////
const dropDown = d3.select('body')
    .append('div', 'dropdown')
    .style('position', 'absolute')
    .style('top', '400px')
    .append('select')
    .attr('name', 'input')
    .classed('Year', true)

dropDown
    .selectAll('option')
    .data(year)
    .enter()
    .append('option')
    //.join('option')
    .text((d) => d) // text showed in the menu
    .attr("value", (d) => { d }) // corresponding value returned by the button

//get the dropdown value
const filterYr = parseFloat(d3.select('.Year').node().value);

dropDown.on('change', () => {
    const yr = parseFloat(d3.select('.Year').node().value);
    draw(yr);
})



////////////////////////////////////////////////////////////
//////////////////////// 1 DATA WRANGLING //////////////////
////////////////////////////////////////////////////////////

const xAccessor = (d) => d.Year;
const yAccessor = (d) => d.Value;
//const zAccessor = (d) => d.Category;

////////////////////////////////////////////////////////////
//////////////////////// 2 CREATE SVG //////////////////////
////////////////////////////////////////////////////////////
//namespace
//define dimension
const width = 1536;
const height = 720;
const svgns = "http://www.w3.org/2000/svg";
const svg = d3.select("svg");

svg.attr("xmlns", svgns).attr("viewBox", `0 0 ${width} ${height}`);

svg
    .append("rect")
    .attr("class", "vBoxRect")
    //.style("overflow", "visible")
    .attr("width", `${width}`)
    .attr("height", `${height}`)
    .attr("stroke", "black")
    .attr("fill", "white");

////////////////////////////////////////////////////////////
//////////////////////// 3 CREATE BOUND ////////////////////
////////////////////////////////////////////////////////////
const padding = {
    top: 70,
    bottom: 100,
    left: 120,
    right: 120
};
const multiplierH = 1; //controls the height of the visual container
const multiplierW = 1; //controls the width of the visual container

const boundHeight = height * multiplierH - padding.top - padding.bottom;
const boundWidth = width * multiplierW - padding.right - padding.left;

//create BOUND rect -- to be deleted later
svg
    .append("rect")
    .attr("class", "boundRect")
    .attr("x", `${padding.left}`)
    .attr("y", `${padding.top}`)
    .attr("width", `${boundWidth}`)
    .attr("height", `${boundHeight}`)
    .attr("fill", "white")
    .attr('stroke', 'black')

//create bound element
const bound = svg
    .append("g")
    .attr("class", "bound")
    //specify transform, must be .style and not .attr, px needs to be mentioned
    .style("transform", `translate(${padding.left}px,${padding.top}px)`);

const textContainer = bound.append('g')
    .classed('textContainer', true)

function draw(filterYr) {


    const data = src.filter(a => a.Year == filterYr)

    //console.log(data.filter(a => a.Rank >= 1 && a.Rank <= 3).sort((a, b) => a.Rank - b.Rank));


    ////////////////////////////////////////////////////////////
    //////////////////////// 4 CREATE SCALE ////////////////////
    ////////////////////////////////////////////////////////////

 
    textContainer
        .selectAll('text')
        .data(data.filter(a => a.Rank >= 1 && a.Rank <= 3))
        .join(
            enter => enter.append('text')
            .attr('transform', (d, i) => `translate(${ 0 },${ d.Rank * 50 })`)
            .attr('x', 30)
            .attr('dy', '1.25em')
            .style('font-family', 'sans-serif')
            .style('font-size', 'xx-large')
            .style('fill', 'blue')
            .style('opacity', '0')
            .text(d => d.Name)
            .transition().duration(1000)
            .style('opacity', 1)
            .selection(),
            update => update
            .transition().duration(1000)
            .attr('transform', (d, i) => `translate(${ 0 },${ d.Rank * 50 })`)
            .style('fill', 'black')
            .text(d => d.Name)
            .selection(),
            exit => exit
            .style('fill', 'red')
            .transition().duration(1000)
            .attr('transform', (d, i) => `translate(${ 100 },${ d.Rank * 50 })`)
            .remove()

        )



}

draw(filterYr);
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<script type="text/javascript" src="https://d3js.org/d3.v7.min.js"></script>

<body>
    <svg>       
    </svg>
    <!--d3 script-->
    <script type="text/javascript" src="prod5.js">
    </script>
</body>

</html>

>Solution :

If you want the data bound by its value, not by its index, you have to provide a key function:

.data(data.filter(a => a.Rank >= 1 && a.Rank <= 3), d => d.Name)
//this is the key function ---------------------------^

Here’s your code with that change:

//desired permutation length 
const length = 5;

//build array from the above length
const perm = Array.from(Array(length).keys()).map((d) => d + 1);

//generate corresponding alphabets for name
const name = perm.map((x) => String.fromCharCode(x - 1 + 65));

//permutation function - https://stackoverflow.com/questions/9960908/permutations-in-javascript/24622772#24622772
function permute(permutation) {
  var length = permutation.length,
    result = [permutation.slice()],
    c = new Array(length).fill(0),
    i = 1,
    k, p;

  while (i < length) {
    if (c[i] < i) {
      k = i % 2 && c[i];
      p = permutation[i];
      permutation[i] = permutation[k];
      permutation[k] = p;
      ++c[i];
      i = 1;
      result.push(permutation.slice());
    } else {
      c[i] = 0;
      ++i;
    }
  }
  return result;
};

//generate permutations
const permut = permute(perm);

//generate year based on permutation
const year = permut.map((x, i) => i + 2000);

//generate a yearly constant based on year to generate final value as per the rank {year-name}
const constant = year.map(d => Math.round(d * Math.random()));

const src =
  year.map((y, i) => {
    return name.map((d, j) => {
      return {
        Name: d,
        Year: y,
        Rank: permut[i][j],
        Const: constant[i],
        Value: Math.round(constant[i] / permut[i][j])
      };
    });
  }).flat();

//console.log(src);

////////////////////////////////////////////////////////////
//////////////////////// 0 BUILD HTML DROPDOWN /////////////
////////////////////////////////////////////////////////////
const dropDown = d3.select('body')
  .append('div', 'dropdown')
  .style('position', 'absolute')
  .style('top', '400px')
  .append('select')
  .attr('name', 'input')
  .classed('Year', true)

dropDown
  .selectAll('option')
  .data(year)
  .enter()
  .append('option')
  //.join('option')
  .text((d) => d) // text showed in the menu
  .attr("value", (d) => {
    d
  }) // corresponding value returned by the button

//get the dropdown value
const filterYr = parseFloat(d3.select('.Year').node().value);

dropDown.on('change', () => {
  const yr = parseFloat(d3.select('.Year').node().value);
  draw(yr);
})



////////////////////////////////////////////////////////////
//////////////////////// 1 DATA WRANGLING //////////////////
////////////////////////////////////////////////////////////

const xAccessor = (d) => d.Year;
const yAccessor = (d) => d.Value;
//const zAccessor = (d) => d.Category;

////////////////////////////////////////////////////////////
//////////////////////// 2 CREATE SVG //////////////////////
////////////////////////////////////////////////////////////
//namespace
//define dimension
const width = 1536;
const height = 720;
const svgns = "http://www.w3.org/2000/svg";
const svg = d3.select("svg");

svg.attr("xmlns", svgns).attr("viewBox", `0 0 ${width} ${height}`);

svg
  .append("rect")
  .attr("class", "vBoxRect")
  //.style("overflow", "visible")
  .attr("width", `${width}`)
  .attr("height", `${height}`)
  .attr("stroke", "black")
  .attr("fill", "white");

////////////////////////////////////////////////////////////
//////////////////////// 3 CREATE BOUND ////////////////////
////////////////////////////////////////////////////////////
const padding = {
  top: 70,
  bottom: 100,
  left: 120,
  right: 120
};
const multiplierH = 1; //controls the height of the visual container
const multiplierW = 1; //controls the width of the visual container

const boundHeight = height * multiplierH - padding.top - padding.bottom;
const boundWidth = width * multiplierW - padding.right - padding.left;

//create BOUND rect -- to be deleted later
svg
  .append("rect")
  .attr("class", "boundRect")
  .attr("x", `${padding.left}`)
  .attr("y", `${padding.top}`)
  .attr("width", `${boundWidth}`)
  .attr("height", `${boundHeight}`)
  .attr("fill", "white")
  .attr('stroke', 'black')

//create bound element
const bound = svg
  .append("g")
  .attr("class", "bound")
  //specify transform, must be .style and not .attr, px needs to be mentioned
  .style("transform", `translate(${padding.left}px,${padding.top}px)`);

const textContainer = bound.append('g')
  .classed('textContainer', true)

function draw(filterYr) {


  const data = src.filter(a => a.Year == filterYr)

  //console.log(data.filter(a => a.Rank >= 1 && a.Rank <= 3).sort((a, b) => a.Rank - b.Rank));


  ////////////////////////////////////////////////////////////
  //////////////////////// 4 CREATE SCALE ////////////////////
  ////////////////////////////////////////////////////////////


  textContainer
    .selectAll('text')
    .data(data.filter(a => a.Rank >= 1 && a.Rank <= 3), d => d.Name)
    .join(
      enter => enter.append('text')
      .attr('transform', (d, i) => `translate(${ 0 },${ d.Rank * 50 })`)
      .attr('x', 30)
      .attr('dy', '1.25em')
      .style('font-family', 'sans-serif')
      .style('font-size', 'xx-large')
      .style('fill', 'blue')
      .style('opacity', '0')
      .text(d => d.Name)
      .transition().duration(1000)
      .style('opacity', 1)
      .selection(),
      update => update
      .transition().duration(1000)
      .attr('transform', (d, i) => `translate(${ 0 },${ d.Rank * 50 })`)
      .style('fill', 'black')
      .text(d => d.Name)
      .selection(),
      exit => exit
      .style('fill', 'red')
      .transition().duration(1000)
      .attr('transform', (d, i) => `translate(${ 100 },${ d.Rank * 50 })`)
      .remove()

    )



}

draw(filterYr);
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<script type="text/javascript" src="https://d3js.org/d3.v7.min.js"></script>

<body>
  <svg>       
    </svg>
  <!--d3 script-->
  <script type="text/javascript" src="prod5.js">
  </script>
</body>

</html>
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading