Enforcing TextBox MaxLength with MS AJAX

If you've ever worked on data-driven webforms, then you know how crucial it is to protect against bad user input. One issue that has plagued me for a while is enforcing the MaxLength property on a TextBox when the TextMode property is set to MultiLine. The default behavior for the TextBox is to render an HTML input tag with its type attribute set to 'text'. When the TextMode is set to MultiLine, the TextBox renders and HTML textarea tag. Unfortunately, the textarea element does not have an attribute named 'maxlength', therefore, you can type whatever you want into it.

After about an hour of googling, I found dozens of examples, but none that really seemed to work "properly". By "properly", I'm saying that they didn't work, but most of them relied on the 'onkeyup' or 'onblur' events to trim the value entered after it had already been entered. Although this kind of worked, it didn't seem like the clean behavior you see in the browser when using the TextBox in SingleLine mode. I also had a lot of trouble finding scripts that worked reliably across browsers. To work around the various inconsistancies on different platforms, I made use the Sys.UI.DomEvent class to abstract the event argument properties and browser keycodes. In order to keep this solution as flexible as possible, I provided the sample in a format that does not depend on any server-side code.

Alright, let's get to the code. In order to enforce the maxlength the way I wanted, I knew I would need to create a javascript function to prevent extra keys from being typed into the box. The function I came up with relies on the way javascript event handlers can supress the browser's default behavior by specifying a return value. When the function returns true, the browser continues with it's default behavior. When the function returns false, then the browser essentially prevents the default behavior from occurring. The following was my initial starting point:

function handleKeyPress(e) {
    var textarea = e.target;
    var actualLength = textarea.value.length;
    var maxLength = textarea.maxlength;
    if (actualLength >= maxLength) {
        return false;
    }
    return true;
}

Getting the basics down was easy. The basic idea is that you assign a 'maxlength' expando attribute on the TextBox. This can done using the ClientScriptManager class offered by the framework. This allows you to add attributes to elements of the DOM without invalidating the xhtml content. (NOTE that in the sample, I just used a fixed startup script to assign this attribute to alleviate the necessity for this to use server-side code.) This can be done with the following code in the Page_Load event on the server:

protected void Page_Load(object sender, EventArgs e)
{
    Page.ClientScript.RegisterExpandoAttribute(TextArea1.ClientID, "maxlength", 20);
}

Or with the following code on the client side:

document.getElementById('textarea1').maxlength = 20;

Then, you handle keystrokes made by the user by assigning an event handler to the textarea's onkeypress event. On each keystroke, you need to check the length of the value of the textarea and see if the current keystroke will put you over the limit. This can be done with the following code in the Page_Load event on the server:

protected void Page_Load(object sender, EventArgs e)
{
    TextArea1.Attributes["onkeypress"] = "return handleKeyPress(event);";
}
This yields the following output when the page is rendered:

<textarea id="textarea1" cols="20" rows="3" onkeypress="return handleKeyPress(event);"></textarea>

What proved really difficult was getting it to work reliably across browsers. The final iteration of my function has 3 separate lines marked as hacks and relies significantly on the MS AJAX client-side framework. Each one is to accomodate a behavior of a different browser. Here is how the final function turned out:

function handleKeyPress(e) {
    var domEvent = new Sys.UI.DomEvent(e);
    // Hack to accomodate Firefox inconsistencies with the keyCode
    if (Sys.Browser.agent == Sys.Browser.Firefox && e.keyCode && (e.keyCode === 46)) {
        domEvent.keyCode = 127;
    } else {
        domEvent.keyCode = e.keyCode;
    }
    
    var textarea = domEvent.target;
    var charCode = domEvent.charCode;
    var textareaValue = textarea.value;		
    // Hack to accomodate IE inconsistencies with whitespace
    textareaValue = textareaValue.replace(/\r\n/g, '\n');
    var actualLength = textareaValue.length;

    if (actualLength >= textarea.maxlength) {
        switch(domEvent.keyCode) {
            case Sys.UI.Key.backspace:
            case Sys.UI.Key.tab:
            case Sys.UI.Key.esc:
            case Sys.UI.Key.pageUp:
            case Sys.UI.Key.pageDown:
            case Sys.UI.Key.end:
            case Sys.UI.Key.home:
            case Sys.UI.Key.left:
            case Sys.UI.Key.up:
            case Sys.UI.Key.right:
            case Sys.UI.Key.down:
            case Sys.UI.Key.del:
                return true;
            case Sys.UI.Key.enter:
            case Sys.UI.Key.space:
                return false;
            default: {
                // Handle highlight/replace operations
                if (document.selection) {
                    var range = document.selection.createRange();
                    var rangeElement = range.parentElement();
                    if (rangeElement == textarea) {
                        if (range.text.length > 0) {
                            return true;
                        }
                    }
                } else if (textarea.selectionStart < textarea.selectionEnd) {
                    return true;
                }
            }
        }
        // Hack to accomodate Safari inconsistencies with the keyCode
        if (domEvent.keyCode == 0 && domEvent.charCode == 0) {
            return true;
        }
        return false;
    }
    return true;
}

