Reputation: 171
I have a problem with D3, if I append an Element a second time I will get duplicate elements in the parent node.
node.enter().insert("svg:g")
.attr("class", 'test')
.attr("width", '63px')
.attr("height", '68px')
.call(force.drag);
node.append("svg:circle")
.attr("class", "bg-circle")
.attr("r", "30px");
For Example i will get:
<g class="test">
<circle class="bg-circle" />
<circle class="bg-circle" />
</g>
But i want:
<g class="test">
<circle class="bg-circle" />
</g>
Even when I call my function to set the nodes a second time.
Upvotes: 15
Views: 11342
Reputation: 1
This question asked 9 years ago but I have been struggling with same problem and fixed like this
Create function for check that
const d3ExistRootControl = <GElement extends BaseType, Datum, PElement extends BaseType, PDatum>(
selection: Selection<GElement, Datum, PElement, PDatum>,
createSelection: () => Selection<GElement, Datum, PElement, PDatum>) => {
if (selection.size()) return selection;
return createSelection();
};
Usage
const mouseG = d3ExistRootControl(
svg.select(`g.${config.classes.mouseOverEffects}`),
() => svg.append('g')
.attr('class', config.classes.mouseOverEffects)
);
With data and type implementations
const mousePerLine = d3ExistRootControl(
mouseG.selectAll(`.${config.classes.mousePerLine}`) as any,
() => mouseG.selectAll(`.${config.classes.mousePerLine}`)
.data(sumstat)
.enter()
.append('g')
.attr('class', config.classes.mousePerLine)
);
Upvotes: 0
Reputation: 27611
I had a single function that either initialized or updated a plot, and to prevent duplicate boilerplate from being made while allowing for transitions to the element, I ended up doing the following:
const grid = d3.select('.grid').node()
? d3.select('.grid')
: g.append('g')
.attr('class', 'grid');
Upvotes: 5
Reputation: 555
I had the same problem and the previews answers didn't help me so I solved it by doing this:
In my html. index i have two buttons that calls different data sources to make the same kind of charts, so every time I clicked one of the buttons I got duplicate graphs (function 1 calls data for divs with _des and function 2 calls data for _ic divs)
<div>
<h3>Graficas</h3>
<label>
<input id="des" type="checkbox" class="radio" name="fooby[1][]" checked onchange='load_des(this)'/>data 1 </label>
<label>
<input id="ic" type="checkbox" class="radio" name="fooby[1][]" onchange='load_ic(this)'/>data 2</label>
</div>
<div class="container">
<div id="donut_des"></div>
<div id="bars_des" ></div>
<div id="donut_ic"></div>
<div id="bars_ic" ></div>
</div>
So I just added this in my main.js file for each function:
function load_des(obj) {
if($(obj).is(":checked")){
document.getElementById('donut_ic').innerHTML = "";
document.getElementById('bars_ic').innerHTML = "";
// do everything else normally
}
function load_ic(obj) {
if($(obj).is(":checked")){
document.getElementById('donut_des').innerHTML = "";
document.getElementById('bars_des').innerHTML = "";
// do everything else normally
}
Hope this works for anyone, since I couldn't solve this any other way.
Upvotes: 0
Reputation: 404
I guess you want to use d3 to draw some UI. I used something like that:
function appendOnce(selection, s) {
var l = s.split("."); // l[0] tag, l[0] dot separated classes
var g = selection.selectAll(s)
.data([0])
g.enter().append(l[0])
.attr("class", l.slice(1).join(" "))
return g;
}
// and then use
var clild = appendOnce(parent, "text.y-caption")
.attr(...)
.style(...);
Upvotes: -1