11

我有一个 D3 折线图,我在图表“后面”放置一个矩形。这个矩形附加了一个鼠标事件,但问题是我的图表还有另一个矩形覆盖在图表的“上方”,该图表也附加了事件。

如何让较低的矩形鼠标事件在覆盖在顶部的较高矩形上方冒泡?非常感谢!

我在这里创建了一个小提琴:

http://jsfiddle.net/TnjCC/1/

这是我的代码。查找“这是我需要鼠标悬停冒泡的地方”评论以查看我想要冒泡的元素。

var data = [
  {"date":"1-May-13","close":58.13},
  {"date":"30-Apr-13","close":53.98},
  {"date":"27-Apr-13","close":67.00},
  {"date":"26-Apr-13","close":89.70},
  {"date":"25-Apr-13","close":99.00},
  {"date":"24-Apr-13","close":130.28},
  {"date":"23-Apr-13","close":166.70},
  {"date":"20-Apr-13","close":234.98},
  {"date":"19-Apr-13","close":345.44},
  {"date":"18-Apr-13","close":443.34},
];

var margin = {top: 20, right: 50, bottom: 30, left: 50},
    width = 960 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;

var parseDate = d3.time.format("%d-%b-%y").parse,
    bisectDate = d3.bisector(function(d) { return d.date; }).left,
    formatValue = d3.format(",.2f"),
    formatCurrency = function(d) { return "$" + formatValue(d); };

var x = d3.time.scale()
    .range([0, width]);

var y = d3.scale.linear()
    .range([height, 0]);

var xAxis = d3.svg.axis()
    .scale(x)
    .orient("bottom");

var yAxis = d3.svg.axis()
    .scale(y)
    .orient("left");

var line = d3.svg.line()
    .x(function(d) { return x(d.date); })
    .y(function(d) { return y(d.close); });

var svg = d3.select("body").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
  .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

  data.forEach(function(d) {
    d.date = parseDate(d.date);
    d.close = +d.close;

  data.sort(function(a, b) {
    return a.date - b.date;
  });

  x.domain([data[0].date, data[data.length - 1].date]);
  y.domain(d3.extent(data, function(d) { return d.close; }));

  svg.append("g")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + height + ")")
      .call(xAxis);

  svg.append("g")
      .attr("class", "y axis")
      .call(yAxis)
    .append("text")
      .attr("transform", "rotate(-90)")
      .attr("y", 6)
      .attr("dy", ".71em")
      .style("text-anchor", "end")
      .text("Price ($)");

  <!-- This is where I need the mouseover to bubble up -->
  var left = x(new Date("Apr 23 2013"));
  var right = x(new Date("Apr 26 2013"));
  var wid = right - left;
  svg.append("rect")
      .attr("id", "range")
      .attr("class", "range")
      .attr("x", left)
      .attr("width", wid)
      .attr("height", height)
      .on("mouseover", function () {
          alert("I can see you!");
      })      

  svg.append("path")
      .datum(data)
      .attr("class", "line")
      .attr("d", line);

  var focus = svg.append("g")
      .attr("class", "focus")
      .style("display", "none");

  focus.append("circle")
      .attr("r", 4.5);

  focus.append("text")
      .attr("x", 9)
      .attr("dy", ".35em");

  svg.append("rect")
      .attr("class", "overlay")
      .attr("width", width)
      .attr("height", height)
      .on("mouseover", function() { focus.style("display", null); })
      .on("mouseout", function() { focus.style("display", "none"); })
      .on("mousemove", mousemove);

  function mousemove() {
    var x0 = x.invert(d3.mouse(this)[0]),
        i = bisectDate(data, x0, 1),
        d0 = data[i - 1],
        d1 = data[i],
        d = x0 - d0.date > d1.date - x0 ? d1 : d0;
    focus.attr("transform", "translate(" + x(d.date) + "," + y(d.close) + ")");
    focus.select("text").text(formatCurrency(d.close));
  }
});
4

3 回答 3

15

您还可以使用以下样式为鼠标事件“隐藏”某些 svg 元素。就我而言,这是mouseover我想冒泡的事件:

