Handling Mouse Events in Overlapping SVG Layers

前端 未结 2 2002
别跟我提以往
别跟我提以往 2021-02-15 01:04

I am building a map visualization with d3.js. I am drawing filled polygons for both the US states and counties. The SVG layer for counties is under the layer for states. The sta

相关标签:
2条回答
  • 2021-02-15 01:20

    Here's a d3 function which does what you want.

    It passes events to lower layers by temporarily disabling pointer events on the top layer, and manually triggering the mouse event on the next layer.

    function passThruEvents(g) {
        g   .on('mousemove.passThru', passThru)
            .on('mousedown.passThru', passThru)
        ;
    
        function passThru(d) {
            var e = d3.event;
    
            var prev = this.style.pointerEvents;
            this.style.pointerEvents = 'none';
    
            var el = document.elementFromPoint(d3.event.x, d3.event.y);
    
            var e2 = document.createEvent('MouseEvent');
            e2.initMouseEvent(e.type,e.bubbles,e.cancelable,e.view, e.detail,e.screenX,e.screenY,e.clientX,e.clientY,e.ctrlKey,e.altKey,e.shiftKey,e.metaKey,e.button,e.relatedTarget);
    
            el.dispatchEvent(e2);
    
            this.style.pointerEvents = prev;
        }
    }
    
    0 讨论(0)
  • 2021-02-15 01:35

    If you want to do something when you click a state, like changing the border style, or the opacity of the shape, you can use the fact that every county belongs to a state and just group the counties in states, capturing the 'click' event in the county, select the corresponding state element and change its visual attributes. A mockup of this strategy:

    // Setup the visualization, assuming that you have a div with id 'chart'
    var width = 300,
        height = 300,
        div = d3.select('#chart'),
        svg = div.append('svg')
            .attr('width', width)
            .attr('height', height);
    
    // Array of states, each one containing one or more counties. In this example,
    // the states are rectangles, but you get the idea
    var states = [
        {
            width: 300,
            height: 300,
            name: 'Fake State',
            counties: [
                {x:   5, y: 5, width:  95, height: 290, fill: '#b4a0a0'},
                {x: 100, y: 5, width: 150, height: 290, fill: '#b4c0a0'},
                {x: 250, y: 5, width:  45, height: 290, fill: '#a4a0c0'}
            ]
        }];
    
    // Create a group for each state, with class state
    var gstates = svg.selectAll('g.state')
        .data(states)
        .enter()
        .append('g')
        .classed('state', true);
    
    // Add the state background, in this case, a transparent rectangle
    gstates
        .append('rect')
        .classed('state', true)
        .attr('width', function(d) { return d.width; })
        .attr('height', function(d) { return d.height; })
        .attr('fill', '#444')
        .attr('fill-opacity', 0.0);
    
    // For each group, add rectangles for each county, binding them to the
    // county array of each state.
    gstates.selectAll('rect.county')
        .data(function(d) { return d.counties; })
        .enter()
        .append('rect')
        .classed('county', true)
            .attr('x', function(d) { return d.x; })
            .attr('y', function(d) { return d.y; })
            .attr('width', function(d) { return d.width; })
            .attr('height', function(d) { return d.height; })
            .attr('fill', function(d) { return d.fill; })
            .on('mouseover', function() {
                d3.select(this).attr('fill-opacity', 0.5);
            })
            .on('mouseout', function() {
                d3.select(this).attr('fill-opacity', 0.9);
            })
            .on('click', function() {
                // Retrive the parent group of each county, and then select
                // the shape corresponding to the state and alter its properties
                // you can also access the state data
                var parent = d3.select(d3.select(this).node().parentNode),
                    state = parent.select('rect.state');
                    // Output 'Fake State'
                    console.log(parent[0][0].__data__.name);
                    state.attr('fill-opacity', 1.0);
            });
    

    There is a working fiddle here: http://jsfiddle.net/pnavarrc/PGTCM/5/. Regards,

    0 讨论(0)
提交回复
热议问题