Introducing TextBox Limiter Control Ajax Control Toolkit Extender
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:
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:
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: ASP.NET,Javascript,HTML,AJAX,AJAX Control Toolkit











mdmasonmbcs said,
August 4, 2008 @ 11:41 am
Good stuff, only one problem - it doesn’t handle delete or backspace once you reach the max number of characters.
To get it to work in FireFox I replaced ‘textContent’ with ‘value’ in the _refreshCountTextBox function.
Sidar Ok said,
August 16, 2008 @ 12:22 pm
Hi Michael,
Sorry for the delay - I was on my annual holidays
I tried the delete & backspace problem, it does seem to work: could you provide the exact steps so I can repro ? Currently I set the maxlength to 50 and typed 50 chars, saw 0 chars left and pressed backspace, and it worked as expected.
OTOH, thanks for the firefox fix ! I’ll update the source and the article now. Much appreciated!
Pedro said,
March 10, 2009 @ 3:38 pm
Hi.
When I try your code in Internet Exporer works perfectly, but in Firefox it blocks when i reach to the limit (50) in the code example, as happends to mdmasonmbcs, and the contextual menu and CTRL+V don’t work.
Knordy said,
July 1, 2009 @ 9:40 am
So I’ve tried to use your extender and tested it on: Internet Explorer 8.0, FireFox 3.0.11, Chrome 2.0.172.33, Opera 9.64, and Safari (for windows) 3.2.1
Firefox and Opera seems to cancel every button event after the max is reached, which for the Backspace isn’t useful. I’ve solved this (maybe not the best solution) by editing the _onKeyPress after the if (e){ adding a if(e.charcode != 8). 8 is the Backspace and it seem to work in all the browsers.
Still Firefox and Opera cancel all other button events like ‘delete’, ‘ctrl-a’, the arrow buttons, etc, but other browsers keep working.
Also only in Internet Explorer the copy function works. Chrome, Safari, Firefox does place the copied text in the box and Opera places the copied text in the box and exceeds the maximum length.
Will try to find a solution/workaround for this.
Knordy said,
July 1, 2009 @ 9:42 am
Correction (firefox, safari & chrome DOESN’T copy the text in the textbox):
Also only in Internet Explorer the copy function works. Chrome, Safari, Firefox doesn’t place the copied text in the box and Opera places the copied text in the box and exceeds the maximum length.
Knordy said,
July 2, 2009 @ 9:22 am
The pasting in other browsers doesn’t work because of this code in the _onPaste:
var oTR = control.document.selection.createRange();
var copiedData = window.clipboardData.getData(“Text”).substr(0, insertLength);
The first line I changed to, where I’m not completly sure if this is correct:
if (document.getSelection)
var selection = document.getSelection(); // Navigator 4.0x
else if (document.selection && document.selection.createRange) {
var oTR = document.selection.createRange(); // IE 4.0x
var selection = oTR.text
}
else
var selection = window.getSelection(); //other?
The second line I can’t find a solution to access the clipboard data in other browsers. For Firefox for example this is because of security reasons. “clipboardData.getData” is only useful for IE.
Knordy said,
July 3, 2009 @ 9:35 am
Again testing on every browser mention above I found that Firefox didn’t show the counter (even after the fix of ‘mdmasonmbcs’. So guessing his fix worked, but perhaps not on the newest version of FF I conmbined the old code with the new one:
So in the refreshCountTextBox I placed this:
if (countTextBox) {
if (innerTextEnabled) {
// Chrome, Internet Explorer, Opera, Safari (for Windows)
countTextBox.innerText = (maxLength - control.value.length) + “innerText”;
}
else if (countTextBox.textContent != undefined) {
// Firefox
countTextBox.textContent = (maxLength - control.value.length) + “textContent”;
}
else {
// Possible bug fix?
countTextBox.value = (maxLength - control.value.length) + “else”;
}
Which seems to does the trick, to use a label as the counter presentation
Alan said,
September 25, 2009 @ 2:51 pm
This control has been working great until I tried to put it in a Template Column in a datagrid and display many at one time. I have a page with about 100 rows and a TextBoxLimiter linked to textbox on each row. In this situation, there is a considerable lag when keying. It’s as if all textboxes in the datagrid are being evaluated on each keystroke, rather than just the textbox being edited. I tried looking through the source code but I am not versed enough in AJAX and javascript to determine what might be happening. Any insight is appreciated. Thanks.
Chaithra said,
August 22, 2012 @ 9:25 am
its working fine in 2.0 and 3.5. Can U do this same thing in 4.0?
I tried a lot to convert same thing to 4.0 using ajax version 4.0 but could not succeed, so can u help me in achieving this…..