I have a D3 line chart where I'm placing a rect 'behind' the chart. This rect has a mouse event attached to it, but the problem is my chart also has another rect overlaid 'above' the chart that also has events attached to it.
How do I get the lower rect mouse events to bubble up above the higher rect that is overlayed on top? Thanks so much!
I've created a Fiddle here:
http://jsfiddle.net/TnjCC/1/
And here is my code. Look for the "This is where I need the mouseover to bubble up" comment to see which element I'd like to bubble up.
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));
}
});
For a quick fix, you can move the range above the overlay and manually call the overlay event handlers from the range.
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!");
});
Bubbling acts at the dom level, and since there is no way to have a rect be a child of another rect, bubbling will not take care of this for you. Grouping the elements together and placing a handler that checks the event target on the group will keep you from registering the event handler twice, but suffers from the same basic problem: when elements overlap, whichever element is declared last in the source order will get the event.
You can also use the following style, to "hide" certain svg elements for mouse events. In my case, it was the mouseover
event, that I wanted to bubble through:
pointer-events: none;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With