The first thing to notice is that I've used the Sys.UI.DomEvent class to encapsulate the raw javascript event argument passed to the function. This helps cover up some of the differences among browsers by providing a consistent and reliable set of properties to access. The next issue I had was that IE does not raise the onkeypress event on a textarea when certain keys are pressed - namely the arrow keys - whereas FireFox, Opera, and Safari do. In my first pass at this function, the arrow and delete keys would become non-operational once the value entered hit the maximum length specified. The next issue I faced was that FireFox has some inconsistent keycodes, which make require a bit of normalization at the beginning of the function. Next, it turns out that that when a user enters a hard return in IE, it actually adds a newline AND a readline character into the box. That is why you see that the length check actually occurs against a value where this combination has been normalized to simple newline characters. Next I had to make sure that the function would only supress the event if it was going to increase the length of the value of the textarea. What that means is that pressing the arrow keys, home, delete, and a few others should always be allowed, whereas alphanumeric keys and whitespace needs to get suppressed. Lastly, if a user had typed in the maximum number of characters, they should be able to highlight a few and then press a key to replace the highlighted text. This took some figuring but I got it worked out. It was here where the browser implementations varied the most, but the I was able to get the code working in the 4 big browsers. The only major shortcoming here is that this doesn't handle paste operations. I'll keep that on the TODO list, but from what I've read, IE is the only browser (except for the forthcoming FireFox 3.0) that supprt the 'onpaste' event.

Again, the sample provided is geared to be a purely server-independent solution. All that is required is the script references to the MS AJAX client library files. That being said, it would be pretty easy to convert this into either a specialized TextBox server control or an AJAX control extender. If there is interest in this, of if you any issues/comments, please drop me a line. Thanks for reading!

-Mark

15 Comments

  • Try using a RegularExpressionValidator and set the ValidationExpression property to:

    ^[\s\S]{0,500}$

    This allows 0 to 500 characters in the multi line TextBox.

  • I agree that that works for preventing users from posting too much text, however it does't prevent them from typing more than 500 characters into the box. The validators only perform their validation in the onchange event which is not triggered on every key stroke. Imagine your dismay if you spent 10 minutes typing into a textbox, only to have the form say you have exceeded the 500 character limit? How would you know where your 500th character was? I think it would be preferable to just prevent users from entering more data than they were allowed to in the first place. Combining these techniques would really be the best solution.

    Perhaps I should have come up with a better title for this post. The idea behind this script is to implement a hardstop when typing into the textbox. Thanks for the tip though.

  • Very useful. Thank you!

  • It doesn't work on paste. you can paste a 1000 character

  • Not sure why you need to see all the key codes. A simple function that truncates would be sufficient
    e.g.
    function CheckLength( textBox, maxLength)
    {
    if ( textBox.value.length > maxLength )
    {
    textBox.value = textBox.value.substr(0,maxLength);
    return false;
    }
    return true;
    }

    Then declare the Textbox as


    Use an onBlur event to check size in the case of pasting text.

  • GridViewBoy - I mentioned in the article that I haven't worked out the paste operations yet. Not all browsers support an onpaste event.

    mdmasonmbcs - The reason not do use the onkeyup event to just truncate the text is that the onkeydown event actually puts the character in the box. When you reach the limit, you get this wierd behavior that seems like a backspace for each key you type. The point is to simulate the way the maxlength property of an input works when using a textarea, not to simply strip out characters.

  • In case of web application, a simple Javascript function can be used instead of lengthy complicated code.


    declare the textarea like this..







    declare this javascript function inside the part

    function handleKeyPress(event,maxLength) {
    var textarea;
    var keyValue = 0;
    var isNetScape = (navigator.appName == "Netscape") ? 1 : 0;
    if(!isNetScape) { //for IE
    keyValue = event.keyCode;
    textarea = event.srcElement;
    } else { // for mozilla/opera...
    keyValue = event.which;
    textarea = event.target;
    }
    var actualLength = textarea.value.length;
    if (actualLength >= maxLength) {
    switch (keyValue){
    case 8:
    case 0:
    return true;
    default:
    return false;
    }
    }
    return true;
    }

  • We use the MaskedEditExtender control from the AJAX toolkit:



    This will prevent further input once the limit is reached, unlike the regex validator techniques.

    Andy

  • Enforcing textbox maxlength with ms ajax.. I like it :)

  • Enforcing textbox maxlength with ms ajax.. Keen :)

  • Enforcing textbox maxlength with ms ajax.. Keen :)

  • WgynW0 The topic is pretty complicated for a beginner!...

  • complicated for a beginner !!!



  • Enforcing textbox maxlength with ms ajax.. I like it :)

  • Hello to all, because I am actually keen of reading this webpage's post to be updated regularly. It consists of good stuff.

Comments have been disabled for this content.