javascriptmacosd3.jssafari

Hover Issue with D3 Ribbon Path in Safari


I'm experiencing an issue with D3.js ribbons in Safari where certain SVG path elements don't respond to hover events for styling changes. The hover effect, such as changing the fill style, works perfectly in Chrome and Firefox. However, in Safari, some paths seem to completely ignore the hover interaction.

I've tried all possible pointer-events values (all, auto, visiblePainted, etc.), but it still doesn't work. Here's what I've already verified:

Here’s how I’m implementing the path drawing:

// Dimensions and container setup
const width = 600, height = 600;
const svg = d3.select("#circos-plot")
    .attr("width", width)
    .attr("height", height)
    .append("g")
    .attr("transform", `translate(${width / 2},${height / 2})`);

// Updated data with new angles
const data = {
    source: { radius: 200, startAngle: 0.10082134423424959, endAngle: 0.23383773956851167 },
    target: { radius: 200, startAngle: 1.0174690537411528, endAngle: 1.0615254394569593 }
};

// Ribbon generator
const ribbon = d3.ribbon().radius(data.source.radius);

// Append ribbon with hover event
svg.append("path")
    .datum(data)
    .attr("d", ribbon)
    .attr("fill", "steelblue")
    .attr("class", "ribbon")  // Add class for hover styling
    .style("pointer-events", "visibleFill")  // Ensure pointer events work
    .on("mouseover", function(event, d) {
      // Change color on hover
      d3.select(this).transition().duration(300).attr("fill", "orange");
    })
    .on("mouseout", function(event) {
      // Reset color when mouse leaves
      d3.select(this).transition().duration(300).attr("fill", "steelblue");
    });

Below is the complete working example:

https://codepen.io/joko3ono/pen/VYZPwWa

And here is the GIF showcasing the bug. gif

Is there any specific code or adjustments needed to make the SVG behave correctly in Safari, similar to how it works in other browsers?


Solution

  •         // Dimensions and container setup
            const width = 600, height = 600;
            const svg = d3.select("#circos-plot")
                .attr("width", width)
                .attr("height", height)
                .append("g")
                .attr("transform", `translate(${width / 2},${height / 2})`);
    
            // Data for the ribbon
            const data = {
                source: { radius: 200, startAngle: 0.1, endAngle: 0.23 },
                target: { radius: 200, startAngle: 1.01, endAngle: 1.06 }
            };
    
            // Ribbon generator
            const ribbon = d3.ribbon().radius(data.source.radius);
    
            // 1. Add the visible ribbon path
            const ribbonPath = svg.append("path")
                .datum(data)
                .attr("d", ribbon)
                .attr("fill", "steelblue")
                .attr("class", "ribbon");
    
            // 2. Add an invisible overlay path for hover interaction
            const invisibleRibbonPath = svg.append("path")
                .datum(data)
                .attr("d", ribbon)
                .attr("fill", "none") // No fill for the overlay
                .attr("stroke", "transparent") // Invisible stroke
                .attr("stroke-width", 20) // Expand the hoverable area
                .style("pointer-events", "stroke") // Interact only with the stroke
                .on("mouseover", function(event) {
                    ribbonPath.transition().duration(300).attr("fill", "orange");
                })
                .on("mouseout", function(event) {
                    ribbonPath.transition().duration(300).attr("fill", "steelblue");
                });
    .ribbon {
                transition: fill 0.3s ease;
            }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.9.0/d3.min.js"></script>
    <svg id="circos-plot"></svg>