<input> lostfocus/onblur event in knockout
Asked Answered
M

6

27

I want to execute an event on a knockout observable bound to an input. This function should be executed when the control lose focus, even without typing anything. I tried to change the event binding but it doesn't fire when the user moves away from the control without typing anything. I tried mouseout event, but that only fires when the user clicks elsewhere in the form, after losing focus - not exactly what I want. I want the even to fire as soon as the focus is moved away from the control, even with tab.

Following is the code I used for mouseout event:

<input
    type="text"
    id="txtFirstName"
    tabindex="1"
    maxlength="25"
    class="txtbox" 
    style="width: 200px;"
    data-bind="value: FirstName, 
               attr: {title: FirstNameErrorMessage },
               css: {validationFailed: !IsValidFirstName() },
               event: {mouseout: ValidateFirstName}" 
/>

this.ValidateFirstName = function () {
    self.IsValidFirstName(true);
    self.FirstNameErrorMessage('');
    if (self.FirstName() == '') {
        self.IsValidFirstName(false);
        self.FirstNameErrorMessage('First Name is required');
    }
}

Can anyone help please?

Mueller answered 19/4, 2013 at 9:44 Comment(0)
S
36

I think that there are a few approaches that you could use. A nice option would be to use KO's hasfocus binding: http://knockoutjs.com/documentation/hasfocus-binding.html.

You can bind against a boolean observable, and then subscribe to it. In the subscription, you can choose to only react when the value is now false.

Something like:

self.FirstName = ko.observable();
self.FirstName.focused = ko.observable();

self.FirstName.focused.subscribe(function(newValue) {
   if (!newValue) {
       //do validation logic here and set any validation observables as necessary
   }
});

Bind against it like:

data-bind="value: FirstName, hasfocus: FirstName.focused"

I think that this would be a good option if you want it to fire everytime a user leaves the field no matter how they leave it and regardless of whether a change was actually made.

Snakemouth answered 19/4, 2013 at 11:44 Comment(6)
+1: OMG I've been chopping and hacking at a snippet of code that I'd written very nearly exactly like this. I couldn't for the life of me figure out what on earth was different that would work in your answer that I hadn't written myself. I had data-bind="text instead of data-bind="value for an input. Thanks for adding the binding statement in your answer. I needed that kick in the pants today apparently.Swagerty
@JoelEtherton - I haven't definitely done that a time or two myself. Glad you figured it out.Snakemouth
I have tried this technique. The problem, for me, is that hasFocus always seems to "fire" (turn to true) on the initial page load, which puts everything in the wrong state for what I'm trying to do. I initialize the corresponding viewmodel value to false, but this didn't change anything.Schlenger
Whoops, just figured out that ANY focus change - including losing focus - will cause the bound value to change. So I just need to interrogate newValue to see if focus is true or false. The fact that it has an event name in it ('focus') sort of made me forget how observables work. :)Schlenger
I'm having issues with this approach. I think it's because I have multiple input fields binding to the same observable. What happens is the thing gets fired like four times with newValue toggling back and forth between true and false. Gonna just use jquery blur.Braze
If fires on initial page load also, does anyone has a solution for this? thanksAnisaanise
E
23

This worked for me:

data-bind="event: { blur: OnBlurEvent }"
Eigenfunction answered 20/8, 2014 at 10:8 Comment(1)
If this is all you want, I dare say this is the best way. I wonder what @rp-niemeyer thinks about this though. Surely this approach crossed his mind?Adaminah
V
22

I like @RPNiemeyer's answer. However, I just wanted to point out that not everything has to be done via Knockout. It's just a tool, and sometimes it's not the best tool for the job. You an always just use direct event binding like you've always done in JS, i.e.

$('#FirstName').on('blur', function () {
    // do something
});

If you need to actually interact with your view model in there, then you can simply use ko.dataFor as described in the Knockout's documentation on event delegation:

$('#FirstName').on('blur', function () {
    var data = ko.dataFor(this);
    // do something with data, i.e. data.FirstName()
});
Vanden answered 19/4, 2013 at 14:48 Comment(1)
I like this solution a bit more because it doesn't add any subscriptions. :)Annora
T
6

I just had the same problem, solved it by creating a custom binding:

ko.bindingHandlers.modifyOnFocusOut = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        $(element).blur(function() {
            //Do your work
        });
    }
}

And then called it like this:

 data-bind="value: FirstName, modifyOnFocusOut: FirstName"
Towering answered 14/7, 2013 at 5:14 Comment(0)
O
5

Have you tried event:{blur: ValidateFirstName} this event will be fired if the user click out or 'tab out' of the input.

<input
    type="text"
    id="txtFirstName"
    tabindex="1"
    maxlength="25"
    class="txtbox" 
    style="width: 200px;"
    data-bind="value: FirstName, 
               attr: {title: FirstNameErrorMessage},
               css: {validationFailed: !IsValidFirstName()},
               event: {blur: ValidateFirstName}"

Here's a JSFiddle of a working example.

Overshine answered 25/10, 2014 at 14:12 Comment(0)
T
0

Using TypeScript I solved it using 2 custom bindings, a SetFocusBinding and a OnBlur Binding... Using the SetFocusBinding I make sure the input field has focus. Using the OnBlur binding a function is called when the blur event is triggered.

module Fx.Ko.Bindings {
    export class SetFocusBinding implements KnockoutBindingHandler {
        public update(element, valueAccessor, allBindingsAccessor) {
            var value = valueAccessor();
            var valueUnwrapped = ko.unwrap(value);
            if (valueUnwrapped == undefined) {
                return;
            }
            if (valueUnwrapped)
                $(element).focus();
        }
    }
}

and ...

    module Fx.Ko.Bindings {
        export class OnBlurBinding implements KnockoutBindingHandler {
            public init(element, valueAccessor, allBindings, viewModel, bindingContext) {
                var value = valueAccessor();
                $(element).on('blur', function (event) {
                    value();
                });
            }
        }
    }
interface KnockoutBindingHandlers {
    onBlur: KnockoutBindingHandler;
}
ko.bindingHandlers.onBlur = new Fx.Ko.Bindings.OnBlurBinding();
Totaquine answered 16/6, 2015 at 9:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.