user1044169
user1044169

Reputation: 2866

Focusout is triggered twice

I have a textbox and a Jquery UI dialog. When a user is done typing into the textbox and presses Enter, I want to display the dialog. In order to accomplish that I use the following code:

$(document).ready(function () {
    var helpDial = $('#helpDialog');
    var input = $('#helpSearch');

    helpDial.dialog({
        width: 800,
        autoOpen: false,
        modal: true,
        title: "Templates",
        position: [165, 158]
    });

    input.focusin(function () {
        helpDial.dialog('close');
    }).focusout(function () {
        helpDial.dialog('open');
    }).on('keypress', function (e) {
        var code = (e.keyCode ? e.keyCode : e.which);
        if (code == 13) {
            input.focusout();
            return false;
        }
    });
});

<input id="helpSearch" />
<div id="helpDialog"></div>

http://jsfiddle.net/7Fpm4/1

The problem is that when I press Enter, the focusout is called twice, one from the input.focusout() and the second time right after the helpDial.dialog('open') in the focusout event handler. This causes two background overlays to be created, and when I close the dialog, one overlay remains visible.

What am I doing wrong? Is there a better way to handle this scenario -- 'an enter pressed in a textfield opens a jQuery dialog'. Thanks.

Upvotes: 4

Views: 9168

Answers (4)

Samuel Gfeller
Samuel Gfeller

Reputation: 1010

I wanted to trigger the focusout event at a place where I did not have access to the focusout event handler so I couldn't just call the function.

The way I solved it was by creating a dummy focusable input field, focusing it and then removing it. This causes the focusout event to be triggered only once.

// Create a focusable dummy input element
let dummyInput = document.createElement('input');
document.body.appendChild(dummyInput);

// Triggers focusout event
dummyInput.focus();

// Remove the dummy input element
document.body.removeChild(dummyInput);

Upvotes: 0

saluce
saluce

Reputation: 13360

The only way to prevent an event from firing is to unbind the event handler. Or, depending on the situation, take differential action based on information available at the time the event fires.

The focusout event is triggered any time the element loses the focus. In this case, every time the user clicks on the textbox, a focusin event is triggered. As soon as the cursor leaves the textbox (which happens when the dialog box is opened), the focusout event is triggered.

The problem with your code is that you forcibly call the focusout event, and then the focusout event is called naturally when you open the dialog. So, change your code as follows:

$(document).ready(function () {
    var helpDial = $('#helpDialog');
    var input = $('#helpSearch');

    helpDial.dialog({
        width: 800,
        autoOpen: false,
        modal: true,
        title: "Templates",
        position: [165, 158]
    });

    input.on('focusin', function () {
        input.on("focusout", textboxFocusOut);
    }).on('keypress', function (e) {
        var code = (e.keyCode ? e.keyCode : e.which);
        if (code == 13) {
            textboxFocusOut();
        }
    });

    function textboxFocusOut() {
        input.off("focusout", textboxFocusOut);
        helpDial.dialog('open');                
    }

});

http://jsfiddle.net/8L7EL/1/

What this code is doing is binding a function to focusout within the focusin handler. If the user navigates away from the textbox, the focusout event handler will be called, which immediately unbinds the focusout event (to prevent binding the function multiple times). If the user hits the enter key, then the function is called manually, which removes the focusout event handler before opening the dialog to prevent triggering the focusout event from firing automatically when the dialog opens.

Upvotes: 6

Paulo R.
Paulo R.

Reputation: 15609

The most obvious solution might be using a variable like enter_pressed to inform the focusout function:

$(function () {
    var helpDial = $('#helpDialog'),
        input = $('#helpSearch'),
        enter_pressed = false;

    helpDial.dialog({
        width: 800,
        autoOpen: false,
        modal: true,
        title: "Templates",
        position: [165, 158]
    });

    input.on('focusout', function () {
        if( !enter_pressed ) {
            helpDial.dialog('open');
        } else {
            enter_pressed = false;
        }
    }).on('keypress', function (e) {
        var code = (e.keyCode ? e.keyCode : e.which);
        if (code == 13) {
            enter_pressed = true;
            helpDial.dialog('open');
        }
    });
});

http://jsfiddle.net/joplomacedo/7Fpm4/7/

However, I believe there's a cleaner solution. I'll try and come up with it.

Aside
Is the .focusin event handler really needed?

Upvotes: 3

Miljan Puzović
Miljan Puzović

Reputation: 5820

http://jsfiddle.net/7Fpm4/2/ i updated your example, just changed input.focusin(function () into input.change(function ()

Upvotes: 0

Related Questions