通过重叠的 svg 元素传播事件

Propagating events through overlapping svg elements

我有两个重叠的 svg.g 组,有不同的 onclick 事件。我使用 opacity 属性定期将组混合到可视化中或从可视化中混合出来。目前,只调用了渲染在顶部的组的 onclick 事件,但我想调用当前可见的组的事件。或者,我总是可以调用这两个事件并在被调用函数中使用条件语句,该语句取决于 opacity 属性。

这是一个例子

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>

</head>
<body>
    <div id="body"></div>
    <script type="text/javascript">

    var canvas_w = 1280 - 80,
        canvas_h = 800 - 180;

    var svg = d3.select("#body").append("div")
        .append("svg:svg")
        .attr("width", canvas_w)
        .attr("height", canvas_h)   

    var visible_group = svg.append("g")
        .attr("opacity", 1)
        .on("click", function(d){console.log("Click")})
        .append("rect")
        .attr("x", 0)
        .attr("y", 0)
        .attr("width", 100)
        .attr("height", 100)
        .style("fill", "blue");

    var invisible_group = svg.append("g")
        .attr("opacity", 0)
        .on("click", function(d){console.log("Invisiclick")})
        .append("rect")
        .attr("x", 0)
        .attr("y", 0)
        .attr("width", 100)
        .attr("height", 100)
        .style("fill", "red");


    </script>
</body>
</html>

此代码将呈现一个蓝色矩形,即可见组。带有红色矩形的组被隐藏。如果你点击蓝色矩形,"Invisiclick"会被打印到控制台,隐藏组的onclick事件。我想将 "Click" 打印到控制台,或者同时打印 "Invisiclick" 和 "Click".

我该怎么做?

如果使用样式 visibility rather than the attribute opacity to set the groups as hidden or visible, you can also use the style pointer-events 将事件限制为可见元素。

var canvas_w = 1280 - 80,
    canvas_h = 800 - 180;

var svg = d3.select("#body").append("div")
    .append("svg:svg")
    .attr("width", canvas_w)
    .attr("height", canvas_h)   

var visible_group = svg.append("g")
    .style("visibility", "visible")
    .style("pointer-events", "visible")
    .on("click", function(d){console.log("Click")})
    .append("rect")
    .attr("x", 0)
    .attr("y", 0)
    .attr("width", 100)
    .attr("height", 100)
    .style("fill", "blue");

var invisible_group = svg.append("g")
    .style("visibility", "hidden")
    .style("pointer-events", "visible")
    .on("click", function(d){console.log("Invisiclick")})
    .append("rect")
    .attr("x", 0)
    .attr("y", 0)
    .attr("width", 100)
    .attr("height", 100)
    .style("fill", "red");


</script>

当您单击蓝色矩形时,此示例将 "Click" 打印到控制台。

不透明度确实使元素半透明,但不会使它们消失。就像您可以点击一块玻璃一样,您可以使用 opacity:0.

单击一个元素

现在,根据两个视图中形状是否不同,有两种选择。如果它们 不是 (例如,您正在绘制世界地图,国家/地区保持不变,只是颜色发生变化),则听最顶层可能最简单,然后运行 if 语句要执行的部分。像这样

    var state = "blue";


    var clickHandler = function() {
        if(state === "blue") {
            console.log("Blue clicked");
        } else {
            console.log("Red clicked");
        }
    }


    var toggleState = function() {
        state = (state === "blue") ? "red" : "blue";
    }

    var updateDisplay = function() {
        blueGroup
            .transition()
            .duration(400)
            .attr("opacity", state === "blue" ? 1 : 0);
        redGroup
            .transition()
            .duration(400)
            .attr("opacity", state === "red" ? 1 : 0);
    }

    var canvas_w = 1280 - 80,
        canvas_h = 120;

    var svg = d3.select("#body").append("div")
        .append("svg:svg")
        .attr("width", canvas_w)
        .attr("height", canvas_h)   

    var blueGroup = svg.append("g")
        .append("rect")
        .attr("opacity", 1)
        .attr("x", 0)
        .attr("y", 0)
        .attr("width", 100)
        .attr("height", 100)
        .style("fill", "blue");

    var redGroup = svg.append("g")
        .on("click", clickHandler)
        .append("rect")
        .attr("opacity", 0)
        .attr("x", 0)
        .attr("y", 0)
        .attr("width", 100)
        .attr("height", 100)
        .style("fill", "red");



    d3.select("button").on("click", function() {
        toggleState();
        updateDisplay();
    });
<script src="https://samizdat.cz/tools/d3/3.5.3.min.js" charset="utf-8"></script>
<div id="body"></div>
<button>change!</button>

如果 形状发生变化,您需要先使用 opacity:0 使元素半透明,然后 display:none使它们消失(否则会瞬间闪出)。另一种选择是 pointer-events, but only if you don't need to support old browsers。 过渡将如下所示:

var state = "blue";

var toggleState = function() {
    state = (state === "blue") ? "red" : "blue";
}

var updateDisplay = function() {
    blueGroup
        .style("display", state === "blue" ? "block" : "none")
        .transition()
        .duration(400)
        .attr("opacity", state === "blue" ? 1 : 0)
        .each("end", function() {
            blueGroup.style("display", state === "blue" ? "block" : "none");
        });
        
    redGroup
        .style("display", state === "red" ? "block" : "none")
        .transition()
        .duration(400)
        .attr("opacity", state === "red" ? 1 : 0)
        .each("end", function() {
            redGroup.style("display", state === "red" ? "block" : "none");
        });
}

var canvas_w = 1280 - 80,
    canvas_h = 120;

var svg = d3.select("#body").append("div")
    .append("svg:svg")
    .attr("width", canvas_w)
    .attr("height", canvas_h)   

var blueGroup = svg.append("g")
    .on("click", function() {
        console.log("Blue clicked");
    })
    .append("rect")
    .attr("opacity", 1)
    .attr("x", 0)
    .attr("y", 0)
    .attr("width", 150)
    .attr("height", 100)
    .style("fill", "blue");

var redGroup = svg.append("g")
    .on("click", function() {
        console.log("Red clicked");
    })
    .append("rect")
    .attr("opacity", 0)
    .style("display", "none")
    .attr("x", 0)
    .attr("y", 0)
    .attr("width", 100)
    .attr("height", 120)
    .style("fill", "red");



d3.select("button").on("click", function() {
    toggleState();
    updateDisplay();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="body"></div>
<button>change!</button>

请注意,在每次转换时,我们现在必须以正确的顺序处理 opacity display。另请注意,现在我们在 both rects.

上都有监听器

如果可以与 .enter().exit() 选择一起使用,该示例会更简单一些,因为您可以去掉 .on("end") 而使用 .remove() 退出转换。

更新:实际上与 display:none 相同的也是 visibility: hidden