by Andreas

JQuery / KnockoutJS – define default button within a DIV

We’ve previously covered several aspects of using Default buttons in ASP.NET and Silverlight (the latter is in Norwegian, but should be semi-readable through an automagically translated version – sponsored by Google Translate).

This time I thought I’d show you how I did this without the need of server side controls. Why? First of all, adding server side controls adds unnecessary overhead by increasing the view state. Second, in my complex client side user interface I’ve stuck to clean HTML, javascript / jQuery and KnockoutJS. I didn’t want to clog it up, and since the solution was as simple as it turned out to be I am glad I stuck to my guns.

I had a look at what ASP.NET generates when a default button is defined for a Panel control. As you may or may not know, the server side Panel control is rendered as a DIV when the page is returned to the client browser. And as expected, Javascript is used to invoke the defined default button click event so I thought I’d just steal this javascript function and call it myself from within my pure HTML DIV element. Not surprisingly, this worked and why shouldn’t it? I just did the exact same thing as the .NET framework does, and there’s not much voodoo or black magic in that.

So, I wanted to invoke the click event for a “Save Changes” button displayed within a Fancybox dialog, simply by clicking ENTER. First, I added the javascript function that I stole from the .NET server control rendering engine (I am pretty sure that’s not what it’s called):

function WebForm_FireDefaultButton(event, target) {
    if (event.keyCode == 13) {
        // this variable has to be added
        var __nonMSDOMBrowser =
            (window.navigator.appName.toLowerCase().indexOf('explorer') == -1);

        var src = event.srcElement || event.target;
        if (!src || (src.tagName.toLowerCase() != "textarea")) {
            var defaultButton;
            if (__nonMSDOMBrowser) {
                defaultButton = document.getElementById(target);
            }
            else {
                defaultButton = document.all[target];
            }
            if (defaultButton && typeof (defaultButton.click) != "undefined") {
                defaultButton.click();
                event.cancelBubble = true;
                if (event.stopPropagation) event.stopPropagation();
                return false;
            }
        }
    }
    return true;
}

Note that I had to add the __nonMSDOMBrowser variable. This is usually added as a global variable on the page, but I saw no reason to do so because none of my other functions care about it.

Then, within my dialog box DIV element I make sure the onkeypress calls the new function with the ID of the button as a parameter:

<div id="addContactDialogBody" class="modal-body" onkeypress="javascript:return WebForm_FireDefaultButton(event, 'btnSaveContact')">
      <!-- This is my user input form, containing a couple of input text fields -->
</div>
<div id="addContactDialogFooter" class="modal-footer">
    <!-- dont be confused, the data-bind attribute is KnockoutJS related and has nothing to do with this example -->
    <input id="btnSaveContact" type="button" value="Save" data-bind="click: addNewContact, visible: true" />
    <input id="btnCancelContact" type="button" value="Cancel" onclick="javascript: $.fancybox.close();" />
</div>

 

When the focus is on an element within the first DIV (removed for clarty in the example code), pressing the enter Key will invoke the click event (or in my case, a KnockoutJS click binding) for the button with id btnSaveContact.

by Andreas

KnockoutJS – how to force binding refresh / re-evaluation

Observable bindings in KnockoutJS get re-evaluated when the focus is changed in a form, for instance when you click a button or use TAB to move from one input field to the next. When KnockoutJS is used in combination with an ASP.NET panel control where a DefaultButton is defined, re-evaluation of observable bindings is not performed when the user clicks ENTER to trigger the button click event. As a result of this, a value entered in an input field that is bound to the view model will not be registered if the user doesn’t navigate (move focus) away from the field first. 

A work-around for this is to use jQuery (or pure javascript) and programmatically set the focus to a different control, for instance the button itself when the event is fired.

self.addNewTimeRegEntry = function () {
    // set focus to button control to force re-evaluation
    $("#<%= btnAddRegEntry.ClientID %>").focus();

    ... // continue as normal

The button click event is bound to the addNewTimeRegEntry function above (through the KnockoutJS framework), and by moving the focus all variables will be refreshed before further processing is done.