Question

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.

Was it helpful?

Solution

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.

OTHER TIPS

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.

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();
    });

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
}

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

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top