pointer-events: none;
于 2017-05-11T12:22:04.363 回答
7

为了快速修复,您可以将范围移动到覆盖上方,并从范围中手动调用覆盖事件处理程序。

http://jsfiddle.net/Rk5Hp/

 svg.append("rect")
      .attr("class", "overlay")
      .attr("width", width)
      .attr("height", height)
      .on("mouseover", function() { focus.style("display", null); })
      .on("mouseout", function() { focus.style("display", "none"); })
      .on("mousemove", mousemove);

  // move range above overlay and call the overlay event handlers from there
  svg.append("rect")
      .attr("id", "range")
      .attr("class", "range")
      .attr("x", left)
      .attr("width", wid)
      .attr("height", height)
      .on("mousemove", mousemove)
      .on("mouseout", function() { focus.style("display", "none"); })
      .on("mouseover", function() {
          focus.style("display", null);
          // event handling for range mouseover (alert broke mouse move)
          console.log("I can see you!");
      });

冒泡行为在 dom 级别,由于没有办法让一个 rect 成为另一个 rect 的孩子,所以冒泡不会为你解决这个问题。将元素组合在一起并在组上放置一个检查事件目标的处理程序将使您无法注册事件处理程序两次,但会遇到相同的基本问题:当元素重叠时,无论在源顺序中最后声明的元素都将获得事件。

于 2013-08-20T16:42:21.940 回答
0

以上所有答案都是正确的,但我想再举一个例子:

    let log = console.log
let data = []
let pointStart = document.querySelector("svg").createSVGPoint()
let pointStop = document.querySelector("svg").createSVGPoint()
let divLog = d3.select("#log")
var svg = d3.select("svg")

var linearfn = d3.line()
  .x(d => d.x)
  .y(d => d.y)
  .curve(d3.curveLinear)

function logTagName(eventName, tagName) {
  divLog.html(divLog.html() + eventName + " : " + tagName + "<br/>")
}

svg.on("click", function() {
    log("tagName: ", event.target.tagName)
    logTagName("svg click", event.target.tagName)
    pointStart.x = event.x - 8
    pointStart.y = event.y - 8

    data.push({
      x: pointStart.x,
      y: pointStart.y
    })

    svg.selectAll("path") // SVG içinde tanımlı path elemanlarını bul
      .data([1]).enter() // 1 elemanlı dizinin eleman sayısı kadarı için enter()
      .append('path') // dizi elemanı kadar path oluştur
      .attr("fill", "none")
      .attr("stroke", "black")
      .attr("stroke-width", 8)
      .attr("d", linearfn(data))
      .on("click", function() {
        log("tagName: ", event.target.tagName)
        logTagName("path click", event.target.tagName)
        /* click event will start from path and pass to the svg element */
        // event.stopPropagation(); // letting pass event bubbling 
      })

    svg.selectAll("circle")
      .data(data)
      .enter()
      .append("circle")
      .attr("cx", d => d.x + .5)
      .attr("cy", d => d.y + .5)
      .attr("r", 20)
      .attr("stroke-width", 3)
      .attr("stroke", "red")
      .attr("cursor", "move")
      .style("fill", "transparent")
      .attr("pointer-events", "all") // when clicked in/outside of circle, it'll handle events
      .on("mouseover", function() {
        log("over oldu")
        d3.select(this).style("stroke", "blue");
      })
      .on("mouseout", function() {
        log("out oldu")
        d3.select(this).style("stroke", "red");
      })
      .on("click", function() {
        event.stopPropagation(); // not letting pass event bubbling
        event.preventDefault();
        log("click oldu")
        d3.select(this).style("stroke", "black");
      })
  })
  .on("mousemove", function() {
    // fare hareketinde de çizdireceğiz ama x,y noktasını 
    // tıklanıncaya kadar diziye eklemeyeceğiz
    pointStop.x = event.x - 8
    pointStop.y = event.y - 8

    svg.select("path")
      .attr("d", linearfn(data.concat({
        x: pointStop.x,
        y: pointStop.y
      })))
  })

https://jsfiddle.net/jsfiddleCem/hnsu68jw/

于 2019-07-21T14:13:28.493 回答