Foreground elements consume all mouse events

3,075 views
Skip to first unread message

Chris Pudney

unread,
May 8, 2012, 9:30:30 PM5/8/12
to d3...@googlegroups.com
G'day,

I've created a lap chart visualization that highlights specific elements on mouseover (and unhighlights on mouseout).

http://bl.ocks.org/d/1509502/

I'm working on adding a zoom feature that works by clicking anywhere within the chart.  I've implemented this by adding a (transparent) rect that listens for mouse click events.

If the rect is in front of the chart then it seems to consume/block all mouse events and the mouseover listeners never get triggered.

If the rect is at the back of the chart then its mouse click listener only gets triggered when clicking on the background.

http://bl.ocks.org/d/2040990/

I could work around this by carefully positioning the rect at the right depth and adding listeners for mouse clicks to the elements that are triggering highlighting but this seems a bit unwieldy.

Is there are more elegant way of getting specific elements to listen for mouseovers and the entire chart listen for mouse clicks?

Thanks,
Chris.

I could wor


Bart Posselt

unread,
May 8, 2012, 10:06:41 PM5/8/12
to d3...@googlegroups.com
I ran into a similar problem with the svg.brush and right-clicking the
mouse. Since it was added to the DOM after everything else on the
graph I was making, it captured all mouse input, including the
contextmenu events. What I wound up doing was detecting the d3.event
in the brushstart method, which selected all elements with a specific
class.

Maybe you could try something similar but with selecting the
background class instead and catching specific mouse behaviors.

Bart

Bart

unread,
May 8, 2012, 10:17:00 PM5/8/12
to d3-js
Here's some code for what I was trying to explain. BTW, this only
works with d3.v2.

svg.append("g")
.attr("class", "brush")
.call(d3.svg.brush().x(x).y(y)
.on("brushstart", brushstart)
.on("brush", brush)
.on("brushend", brushend)
);

function brushstart() {
if (!rightClick()) {
svg.classed("selecting", true);
} else { //block selecting behavior and show the context menu
console.log('right click');
contextmenu.show();
}
}

function rightClick() {
if (d3.event.sourceEvent.which == 3 || d3.event.sourceEvent.button
== 2) { //3==firefox, 2==ie
return true;
} else {
return false;
}
}


Bart

Scott Murray

unread,
May 9, 2012, 10:41:24 AM5/9/12
to d3...@googlegroups.com
> Is there are more elegant way of getting specific elements to listen for mouseovers and the entire chart listen for mouse clicks?


Chris, have you seen the CSS pointer-events property?

https://developer.mozilla.org/en/CSS/pointer-events

That lets you pass mouse events "through" elements in front to elements behind.

However, I don't think it lets you pick and choose which events to capture or pass through, so that may not be helpful.


> I could work around this by carefully positioning the rect at the right depth and adding listeners for mouse clicks to the elements that are triggering highlighting but this seems a bit unwieldy.

That's the only way I know how to do it. Similar to regular HTML elements, just expect that whatever is in front to capture the events. So if you have a button, obviously keep it in front of any other elements. Otherwise it will be obscured and unclickable.


I'd love to be proven wrong, though, if anyone has ideas on managing event capture independent of z-index/visual position!

Scot

Chris Pudney

unread,
May 13, 2012, 9:14:25 PM5/13/12
to d3...@googlegroups.com
G'day,


Here's some code for what I was trying to explain. BTW, this only works with d3.v2.

Thanks Bart - that looks useful.

Regards,
Chris.

Chris Pudney

unread,
May 13, 2012, 9:17:55 PM5/13/12
to d3...@googlegroups.com
G'day,


> Chris, have you seen the CSS pointer-events property? https://developer.mozilla.org/en/CSS/pointer-events

No I hadn't - that could come in handy.


>> I could work around this by carefully positioning the rect at the right depth and adding listeners for mouse clicks to the elements that are triggering highlighting but this seems a bit unwieldy.
> That's the only way I know how to do it.  Similar to regular HTML elements, just expect that whatever is in front to capture the events.

Seems to be the case.

Thanks,
Chris.

Chris Pudney

unread,
May 14, 2012, 8:53:47 PM5/14/12
to d3...@googlegroups.com
G'day,

So following Bart's suggestion I added all elements that are mouseover listeners to a "zoom" class and then added a click listener to this class:

    // Listen for clicks -> zoom.
    vis.selectAll('.zoom')
        .on("click", function() {

            toggleZoom(vis, d3.mouse(this)[0]);
        });

I also added a background rect to capture clicks that weren't on any foreground element:

   // Background rect to catch zoom clicks.
    vis.append('svg:rect')
        .attr('class', 'zoom')
        .attr('x', 0)
        .attr('y', 0)
        .attr('width', WIDTH)
        .attr('height', HEIGHT)
        .style('opacity', 0.0);

Seems to work well: clicks (toggle) zoom and mouseover higlights

http://bl.ocks.org/d/2040990/

Thanks,
Chris.
Message has been deleted
Message has been deleted

Marc Fawzi

unread,
Oct 5, 2012, 3:02:11 PM10/5/12
to d3...@googlegroups.com, d3...@googlegroups.com
Have u tried setting CSS style "pointer-events: none" on the div where u want events to go thru ?

Sent from my iPhone

On Oct 5, 2012, at 11:56 AM, Dan Andreescu <dan.an...@gmail.com> wrote:

Thanks for this thread.  I had an additional problem that others here might be able to relate to.  I made a chart with a d3.brush that zooms in on the selected area on brushend.  Then, when the user double clicks, the zoom is reset:

    zoomBrush = d3.svg.brush().x(...).on('brushend', handleZoom);
    zoomBrushContainer.call(zoomBrush);
    ...
    svgElement.on('dblclick', handleUnzoom);

The problem is, my brush's container (zoomBrushContainer in the code above) traps the click event for its own uses.  So the only way I could figure to solve it so far was to hide the brush on zoom and show it again on unzoom:

    function handleZoom () {
        ...
        $(zoomBrushContainer).hide();
    }
    function handleUnzoom () {
        $(zoomBrushContainer).show();
    }

So if anyone has a more elegant solution, I'd be grateful.  Otherwise, hopefully this hack helps others :)

Dan Andreescu

unread,
Oct 5, 2012, 3:02:41 PM10/5/12
to d3...@googlegroups.com
Thanks for this thread.  I had an additional problem that others here might be able to relate to.  I made a chart with a d3.brush that zooms in on the selected area on brushend.  Then, when the user double clicks, the zoom is reset:

    zoomBrush = d3.svg.brush().x(...).on('brushend', handleZoom);
    zoomBrushContainer.call(zoomBrush);
    ...
    svgElement.on('dblclick', handleUnzoom);

The problem is, my brush's container (zoomBrushContainer in the code above) traps the click event for its own uses.  So the only way I could figure to solve it so far was to hide the brush on zoom and show it again on unzoom:

    function handleZoom () {
        ...
        $(zoomBrushContainer).toggle(false);
    }
    function handleUnzoom () {
        $(zoomBrushContainer).toggle(true);
    }
Reply all
Reply to author
Forward
0 new messages