简体   繁体   中英

Adding an event listener to a d3 brush

I'm trying to figure out how to make the rectangle created by a d3 brush (the events rectangle, in particular), respond to a click event. Eventually, I'd like to have a single click on this object bring up a menu, but I can't seem to get the rect element to catch the event.

I've tried the following code:

var selector = d3.svg.brush();

var x = d3.scale.linear()
    .range([0,500])
    .domain([0,500]);

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

d3.select('#myDiv')
    .append('svg')
    .attr('id','mySVG')
    .attr('height',500)
    .attr('width',500)
    .call(selector.x(x).y(y).on('brushend',bindSelect))

function bindSelect(){
    d3.select('#mySVG rect.extent')
        .on('click',function(){alert('hi mom!')})
}

bindSelect seems to fire just fine, and successfully select the .extent rectangle, but I'm not getting any response on clicking the rectangle.

The problem seems to be that d3 fires a brushend event when the rectangle is clicked, which I'm guessing stops event propagation somewhere?

Does anyone know how to get around this? My only thought is to create another rect on top of the extents rect on the brushend event, and then use that one to handle click behavior, but it seems like a messy way of doing things.

Also, here is a fiddle of the code.

Getting the event you want might be a bit hairy and require changes to the D3 source code. However, you can use the brushend event together with the methods provided for the brush to figure out whether somebody has clicked on the brush rectangle.

var extent = selector.extent();

function bindSelect(){
  if(!selector.empty() && !(extent < selector.extent() || extent > selector.extent())) {
    console.log("foo");
  }
  extent = selector.extent();
}

The idea is that if the selection is not empty and the selected extent is the same as it was at the last brushend event, then the user clicked on the selection instead of manipulating it.

This is a slightly improved version of Lars's answer, to handle two edge cases:

  1. If you drag the brush and return it to it's exact original position, this will trigger the event.
  2. If you drag the brush past the end of the container, the brush will not move while your cursor does, also triggering the event.

First, set up your brush like this:

selector.x(x).y(y).on('brushstart', saveCursorPos)
                  .on('brushend',   checkIfMoved))

Then,

var clickPos = [-1,-1];

function saveCursorPos() {
    var e = d3.event.sourceEvent;
    clickPos = [e.clientX,e.clientY];
}

function checkIfMoved() {
    var e = d3.event.sourceEvent;
    if(!selector.empty() && clickPos[0] === e.clientX && clickPos[1] === e.clientY) {
        alert("foo");
    }
}

The idea being to save the mouse position as opposed to the brush extent when you start dragging, and check if that has changed when the button is released.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM