I want to be able to adjust the data of my D3 chart and update the chart. To do this, I tried to delete the old “slice” elements and then update them.
My problem is that the wrong “slices” elements are deleted (not the missing ones) and the same, that are delete will be opened again (picture, green should be delete and red will be deleted). I want the “g”, “path” and “text” elements to be deleted in “g.slice” and then the new elements will be created with the new data. My delete and update code will be find in the “Enter(), Update, Exit()” comment section.
// Data
const data1 = {
"name": "TOPICS",
"children": [{
"name": "Topic A",
"children": [{
"name": "Sub A1",
"size": 4
}, {
"name": "Sub A2",
"size": 4
}]
}, {
"name": "Topic B",
"children": [{
"name": "Sub B1",
"size": 3
}, {
"name": "Sub B2",
"size": 3
}, {
"name": "Sub B3",
"size": 3
}]
}, {
"name": "Topic C",
"children": [{
"name": "Sub A3",
"size": 4
}, {
"name": "Sub A4",
"size": 4
}]
}]
};
const data2 = {
"name": "TOPICS",
"children": [{
"name": "Topic A",
"children": [{
"name": "Sub A1",
"size": 4
}, {
"name": "Sub A2",
"size": 4
}]
}, {
"name": "Topic B",
"children": [{
"name": "Sub B1",
"size": 3
}, {
"name": "Sub B2",
"size": 3
}, {
"name": "Sub B3",
"size": 3
}]
}]
};
//-------------------------------------------------------------------------------------------
// Declare variables
var i_region_static_id = "sunburst",
parentDiv = document.getElementById(i_region_static_id),
width = parentDiv.clientWidth,
height = 450,
root,
rootDepth,
x,
y,
arc,
middleArcLine,
middleAngle,
color = d3.scaleOrdinal(d3.schemeCategory20);
maxRadius = (Math.min(width, height) / 2) - 5;
const partition = d3.partition();
//-----------------------------------------------------------------------------------
// SVG-Element
var svg = d3.select('#' + i_region_static_id).append('svg')
.style('width', width)
.style('height', height)
.attr('viewBox', `${-width / 2} ${-height / 2} ${width} ${height}`)
.on('dblclick', d => {
if (event.detail === 2) focusOn() // Double click
});
//-----------------------------------------------------------------------------------
// X-Scale
x = d3.scaleLinear()
.range([0, 2 * Math.PI])
.clamp(true);
//-----------------------------------------------------------------------------------
// Y-Scale
y = d3.scaleSqrt()
.range([maxRadius * .1, maxRadius]);
//-----------------------------------------------------------------------------------
// Create Arc generator
arc = d3.arc()
.startAngle(d => x(d.x0))
.endAngle(d => x(d.x1))
.innerRadius(d => Math.max(0, y(d.y0)))
.outerRadius(d => Math.max(0, y(d.y1)))
//-----------------------------------------------------------------------------------
middleArcLine = d => {
const halfPi = Math.PI / 2;
const angles = [x(d.x0) - halfPi, x(d.x1) - halfPi];
const r = Math.max(0, (y(d.y0) + y(d.y1)) / 2);
const middleAngle = (angles[1] + angles[0]) / 2;
const invertDirection = middleAngle > 0 && middleAngle < Math.PI; // On lower quadrants write text ccw
if (invertDirection) {
angles.reverse();
}
const path = d3.path();
path.arc(0, 0, r, angles[0], angles[1], invertDirection);
return path.toString();
}
//-------------------------------------------------------------------------------------------
// Check if node in depth
function maxDepth(d) {
if (rootDepth == undefined) { // If user clicks next to sun = root undefined
rootDepth = 0;
}
return ((d.depth - rootDepth) < 2);
}
//-------------------------------------------------------------------------------------------
function focusOn(d = {
x0: 0,
x1: 1,
y0: 0,
y1: 1
}) {
// Reset to top-level if no data point specified
// Activate top-level node if no data point specified
if (d.data == undefined) {
svg.selectAll(".slice")
.filter(d => d.parent == undefined && d.children != undefined)
.each(function(d) {
activateNode(d);
});
}
root = d; // Root-node
rootDepth = root.depth; // Root node depth for maxDepth(d)
const transition = svg.transition()
.duration(750)
.tween('scale', () => {
const xd = d3.interpolate(x.domain(), [d.x0, d.x1]),
yd = d3.interpolate(y.domain(), [d.y0, 1]);
return t => {
x.domain(xd(t));
y.domain(yd(t));
};
});
transition.selectAll('.slice')
.attr('display', d => maxDepth(d) ? null : 'none'); // Display nodes only in depth for transition
transition.selectAll('path.main-arc')
.filter(d => maxDepth(d))
.attrTween('d', d => () => arc(d));
transition.selectAll('path.hidden-arc')
.filter(d => maxDepth(d))
.attrTween('d', d => () => middleArcLine(d));
transition.selectAll('text')
.filter(d => maxDepth(d))
.attrTween('display', d => () => textFits(d) ? null : 'none'); // Display text only in depth
moveStackToFront(d);
// Foreground nodes -> inner nodes higher than outer nodes
function moveStackToFront(elD) {
svg.selectAll('.slice').filter(d => d === elD)
.each(function(d) {
this.parentNode.appendChild(this);
if (d.parent) {
moveStackToFront(d.parent);
}
})
}
}
//-------------------------------------------------------------------------------------------
// Initialize and Update sun
function sun(pData) {
root = d3.hierarchy(pData); //set data
root.sum(d =>
(d.children == undefined) ? ((d.size == undefined) ? 1 : d.size) : 0 //parent value defined by childrens values
);
const slice = svg.selectAll('g.slice')
.data(
partition(root)
.descendants()
);
slice.exit().remove();
//-------------------------------------------------------------------------------------------
// Enter(), Udpate, Exit()
const newSlice = slice.enter()
.append('g').attr('class', 'slice')
.attr('display', d => d.depth < 2 ? null : 'none') // Hide levels lower depth
.on('dblclick', d => {
focusOn(d);
})
.append('path')
.attr('class', 'main-arc')
.style('fill', d => (d.data.color == undefined) ? color((d.children ? d : d.parent).data.name) : d.data.color) //set source color, otherwise default color
.attr('d', arc)
.append('path')
.attr('class', 'hidden-arc')
.attr('id', (_, i) => `hiddenArc${i}`)
.attr('d', middleArcLine)
const text = newSlice.append('text')
.attr('display', d => d ? null : 'none'); // Hide text on lower levels
text.append('textPath')
.attr('startOffset', '50%')
.attr('xlink:href', (_, i) => `#hiddenArc${i}`)
.text(d => d.data.name) // Set text in sector
.attr('fill', d => 'black');
// Delete Elements
slice.exit().remove();
}
//-------------------------------------------------------------------------------------------
sun(data1)
let i = 0;
d3.interval(() => {
if (i++ % 2 === 0) {
console.log("data2")
sun(data2);
} else {
console.log("data1")
sun(data1);
}
}, 4000)
.slice {
cursor: pointer;
}
.slice .main-arc {
stroke: #fff;
stroke-width: 1px;
}
.slice .hidden-arc {
fill: none;
}
.slice text {
pointer-events: none;
text-anchor: middle;
}
<div id="sunburst"></div>
<script src="https://d3js.org/d3.v4.min.js" charset="utf-8"></script>
<script src="https://unpkg.com/d3fc" charset="utf-8"></script>





