Archive for the ‘JavaScript’ Category

Introducing TextBox Limiter Control Ajax Control Toolkit Extender

May 29th, 2008 by Sidar Ok

You can download the sources from here

ASP.NET TextBox has an integer attribute “MaxLength”which corresponds to html text input’s property with the same name. It works perfectly when the textbox is single line, normal input type “text”.

But when we want to work in a multiline box, such as an e-mail message or sending and SMS, we want to limit it in the same way and what happens? We see that generated control is a “textarea” and it doesn’t support maximum length! Gee!

Now of course we can use Regular Expression validators to validate and tell at the client side, but we don’t want to just tell! We want to prevent it exceeding the predefined size too!

That’s why I came up with this Ajax Control Toolkit extender that I called TextboxLimitExtender. We just give it the MultiLine text area to operate on, and the maximum length. I also added an option to show how many characters left on a text control of your choice. The extender contains a server side method to do the double check on server side.

Here is a screenshot of what you will expect to get at the end of it:

clip_image002

Picture 1. Extender in action

How to Use It

After adding TextboxLimiterExtender and Ajax Control Toolkit assemblies to your project as references, add the following at the beginning of your page or user control that you want to use the TextboxLimitExtender:

<%@ Register Assembly=”TextboxLimitExtender” 
Namespace=”TextboxLimitExtender” TagPrefix=”cc1″ %>

Of course, we have to be sure that we have a script manager:

<asp:ScriptManager ID=”sm” runat=”server” />

Now let’s assume that our target textbox is defined like the following:

<asp:TextBox ID=”limitedTextBox” runat=”server” TextMode=”MultiLine” />

And just beneath it we have our static text and a label to show how many characters left:

You have <asp:Label ID=”charsLeftLabel” runat=”server” ForeColor=”Red” /> 
chars left.
Now the moment of truth: with these controls extender goes like this:
<cc1:TextboxLimitExtender ID=”TextboxLimitExtender1″ runat=”server” 
MaxLength=”50″ TargetControlID=”limitedTextBox” 
TargetCountTextControlId=”charsLeftTextBox”>    

        </cc1:TextboxLimitExtender>

How it Works

It handles the every key hit and checks if the checkbox length exceeded the maximum length or not. If it didn’t, then does nothing. If it did, then it cancels the event so the offending chars never get typed.

In addition, we need to handle copy & paste behaviors to prevent them from happening for the same reasons above.

Implementation

Server Side

We will have 2 properties, one for the ID of the control to write how many characters left, and another one to keep maximum length.

Here is TextboxLimiterExtender.cs that writes the injects values for the script:

   1: [Designer(typeof(TextboxLimitExtenderDesigner))]
   2: [ClientScriptResource(“TextboxLimitExtender.TextboxLimitExtenderBehavior”,
   3:     “TextboxLimitExtender.TextboxLimitExtenderBehavior.js”)]
   4: [TargetControlType(typeof(ITextControl))]
   5: public class TextboxLimitExtender : ExtenderControlBase
   6: {
   7:     // TODO: Add your property accessors here.
   8:     //
   9:     [ExtenderControlProperty]
  10:     [DefaultValue(“”)]
  11:     [IDReferenceProperty(typeof(ITextControl))]
  12:     public string TargetCountTextControlId
  13:     {
  14:         get
  15:         {
  16:             return GetPropertyValue(“TargetCountTextControlId”, string.Empty);
  17:         }
  18:         set
  19:         {
  20:             SetPropertyValue(“TargetCountTextControlId”, value);
  21:         }
  22:     }
  23:
  24:     [ExtenderControlProperty]
  25:     [DefaultValue(“1000″)]
  26:     public int MaxLength
  27:     {
  28:         get
  29:         {
  30:             return GetPropertyValue<int>(“MaxLength”, 0);
  31:         }
  32:         set
  33:         {
  34:             SetPropertyValue<int>(“MaxLength”, value);
  35:         }
  36:     }
  37:
  38:     /// <summary>
  39:     /// Validates the textbox against the maximum number.
  40:     /// </summary>
  41:     /// <returns></returns>
  42:     public bool Validate()
  43:     {
  44:         return ((ITextControl)this.TargetControl).Text.Length <= MaxLength;
  45:     }
  46:
  47: }

