33

I have run into this problem a few times and I'm not happy with the solutions I've used before.

I have an input box with a blur event that validates the content of it and a button which will fill the input box on click. The problem is clicking the button fires the input blur event and then the button click event so content inserted by the button is not what is validated.

See http://jsfiddle.net/jakecr/dL3K3/

I know this is the correct behavior but I can't think of a clean way to get around the problem.

Jake
  • 12,713
  • 18
  • 66
  • 96
  • Your question is not clear? what is your problem and expected solution? – Chinmayee G Nov 03 '10 at 07:28
  • Basically one user action causes two events which fire in order but the action of the second first relies on that of the first. I've considered using setTimeout to delay the first event, but that doesn't seem like an ideal solution. – Jake Nov 03 '10 at 07:38
  • I've asked this question in quite a general form because I've encountered this problem a few times in different circumstances. – Jake Nov 03 '10 at 07:42
  • No matter what you do, if the focus changes from one element to another, the first element's `blur` event will be fired before the second's `focus` or `click`. Perhaps you should be using the `change` event? Then it might evaluate it before the button's `click`, then again after? (untested) – clarkf Nov 03 '10 at 07:47

6 Answers6

45

We had a similar problem at work. what we figured out in the end was that the mousedown event will fire before the blur event allowing you to set the content before the validation or even cancel the validation process completely using a flag.

check this fiddle I made using your example- http://jsfiddle.net/dL3K3/31/

$(function(){
    var flag = true;
    $('input').blur(function(){
        if(!$(this).val() && flag){
            alert("Grrr, It's empty");
        }
    });

    $('button').mousedown(function(){
        flag = false;
    });    
    $('button').mouseup(function(){
        flag = true;
        $('input').val('content').focus();
    });

});

-Edit-

As @mclin Suggested, using the mousedown event and preventDefault will prevent blur behavior. And you can just leave the original click event intact -

http://jsfiddle.net/dL3K3/364/

$(function(){
    $('input').blur(function(){
        if(!$(this).val()){
            alert("Grrr, It's empty");
        }
    });

    $('button').mousedown(function(e){
        e.preventDefault();
    });    
    $('button').click(function(){
        $('input').val('content');
    });

});

This solution might be a bit cleaner and better for most cases just note that if you use it you'll preventDefault and blur of any other element the focus is currently on but for most use cases that shouldn't be an issue.

Yoni Jah
  • 773
  • 7
  • 15
  • mousedown event can cause the submit to happen regardless of validation see here http://stackoverflow.com/questions/43011387/mousedown-still-submitting-form-when-it-should-not – DragonFire Mar 25 '17 at 23:19
  • @DragonFire This has nothing to do with this issue or mouswdown. Your code is running async and your treating it as synchronous code – Yoni Jah Mar 28 '17 at 02:06
  • very good solution mouse down event has done the job basically i want to something to done before blur event so mouse down worked for me Thanks – Muneer Khan Jul 14 '22 at 13:06
25

I have a better solution than Yoni Jah's: call preventDefault on the button's mousedown event. The onBlur never happens so you're free to do whatever in the click event.

This is better because it doesn't change the click behavior by making things happen on mouse down instead of mouse up, which can be unexpected.

mclin
  • 3,599
  • 1
  • 20
  • 18
9

This might also help:

Set a timer that delays the blur event action before it fires the button click event.

// Namespace for timer var setTimer = { timer: null }

    $('input,select').blur(function () {
        setTimer.timer = setTimeout(function () {
           functionCall();
        }, 1000);
    });

    $('input,select').focus(function () {
        setTimer.timer = setTimeout(function () {
            functionCall();
        }, 1000);
    });

    $("#btn").click(function () {
        clearTimeout(setTimer.timer);
        functionCall();
    });
Griwes
  • 8,805
  • 2
  • 43
  • 70
Vardhini
  • 353
  • 3
  • 9
2

Separate the input validation logic to it's own function which is called both by the blur event automatically and then by the click event after you've modified the value in the input box. True your validation logic would be called twice, but I'm not seeing a way around that without making a lot more changes.

Example using jQuery:

$('input').blur(function() {
    validate(this);
});

$('submit').click(function() {
   //modify the input element
   validate(inputElement);
});

var validate = function(obj) {
   // validate the object
}
pitx3
  • 124
  • 4
  • Not quite the solution I was after because the blur event is still triggered, but I don't think it's possible to do exactly what I want. – Jake Nov 08 '10 at 21:58
0

Sometimes it is useful to get a child object on mouseup, but the parent wants to hide its children on blur. We can cancel the hiding of the children elements, then wait until our mouseup occurs, then force the blur to occur when we are ready

js

var cancelHide = false;

$('.my-input').blur(function() {
    if (!cancelHide) {
        $('.my-item').hide();
    }
});

$('.my-input').click(function() {
    $('.my-item').show();
});

$('.my-item').mousedown(function() {
    cancelHide = true;
});

$('.my-item').mouseup(function() {
    var text = $(this).text();
    alert(text);
    cancelHide = false;
    $('.my-input').blur();
});

html

<input type="text" class="my-input"/>
<div class="my-item">Hello</div>
<div class="my-item">Lovely</div>
<div class="my-item">World</div>

css

.my-item {
    display:none;
}

https://jsfiddle.net/tic84/on421rxg/

Clicking the list items will only alert on mouseup, despite the parent trying to hide them on blur

tic
  • 2,484
  • 1
  • 21
  • 33
-1

The trick is not to use blur and click events, because they are always performed in order of blur first, click second. Instead you should check when the element having focus has changed, because that happens only after the click.

I think I have an elegant solution for this one:

var alreadyValidated = 'true';

setInterval(function(){
    if(document.activeElement.id != 'validation-field-id'){
        if(alreadyValidated=='false'){
            alreadyValidated = 'true';
            validate(document.activeElement);       
        }
    }
    else {
        alreadyValidated = 'false';
    }
}, 200);

These days all modern browsers support document.activeElement.

Ahti Ahde
  • 7
  • 2