Clox
Clox

Reputation: 1943

Stop propagation for a specific handler

Let's say I have custom dropdown(). When the button is clicked I want to bring up the menu, and when the user clicks outside of the menu I want it to close. So I do something like this:

$(myDropDown).mousedown(dropDownMouseDown);
$("html").mousedown(htmlMouseDown,myDropDown);
function dropDownMouseDown(event) {
    event.target.open();
    event.stopPropagation();//I need this line or else htmlMouseDown will be called immediately causing the dropDown-menu to close right before its opened
}
function htmlMouseDown() {
    this.close();
}

Well, this works. But what if I add two of these? If I click to open the first, then the same on the second then both will be open because dropDownMouseDown stops the propagation so that htmlMouseDown never gets called for the first. How do I get around this? If I only had these two then adding some logic for that would of course be easy, but if the quantity is dynamic? Also I might not want to call event.stopPropagation() because it will do strange stuff to other libraries I'm using which listen for that event too? I also tried putting this line: $("html").mousedown(htmlMouseDown,myDropDown) inside the dropDownMouseDown-handler but it will be called immediately anyway once the bubbling reaches the html-element.

Upvotes: 4

Views: 1123

Answers (3)

Clox
Clox

Reputation: 1943

Thanks for the answers. They're really appreciated. I did figure out a way of doing it that I'm satisfied with. Here's how:

$(myDropDown).mousedown(dropDownMouseDown);
$("html").mousedown(myDropDown,htmlMouseDown);//Pass in the dropDown as the data argument, which can then be accessed by doing event.data in the handler
function dropDownMouseDown(event) {
    event.target.open();
}
function htmlMouseDown(event) {
    if (event.target!=event.data)//event.target is the element that was clicked, event.data is set to the dropdown that this handler was added for. Unless these two elements are the same then we can...
        event.data.close();///close the dropdown this handler was added for
}

Can't believe I didn't think of that. In my case though the element that opens/closes has child-elements so event.target could be one of the child elements instead of the element that the handler was attached to. So I changed my html-element-handler to this:

    function htmlMouseDown(event) {
       var element=event.target;
        while (element) {
            if (element==event.data)
                return;
            element=element.parentElement;
        }
        event.data.hide();
    }

Upvotes: 0

jbl
jbl

Reputation: 15413

Assuming you have a selector for your dropdows, (let's say ".dropdown"), I would try to use '.not()'

$('.dropdown').mousedown(dropDownMouseDown);

$("html").on('mousedown', htmlMouseDown);

function dropDownMouseDown(event) {
    event.target.open();
}

function htmlMouseDown(event) {
  $('.dropdown').not($(event.target)).close();
}

Here is a fiddle in the same idea with css classes : http://jsfiddle.net/eFEL6/4/

Upvotes: 2

Frederik.L
Frederik.L

Reputation: 5620

What about using a variable that contains the last openened one ? There are probably many other ways of doing this, but here is a way I could think of:

var lastOpened = null; // initially nothing is open (unless something is)

Then:

function dropDownMouseDown(event) {
    if (lastOpened != null) { // if one is still open
        lastOpened.close(); // close it
        lastOpened = null; // nothing is open anymore
    }

    event.target.open();
    lastOpened = event.target; // now this one is open
    event.stopPropagation();
}

function htmlMouseDown() {
    this.close();
    lastOpened = null; // nothing is open
}

That should work in a way that the last opened one always close itself before opening a new one.

Upvotes: 1

Related Questions