D3.js Error: Cannot read properties of undefined (reading 'stopPropagation')

28 views Asked by At

I am currently in the process of creating a sunburst chart with d3.js according to these instructions "https://www.demo2s.com/javascript/javascript-d3-js-dynamically-update-sunburst-if-source-json-is-updated.html".

I used the d3.v4 library for this.

Now that everything is working, I wanted to use the new version d3.v7.

But when I change the source to d3.v7, I get the following error "Cannot read properties of undefined (reading 'stopPropagation')".

I assume that this function is no longer supported in the new library. But how do I replace this function with v7? If I change the source from v4 to v7 and comment out the line with "d3.event.stopPropagation();", the dbclick event no longer works. If im using d3.v4 everything works fine.

Any Ideas?

            // 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
                }]
            }]
            };

            function renderPieChart(d3Instance, dataToUse){
                //-------------------------------------------------------------------------------------------            
                // Declare variables
                let 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.schemeCategory10),
                    maxRadius = (Math.min(width, height) / 2) - 5,
                    l_highligh_selection = 'Y',
                    lastActivatedNode;
                    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]);

                //-------------------------------------------------------------------------------------------
                // Text-fit constant
                const textFits = d => {
                        const CHAR_SPACE = 6;

                        const deltaAngle = x(d.x1) - x(d.x0);
                        const r = Math.max(0, (y(d.y0) + y(d.y1)) / 2);
                        const perimeter = r * deltaAngle;

                        return d.data.name.length * CHAR_SPACE < perimeter;
                };

                //-----------------------------------------------------------------------------------
                /* Deselect last Node*/
                function deactivateNode(elD) {
                        lastActivatedNode.selectAll('.main-arc')
                                            .style('stroke', null)
                                            .style('stroke-width', null)
                    
                        lastActivatedNode.selectAll('text')
                                            .style('font-weight', 'normal');

                        lastActivatedNode = null;
                    };

                //-----------------------------------------------------------------------------------
                // 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);
                }

                //------------------------------------------------------------------------------------
                // Visually select a node
                function activateNode(elD){
                    // If other node has been activated -> deactivated node    
                    // Find clicked node
                    var nodeSlice = svg.selectAll(".slice")
                                .filter(d => d === elD);
                    
                    // Set lastActivatedNode for upcoming deactivation
                    lastActivatedNode = nodeSlice;
                    
                    // Foreground node  
                    nodeSlice.each(function(d) {
                            this.parentNode.appendChild(this)
                        });

                    // Add highlighting 
                    nodeSlice.selectAll('.main-arc')
                    .style('stroke', '#000')
                    .style('stroke-width', '2px')

                    // Highlight text
                    nodeSlice.selectAll('text')
                    .style('font-weight', 'bold'); 
                }

                //-------------------------------------------------------------------------------------------
                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()
                        );

                    //-------------------------------------------------------------------------------------------
                    // 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 => {
                                                            d3.event.stopPropagation();
                                                            focusOn(d);
                                                            })
                                            .on('mouseover', d => {if(l_highligh_selection == 'Y') activateNode(d);})
                                            .on('mouseleave', d => {if(l_highligh_selection == 'Y') deactivateNode(d);});

                    newSlice.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);

                    newSlice.append('path')
                                .attr('class', 'hidden-arc')
                                .attr('id', (_, i) => `hiddenArc${i}`)
                                .attr('d', middleArcLine);

                    const text = newSlice.append('text')
                                            .attr('display', d => textFits(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(dataToUse);
            return d3;
            }

            let d3Instance = renderPieChart(null, data1);
.slice {
    cursor: pointer;
}

.slice .main-arc {
    stroke: #fff;
    stroke-width: 1px;
}

.slice .hidden-arc {
    fill: none;
}

.slice text {
    pointer-events: none;
    text-anchor: middle;
}
<!DOCTYPE html>
<html>             
    <head>              
        <link rel="stylesheet" href="sun.css">
        <meta charset="utf-8" />   
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />   
        <title> 1.Grundgerüst </title>
    </head>
    <body>     
    <div id="sunburst"></div>
    <script src="https://d3js.org/d3.v7.js" charset="utf-8"></script>
    <!-- <script src="https://d3js.org/d3.v4.min.js" charset="utf-8"></script> -->
    </body> 
</html> 

0

There are 0 answers