0

I am trying to replace text inside existing divs using d3.select when a new dataset is selected, but what I've written adds new divs every time a new dataset is selected. The use case is a leaderboard that picks up the top people groups (d.Culture) that have created objects out of a certain material (Gold, Silver, Bronze, etc.).

Page in question: https://3milychu.github.io/whatmakesart/
It takes a full minute to load (if you see the bar chart, page is loaded)

I've also tried adding .remove() and .exit() unsuccessfully per other suggestions I've viewed.

For some reason, even though the targeted divs are classes, I have to use ".myDiv" instead of "#myDiv" to see the text.

function origins(dataset) {

var totalRows = dataset.length;
console.log(totalRows);

var format = d3.format(".0%");

var origins = d3.nest()
    .key(function(d) { return d.Culture; })
    .rollup(function(v) { return v.length; })
    .entries(dataset)
    .sort(function(a,b) {return d3.descending(a.value,b.value);});
console.log(origins);

 var culture1 = d3.select(".culture").selectAll(".culture1")
    .data(origins)
    .enter()
    .append("div")
    .attr("id", "culture1")
    .filter(function (d, i) { return i === 0;})
    .text(function(d) { return d.key + " " + format(d.value/totalRows); }) 

 <--repeat for "culture2", "culture3", etc. --> 

};

The html divs I am trying to target:

<!-- Origins -->
<div id ="origins">
    <h1>Origins</h1>
    <div class="culture" id="culture1"></div>
    <div class="culture" id="culture2"></div>
    <div class="culture" id="culture3"></div>
    <div class="culture" id="culture4"></div>
    <div class="culture" id="culture5"></div>
    <div class="culture" id="culture6"></div>
    <div class="culture" id="culture7"></div>
</div>
Amit Phaltankar
  • 3,341
  • 2
  • 20
  • 37
Emily Chu
  • 187
  • 3
  • 15

2 Answers2

0

There are a couple of things going on here that are problematic.

First, you're appending lots of divs with the same ID when you do .attr("id", "culture1") . This can cause all sorts of unexpected problems.

The filter function doesn't work quite as you would expect. When you do .filter(function (d, i) { return i === 0;}), you're still creating empty divs for all of the elements that do not match the filter. Use the DOM inspector in chrome and you'll see you have tons of empty divs. If you're only trying to get the first element from the array, I suggest passing only that data into the data function. If origins is going to be a different size each time you pass it into the data function, then you will need to implement removing the element on exit. But if the origins array is the same size, the existing data should just update.

That should help fix up the behavior you're seeing. It's possible there are other issues, but these are definite issues that should be fixed first.

Jeff Storey
  • 56,312
  • 72
  • 233
  • 406
0

I got the following to replace the ranks upon selection change:

function origins(dataset) {

var totalRows = dataset.length;
console.log(totalRows);

var format = d3.format(".0%");

var origins = d3.nest()
    .key(function(d) { return d.Culture; })
    .rollup(function(v) { return v.length; })
    .entries(dataset)
    .sort(function(a,b) {return d3.descending(a.value,b.value);});
// console.log(origins);

d3.select(".culture").selectAll("text").remove()

var culture1 = d3.select(".culture").selectAll("#ranks")
        .data(origins.filter(function (d, i) { return i === 0;}))
        .enter()
        .append("text")
        .attr("id", "culture1")
        .text(function(d) { return d.key + " " + format(d.value/totalRows); })
        .exit();

<--repeat for all ranks -->
};

The html was changed to:

    <div id ="origins">
        <h1>Origins</h1>
        <div class="culture" id="ranks"></div>
    </div>

The key was to select the element types within the div, and .remove() them prior to declaring the variable and appending. Also per Jeff's suggestion, passing through the filter at .data() prevented creating extra elements.

Emily Chu
  • 187
  • 3
  • 15