itsNino91 itsNino91 - 6 months ago 23
jQuery Question

Firefox click event buggy

I'm not really sure how to recreate this problem for you guys but I'll do my best to explain it to you. I have a d3 tree layout structure. By clicking on a parent bubble should expand its children and so on. In my code I currently have:

// Enter any new nodes at the parent's previous position.
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; })
.on("mousedown", function(d, event) {
if(event.ctrlKey){
if (d.shortName != undefined) {
modalOpen(d);
}
else{
click(d);
}
}
//event.which is handled in chrome
//event.button is handled in IE10
else if((event.which == 1)||(event.button == 1)||(event == 1)){
click(d);
}
});


FireFox does't recognize event.ctrlKey but will need to. There is one problem. The main problem is the else if statement. I have event.which for chrome, event.button for IE and event in FireFox doesn't seem to give me anymore than an integer. The mouseevent.button property as described on the Mozilla Developer Network page says:

The button number that was pressed when the mouse event was fired:
Left button=0, middle button=1 (if present), right button=2.
For mice configured for left handed use in which the button actions are
reversed the values are instead read from right to left.


I always try to left click, however sometimes I get a value of 0, or 2. If I try to right click I'll get 1, 0, or 2. It's never really consistent. If i do get down to the childmost node and I do a regular click, I get an int of either, 19, 9, 3, 2, 0, or 1. 19 and 9 seem to be the most consistent though in this case. I have no clue why Firefox has to be so difficult but I hope one of you could help me fix this so it doesn't happen every time.

Answer

When setting an event handler in d3, the two passed parameters are not the datum and the event (as you assume), but the datum and the index, like this:

.on("mousedown", function(d, i) { 
    // 'd' is the datum
    // 'i' is the index
})

This is described in the API documentation (https://github.com/mbostock/d3/wiki/Selections#on):

selection.on(type[, listener[, capture]])

Adds or removes an event listener to each element in the current selection, for the specified type. The type is a string event type name, such as "click", "mouseover", or "submit". (Any DOM event type supported by your browser may be used.) The listener is stored by decorating the selected DOM elements using the naming convention "__ontype". The specified listener is invoked in the same manner as other operator functions, being passed the current datum d and index i, with the this context as the current DOM element. To access the current event within a listener, use the global d3.event. The return value of the event listener is ignored.

That would explain why you get random integers - the index will vary depending on which element you click. In order to get the mouse event use the global d3.event, like this:

.on("mousedown", function(d, i) { 
    if(d3.event.ctrlKey){
        // your code here
    }
})

So your final code should look like this:

// Enter any new nodes at the parent's previous position.
var nodeEnter = node.enter().append("g")
    .attr("class", "node")
    .attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; })
    .on("mousedown", function(d, i) { 
        if(d3.event.ctrlKey){
            if (d.shortName != undefined) {
                modalOpen(d);
            } else {
                click(d);
            }
        }
        //event.which is handled in chrome
        //event.button is handled in IE10
        else if((d3.event.which == 1)||(d3.event.button == 1)||(d3.event == 1)){
            click(d);
        }
});