As you can see the type of target control and the control to write target count are type of ITextControl interface. This is an interface implemented by every control that has Text property, so you can swap between Textbox and Labels. Here is a screenshot that writes the content to a TextBox instead of a label:

clip_image002[1]

Picture 2. Textbox Limiter outputting to a Textbox instead of a Label

Client Side

In the behaviour file we will define the variables that are coming from the server side and the events to achieve the behaviour needed. The code below shows how to create the behaviour . We are also initialising the methods that we are going to use here:

   1: TextboxLimitExtender.TextboxLimitExtenderBehavior = function(element) {
   2:     TextboxLimitExtender.TextboxLimitExtenderBehavior.initializeBase(this, [element]);
   3:
   4:     // initializing property values
   5:     //
   6:     this._TargetCountTextControlId = null;
   7:     this._MaxLength = 1000;
   8:
   9: //    //initializing handlers
  10:     this._onKeyPressHandler = null;
  11:     this._onBeforePasteHandler = null;
  12:     this._onPasteHandler = null;
  13:     this._onKeyDownHandler = null;
  14:     this._onKeyUpHandler = null;
  15: }

The rest goes as the same with a standard implementation of an Ajax Control Toolkit Extender, but I’ll show some important methods that are listed above.

RefreshCountTextbox method calculates the characters left and updates the count on the targetCountTextControl .

   1: _refreshCountTextBox: function() {
   2:
   3:         var control = this.get_element();
   4:         var maxLength = this.get_MaxLength();
   5:         var tbId = this.get_TargetCountTextControlId();
   6:         var countTextBox;
   7:         //var countMode = this.
   8:         if (tbId) {
   9:             countTextBox = $get(tbId);
  10:         }
  11:         else
  12:             return; //nowhere to write.
  13:
  14:         var innerTextEnabled = (document.getElementsByTagName(“body”)[0].innerText !=
  15: undefined) ? true : false;
  16:
  17:         if (countTextBox)
  18:         {
  19:
  20:             if(innerTextEnabled)
  21:             {
  22:                 countTextBox.innerText = maxLength - control.value.length;
  23:             }
  24:             else
  25:             {
  26:                 countTextBox.textContent = maxLength - control.value.length;
  27:             }
  28:         }

On pasting, things get a bit more interesting. We need to cancel default pasting in order to perform our own one, so we handle onbeforepasting:

   1: _onBeforePaste: function(e) {
   2:         //cancel default behaviour
   3:         if (e) {
   4:             e.preventDefault();
   5:         }
   6:         else {
   7:             event.returnValue = false;
   8:         }
   9:
  10:         this._refreshCountTextBox();
  11:     },

And now that we cancelled the paste, we have the responsibility to reach to what user wanted to copy and tailor it until it doesn’t exceed max length. If it exceeds, than the trailing bits won’t be in the box:

   1: _onPaste: function(e) {
   2:         var control = this.get_element();
   3:         var maxLength = this.get_MaxLength();
   4:         //cancel default behaviour to override
   5:
   6:         if (e) {
   7:             e.preventDefault();
   8:         }
   9:         else {
  10:             event.returnValue = false;
  11:         }
  12:         var oTR = control.document.selection.createRange();
  13:         var insertLength = maxLength - control.value.length + oTR.text.length;
  14:         var copiedData = window.clipboardData.getData(“Text”).substr(0, insertLength);
  15:         oTR.text = copiedData;
  16:
  17:         this._refreshCountTextBox();
  18:     },

Limitations & Remarks

Although the sample project is in .NET 3.5, the code is fully 2.0 compatible. It works fine in IE 6.0 and 7.0, but for FireFox it limits the textbox but doesn’t print the number of characters left for some reason and I was too lazy to investigate it(see update).

Conclusion

This extender wraps up the needed strategy for limiting a textbox and showing how many characters left. You can use download the source code from here and use it in anyway you want.

Feel free to post suggestions, improvements or critics under this post or to my mail address sidarok at sidarok dot com.

UPDATE: Thanks to Michael, it works for Firefox now. Source is updated. See comments.

UPDATE 2 : I am not developing the source any further, including doing no compatibility checks or new updates. Please see the comments below of people who are gracefully providing information on the issues they come across with and don’t hesitate to share with others like they are doing.

Technorati Tags: ,,,,

kick it on DotNetKicks.com

Share it on: These icons link to social bookmarking sites where readers can share and discover new web pages.
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google
  • Blogosphere News
  • e-mail
  • YahooMyWeb
  • DotNetKicks
  • DZone

Cross Browser Guide Part 3 – Event Handling in Different Browsers

May 10th, 2008 by Sidar Ok

For the first two articles of the series: Part 1 and Part 2 .

The worst part of making an application work in multiple browsers is the different interpretation of JavaScript by every browser (you know what I mean). One of the most obvious ones is the event handling architecture difference amongst Internet Explorer and the browsers those follow W3C standards for DOM event handling.

This is a very important topic because everything starts with events. No events, no scripting. If at one point of your script your event handling fails, it is very likely that the rest of it will not be executed. So we need to understand the event models of –at least – the major browsers. We can group them by three major categories:

1 – Traditional Model

In the old browsers we were able to attach only through inline scripting such as:

<input type=”button” id=”myButton” value=”Press” onclick=”alert(’hello world!’)” />

But this is not easily maintainable and recommended now. So the Netscape notation is a common way to hook your events up:

element.onclick = doSomething;

As you can see there is a certain drawback that we can not add more than one listeners to an event as we can do in today’s modern languages. This model is supported by most of the browsers, so don’t worry, you don’t need to write any extra codes for here.

2 – W3C Model

In 2000, W3C has published a DOM Level 2 Events Specification to address the problems about the traditional model.

In this model, assignments to a specific event are done by add and remove events for a specific element. For example, to add you say:

myButton.addEventListener(‘click’, doSomething, false);

Where to remove you need to write:

myButton.removeEventListener(‘click’, doSomething, false);

As you can see you can add or remove multiple listeners to an event in this model. For e.g the following manages to fire both doSomething1 and doSomething2 when myButton is clicked:

myButton.addEventListener(‘click’, doSomething1, false);
myButton.addEventListener(‘click’, doSomething2, false);

W3C also supports anonymous functions that are very similar to anonymous functions in C#.NET 2.0.

The last Boolean parameter states whether the event handling will be done in bubbling phase or not (in handling phase).

3 – Microsoft Event Bubbling Model

This event model is similar to W3C’s one, but it is not the same. The name of the method to attach the event is different, and as in the below:

myButton.attachEvent(‘onclick’, doSomething);

and to remove the handler you use:

myButton.detachEvent(‘onclick’, doSomething);

As you see, there is not third parameter specifying capture or bubble, as the events isn MS programming environment always bubble, not captured.

As a result of this it is impossible to know exactly what element raised that event without doing anything ( I advice to look at MS Ajax Source code to see how they handled this situation)

That’s why while working in IE 7.0, we need to be careful about window.event behavior. This is there to store the latest event happened in the window event stack, but is not supported by the other browsers. For e.g, you want to cancel the default behavior in a specific circumstance, and to do this the way in IE 7.0 is:

window.event.returnValue = true;

But this will not work in FireFox. You then need to check an extra event handler, that is automatically injected in by Firefox and our event handler transforms as following:

function doSomething()
{
  if (!e) // if the parameter is provided by the browser
  {
    e = window.event;
  }     

  if (e.preventDefault)  // firefox style
  {
    e.preventDefault();
  }
  else
  {
    e.returnValue = false;
  }
}

Also if you return false from the listener, the default action will be prevented (such as the post back of a button or a redirection of a link. This is very useful especially in client side validation of forms.

We will continue to talk about the JavaScript problems across the browsers in the following posts, stay cool!

kick it on DotNetKicks.com


Share it on: These icons link to social bookmarking sites where readers can share and discover new web pages.
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google
  • Blogosphere News
  • e-mail
  • YahooMyWeb
  • DotNetKicks
  • DZone