I have always considered myself to be quite client-side challenged. Having been soured to the thought of JavaScript by all the browser specific code I used to have to write years ago I swore off it and took to server-side development and never looked back. After a year’s hiatus doing WPF programming, I’ve recently found myself back in the thick of web development with new patterns and technologies at the forefront, namely the MVC framework and the ubiquity of jQuery. Before I begin this post I’d like to say that jQuery is, well, everything that is good about client-side programming in my opinion.

So recently I found the need to roll my own dirty form warning mechanism on a site I was working on. These forms contain quite a lot of fields and so there is gratuitous use of the TabControl from the Microsoft AJAX Control Toolkit. One of the requirements was to alert the user to changes made on one of the tabs and offer the option to save the changes before allowing the user to change tabs. Initially I thought that this task would be a breeze, what with the OnClientActiveTabChanged handler on the TabContainer control and all. Then the sad reality dawned on me. There is no way to cancel the changing of tabs from the client side. Like a freight train full of explosives it barrels on, destroying my workflow and with it any chance I’ll get sleep. But all is not lost, because with a few JavaScript ninja moves, some nifty jQuery (optional) and no personal life there is a clean solution to this problem, So what’s the answer? Read on for the details.

What’s The Issue

The problem with the existing implementation is two-fold. Taking a dive into the JavaScript for the control yields the first problem (lines 6 - 11):

></tr></table></code></figure> I included more code here than is required because it’s important to see the context of where the problem lies. We’re looking at the prototype object definition for the TabContainer control, the root object of the TabControl. The problem method here is the **raiseActiveTabChanged **definition.  What this brilliant little snippet does is pretty much say, if there’s a client side handler for when the tabs change, fire it off. Then there’s the postback. What’s problematic here is that the result of the user handler for tab changes is completely left out of the decision to postback. I suppose someone could be sneaky and just set the autopostbackid to null but that seems like an important piece. When I started out, I thought maybe this was the place to make my attack, but then there was the second issue, illustrated below:
>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12</td>
AjaxControlToolkit.TabContainer.prototype = { add_activeTabChanged : function(handler) { this.get_events().addHandler("activeTabChanged", handler); }, remove_activeTabChanged : function(handler) { this.get_events().removeHandler("activeTabChanged", handler); }, raiseActiveTabChanged : function() { var eh = this.get_events().getHandler("activeTabChanged"); if (eh) { eh(this, Sys.EventArgs.Empty); } if (this._autoPostBackId) { __doPostBack(this._autoPostBackId, "activeTabChanged:" + this.get_activeTabIndex()); } } } </pre></div>
</tr></table></code></figure> That’s right, when a tab is clicked, the first thing that happens (even before the postback) is that the new tab’s panel is shown and the current panel hidden via the tabControl’s **set_activeTabIndex()** function. The key parts of this function are on lines 7 and/or 9, which call the **\_set\_active()** method of the tab, which sets it’s visibility to shown. This means that even if you were able to avoid the postback and give a user some sort of confirmation dialog, the user would still be looking at the new tab, and only after confirming, would be brought back to the original tab. I don’t know about you, but that doesn’t scream good UX to me. ## There’s Gold in Them There Hills After a bit of searching, I did eventually find my diamond in the rough. The following event handler is the earliest place where the **set_activeTab()** method is called, and it’s defined on the TabPanel prototype itself. This will be where I perform my ninja skills:
_header_onclick : function(e) {
this.raiseClick();this.get_owner().set_activeTab(this);}
As you can see in the snippet, this is what controls how the TabControl does it’s thing here, so if we could just augment this code with our own handler and put some additional logic around the call to the owner’s set_ActiveTab method then we’d be cooking with gas in no time. Well, it turns out there is a way to do this. ## Secret, Secret, I’ve Got a Secret! While I wish every problem could be solved by listening to Mr. Roboto by Styx, alas we live in reality. A long time ago a JavaScript master, who just so happens to be the other person that posts on this blog, told me long ago that you can pretty much override any default JavaScript object’s method declarations, property declarations, actually, pretty much anything you want. Now, thanks to a bit of a refresher course and my new target function, the following function is what I used to replace the above **\_header\_onclick** method:
AjaxControlToolkit.TabPanel.prototype._header_onclick =
  function(e) {
      this.raiseClick();

       if (confirm('Tabs are changing! Click Ok to proceed, or Cancel to remain current tab.'))
              this.get_owner().set_activeTab(this);
        else
              return false;
  };
As you can see, it’s so simple. The key line is the first one. I’m telling the TabPanel object that I’m replacing its current definition with my own. Then I just put in a confirm dialog (line 5) and depending on the result I either let the tab control work as expected or I can cancel and do nothing. Basically, this is all there is to it. So how do we make it work? That’s also simple. You can use a little bit of jQuery, or you can use the existing Ajax client side lifecycle events. First, let’s see how we can do this with Ajax:
function pageLoad(sender, args){
  AjaxControlToolkit.TabPanel.prototype._header_onclick =   function(e) {
         this.raiseClick();

         if (confirm('Tabs are changing! Click OK to proceed, or click Cancel to remain on the current tab.'))
                 this.get_owner().set_activeTab(this);
        else
                 return false;
  };
}
In Ajax, any function called pageLoad found in the page scripts will automatically be run when the page is loaded. You could also be explicit and use the Sys.Application.add_load() method. Now let’s take a look at the jQuery implementation:
$(function() {
  AjaxControlToolkit.TabPanel.prototype._header_onclick =
    function(e) {
        this.raiseClick();

        if (confirm('Tabs are changing! Click OK to proceed, or click Cancel to remain on the current tab.'))
                 this.get_owner().set_activeTab(this);
         else
                 return false;
     };
});

Dave Marini  

I am a full stack software architect with over 20 years of experience developing scalable, enterprise level applications targeted for Windows and the web. I have worked with a number of SPA front end frameworks, .NET middleware technologies, both relational and document databases and various big data search platforms. I'm currently interested in Microservice and Serverless architecture patterns.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13</td>
set_activeTabIndex : function(value) { if (!this.get_isInitialized()) { this._cachedActiveTabIndex = value;} else { if (value < -1 || value >= this.get_tabs().length) { throw Error.argumentOutOfRange("value"); } if (this._activeTabIndex != -1) { this.get_tabs()[this._activeTabIndex]._set_active(false);} this._activeTabIndex = value;if (this._activeTabIndex != -1) { this.get_tabs()[this._activeTabIndex]._set_active(true);} if (this._loaded) { this.raiseActiveTabChanged();} this.raisePropertyChanged("activeTabIndex");} } </pre></div>