jQuery: fire click() before blur() event
Asked Answered
H

5

141

I have an input field, where I try to make autocomplete suggestion. Code looks like

<input type="text" id="myinput">
<div id="myresults"></div>

On input's blur() event I want to hide results' div:

$("#myinput").live('blur',function(){
     $("#myresults").hide();
});

When I write something into my input I send request to server and get json response, parse it into ul->li structure and put this ul to my #myresults div.

When I click to this parsed li element I want to set value from li to input and hide #myresults div

$("#myresults ul li").live('click',function(){
     $("#myinput").val($(this).html());
     $("#myresults").hide();
});

Everything is going good, but when I click to my li blur() event fires before click() and input's value don't get li's html.

How can I set up click() event before blur()?

Hegira answered 18/5, 2012 at 12:41 Comment(6)
so that case why you use blur function ? when your need is already fullfilled from click function.Buddy
This probably won't change the order that your events are firing, but if blur is really firing after you click on an li, then you probably don't need to execute $("#myresults").hide() in the click handler. What it seems like is happening is that $(this).html() is returning an empty string. Could you log or alert $(this).html() to make sure?Trephine
@OwaisIqbal sometimes there's no results, sometimes user don't want to select anything from suggestions, sometimes user don't want to use this input anymore, but I must hide unused resultHegira
@Trephine there're many actions in my handlers, I've just minimized them to easy understanding the problem in my question. p.s. I've tried to add watch-point in firebug on blur() and click() handlers, there's no actions after blur() endsHegira
It looks like you need to queue the functions. Try taking a look at this page: #1058658. The First-In-First-Out (FIFO) portion may pertain to youCrater
I'm not sure what might be happening since you have more event handlers than what you're showing. Here is a jsfiddle doing what it looks like you are wanting; does it approximate the behavior you're trying to create? I'm calling show() on blur to give something to click on. Can you create the problem there?Trephine
M
331

Solution 1

Listen to mousedown instead of click.

The mousedown and blur events occur one after another when you press the mouse button, but click only occurs when you release it.

Solution 2

You can preventDefault() in mousedown to block the dropdown from stealing focus. The slight advantage is that the value will be selected when the mouse button is released, which is how native select components work. JSFiddle

$('input').on('focus', function() {
    $('ul').show();
}).on('blur', function() {
    $('ul').hide();
});

$('ul').on('mousedown', function(event) {
    event.preventDefault();
}).on('click', 'li', function() {
    $('input').val(this.textContent).blur();
});
Multiply answered 18/5, 2012 at 13:2 Comment(8)
Solution 1 worked for me in this same situation! Easy change! Thank you :)Rig
Thank you :) But can you explain why listening to mousedown instead of click works like charm ?Lenhart
@MudassirAli because mousedown event comes before blur, and click comes after. jsfiddle.net/f3BKPMultiply
Solution #1 is in my opinion the better choice. Simple and effective. Timeout would mean more to code + more room for unexpected behavior (how long a time out? long enough to register the click, but not too long to confuse the use...).Arbitral
@Arbitral You're right, timeout would be too unreliable. I've updated the answer with a better alternative solution.Multiply
Solution #2 is ingenious. I would have never figured it out! ThanksForeboding
I got stumped translating this solution to angular's attribute based events but I found that if I preventDefault on mousedown on the same element that was failing to be clicked due to a blur event somewhere else, it works. Like this: <li (click)="handler()" (mousedown)="$event.preventDefault()"></li>Mangrove
I had a similar issue on a react app and the second option worked just fine, thank you so much! –Donothingism
N
2
$(document).on('blur', "#myinput", hideResult);

$(document).on('mousedown', "#myresults ul li", function(){
     $(document).off('blur', "#myinput", hideResult); //unbind the handler before updating value
     $("#myinput").val($(this).html()).blur(); //make sure it does not have focus anymore
     hideResult(); 
     $(document).on('blur', "#myinput", hideResult);  //rebind the handler if needed
});

function hideResult() {
     $("#myresults").hide();
}

FIDDLE

Nildanile answered 18/5, 2012 at 13:19 Comment(0)
S
2

I faced this issue and using mousedown is not an option for me.

I solved this by using setTimeout in the handler function

        elem1.on('blur',function(e) {

            var obj = $(this);

            // Delay to allow click to execute
            setTimeout(function () {
                if (e.type == 'blur') {

                  // Do stuff here

                }
            }, 250);
        });

        elem2.click( function() {
            console.log('Clicked');
        });
Seagirt answered 13/4, 2016 at 10:6 Comment(0)
I
2

I just answered a similar question: https://mcmap.net/q/161620/-blur-event-stops-click-event-from-working

An alternative to the mousedown solutions is have it ignore blur events caused by clicking specific elements (i.e., in your blur handler, skip the execution if it's a result of clicking a specific element).

$("#myinput").live('blur',function(e){
     if (!e.relatedTarget || !$(e.relatedTarget).is("#myresults ul li")) {
         $("#myresults").hide();
     }
});
Ilailaire answered 10/10, 2017 at 21:51 Comment(2)
in my case, event.relatedTarget returns null, any idea why could that be ?Arad
ok, got the answer here: #42764994Arad
C
0

Mousedown solutions does not work for me, when I click on non button elements. So here is what I've done with blur function in my code:

.on("blur",":text,:checkbox,:radio,select",function() {
/*
* el.container 
* container, you want detect click function on.
*/
            var outerDir = el.container.find(':hover').last();
            if (outerDir.length) {
                if ($.inArray(outerDir.prop('tagName'), ["A","SELECT"] ) != -1 || outerDir.prop('type') == 'button' || outerDir.prop('type') == 'submit') {
                    outerDir.click();
                }
                if (outerDir.prop('type') == 'checkbox') {
                    outerDir.change();
                }
                return false;
            }
/*          
* YOUR_BLUR_FUNCTION_HERE;
*/
});
Cocktail answered 12/2, 2015 at 16:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.