Previous Page
Next Page

Hack 12. Submit Text Field or textarea Values to the Server Without a Browser Refresh

Create a smooth transition between entering information into a textarea or text field and instantly transferring the data to the server.

Ajax applications can automatically send to a server program the information that the user has entered into a text field or textarea. The application code waits for the text widget's onblur event to occur, then uses the request object to send just the data from that field or textarea. In many applications, this technique is preferable to requiring the user to click a Submit button, then sending all of the form's values to the server in a big clump. It is also much snappier in terms of the application's responsiveness. For example, an online quiz or teaching application can fetch and display the correct answer to a question as soon as the user has moved away from the field, instead of requiring the user to click a button and refresh the page just to see specific answers. Real-time language translation is another possible application for this user-interface behavior.

The onblur event is triggered when a web form control such as a text field loses the keyboard focus, which is typically caused by the user pressing the Tab key or clicking outside of the field. You can also use the onkeypress, onkeydown, or onkeyup event handlers to respond to user interaction with a text widget.


Here is this hack's sequence of events for sending text to the server:

  1. The user tabs into the field or clicks in a textarea.

  2. The user types some text.

  3. The user then presses Tab or clicks on another part of the page to exit the text field or textarea.

One issue with intervention-less form sending is that users are not accustomed to this kind of behavior from web forms. A user might be put off or confused by web-form controls such as text fields that dynamically submit their own data. The user interface should make it clear that "something is going to happen" when the user is finished with the text field, or display a message or progress indicator when the request object is sending the data. In addition, depending on the sensitivity of the task, you may want to add another layer of communication with the user, such as an alert window asking "Are you sure you want to submit the information?"


This hack includes a text field and a textarea that send HTTP requests with their values when the user is finished with them. Figure 2-1 shows the web page loaded into a browser window.

Figure 2-1. No buttons need apply


The user types some information into the text field or textarea (the larger data-entry box) and then exits the control, and the application automatically sends what the user typed to a server component. Here is the HTML code for this page:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
 "http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <script type="text/javascript" src="js/hacks_2_1.js"></script>
    <link rel="stylesheet" type="text/css" href="/css/hacks.css" />
    <title>Submit your information</title>
</head>
<body>
<h3>Get stats from textareas and textfields using Ajax</h3>
<form action="javascript:void%200" >
<div id="textf">
Enter a few words for submitting to our server: 
<input type="text" name="tfield" id="tfield" size="35" />
</div>
<div id="texta">
<span style="vertical-align: top">Enter a phrase for submitting to our
 server:</span> <textarea name="tarea" rows="20" id="tarea" cols="20">
</textarea>
</div> 
</form>
</body>
</html>

Instead of a user clicking a button to send the form information, each text control sets the action in motion itself.

When the user presses Tab or clicks outside of one of the text widgets, the code specified by the widget's onblur event handler is executed. The upcoming code sample shows how this event handler is set up after the browser has finished loading the page.

The script tag in the HTML imports a JavaScript file, hacks_2_1.js. This file contains all the code necessary for running this hack. The following sample includes all the code for sending a request and handling the return value (in the handleResponse( ) function). "Display Text Field or textarea Values Using Server Data" [Hack #13] explains the related technique of inserting the server's response into text controls, but that shouldn't prevent you from peeking at handleResponse( ) if you want! Here's the relevant JavaScript code:

var formObj = null;
var formObjTyp = "";
var request=null;

//input field's event handlers
window.onload=function(  ){
    var txtA = document.getElementById("tarea");
    if(txtA != null){
        txtA.onblur=function(  ){if (this.value) { getInfo(this);}};  }

    var tfd = document.getElementById("tfield");
    if(tfd != null){
        tfd.onblur=function(  ){if (this.value) { getInfo(this);}};  }
}

function getInfo(obj){
    if (obj == null ) { return; }
    formObj=obj;
    formObjTyp =obj.tagName;
    if(formObjTyp == "input" || formObjTyp == "INPUT"){
        formObjTyp = formObjTyp + " "+formObj.type;
    }
    formObjTyp = formObjTyp.toLowerCase(  );
    var url = "http://www.parkerriver.com/s/webforms?objtype="+
            encodeURIComponent(formObjTyp)+"&val="+ encodeURIComponent(obj.value);
    httpRequest("GET",url,true);
}

//event handler for XMLHttpRequest
function handleResponse(  ){
    try{
        if(request.readyState == 4){
            if(request.status == 200){
                var resp = request.responseText;
                var func = new Function("return "+resp);
                var objt = func(  );
                if(formObjTyp == "textarea"){
                    if(formObj != null){
                        formObj.value = objt.Form_field_type +
                                " character count: "+objt.Text_length+
                                "\\nWord count: "+
                                objt.Word_count+"\\nServer info: "+
                                objt.Server_info;
                    }
                } else if(formObjTyp == "input text"){
                    if(formObj != null){
                        formObj.value = objt.Form_field_type +
                                " # characters: "+objt.Text_length+
                                " Word count: "+objt.Word_count; }
                }
            } else {
                //request.status is 503 
                //if the application isn't available; 
                //500 if the application has a bug
                alert(
                        "A problem occurred with communicating between the "+
                        "XMLHttpRequest object and the server program.");
            }
        }//end outer if
    } catch (err) {
        alert("It does not appear that the server is available "+
              "for this application. Please"+
              " try again very soon. \\nError: "+err.message);

    }
}

/* Initialize a request object that is already constructed */
function initReq(reqType,url,bool){
    try{
        /* Specify the function that will handle the 
        HTTP response */
        request.onreadystatechange=handleResponse;
        request.open(reqType,url,bool);
        request.send(null);
    } catch (errv) {
        alert(
                "The application cannot contact the server "+
                "at the moment. "+
                "Please try again in a few seconds." );
    }
}
/* Wrapper function for constructing a request object.
 Parameters:
  reqType: The HTTP request type, such as GET or POST.
  url: The URL of the server program.
  asynch: Whether to send the request asynchronously or not. */
function httpRequest(reqType,url,asynch){
    //Mozilla-based browsers
    if(window.XMLHttpRequest){
        request = new XMLHttpRequest(  );
    } else if (window.ActiveXObject){
        request=new ActiveXObject("Msxml2.XMLHTTP");
        if (! request){
            request=new ActiveXObject("Microsoft.XMLHTTP");
        }
     }
    //the request could still be null if neither ActiveXObject
    //initialization succeeded
    if(request){
       initReq(reqType,url,asynch);
    } else {
        alert("Your browser does not permit the use of all "+
              "of this application's features!");}
}

The code declares two top-level JavaScript variables: formObj and formObjTyp. The former variable holds the input or textarea object (other functions in the code will need access to it later), and the latter holds a string representing a form object tag name, such as "INPUT" or "TEXTAREA." This string is one of the parameters that the server component requires (see the formatted URL that appears at the end of the next section, "Get the First Serve In").

These variables are simply part of this hack's behavior and, in general, are not required for sending form values with the request object.


As mentioned previously, the code sets up the text widgets' onblur event handlers when the browser finishes loading the page. You can accomplish this task in JavaScript by assigning a function to the window's onload event handler. Using the window.onload code, as follows, is an alternative to calling the JavaScript functions from within an HTML element's onblur attribute:

window.onload=function(  ){
    var txtA = document.getElementById("tarea");
    if(txtA != null){
        txtA.onblur=function(  ){if (this.value) { getInfo(this);}};  }
    var tfd = document.getElementById("tfield");
    if(tfd != null){
        tfd.onblur=function(  ){if (this.value) { getInfo(this);}};  }
}

These text fields are now hot. Once the user types a value and exits a control, the information entered is off and running to the server; the user doesn't have to click another button to send it.

Event handlers are simply attributes of an object to which your code can assign a function or block of code that defines some behavior. So, if you want to control how a radio button behaves when it's clicked, set up its onclick event handler. For example:

//Get a reference to a radio button element
//on a web page
var rad = document.getElementById("radio1");
//display a pop-up dialog window when it's clicked
rad.onclick=function( ){ alert("I was clicked!");};


Get the First Serve In

The main job of the text-field event handlers is to call the getInfo( ) function. This function grabs whatever the user typed into the text widget and sends this value to the server:

function getInfo(obj){
    if (obj == null ) { return; }
    formObj=obj;
    formObjTyp =obj.tagName;
    if(formObjTyp == "input" || formObjTyp == "INPUT"){
        formObjTyp = formObjTyp + " "+formObj.type;
    }
    formObjTyp = formObjTyp.toLowerCase(  );
    var url = "http://www.parkerriver.com/s/webforms?objtype="+
              encodeURIComponent(formObjTyp)+"&val="+
              encodeURIComponent(obj.value);
    httpRequest("GET",url,true);
}

The getInfo( ) function takes as a parameter an object that represents the text field or textarea. We pass in references to the input or textarea objects so that the JavaScript code can use them to handle the server return value.

"Display Text Field or textarea Values Using Server Data" [Hack #13] shows how to display the server's return value inside these text widgets. Because a textarea generally holds more information than a text field, the server sends back more data if the original object was a textarea as opposed to a text field.


The last part of the previous code, httpRequest("GET",url,true), is the function call that actually sends the user's information to the server.

However, a few things have to occur before the code calls that function, such as putting together a proper URL (the server's address on the Internet). The server component is expecting a string describing the kind of form object from which the data derives. In this application, the string is formulated from the tagName property of the Element object (returning INPUT or TEXTAREA).

The code needs this value to tell the server whether its return value will be inserted into a text field or a textarea. Again, this is described in "Display Text Field or textarea Values Using Server Data" [Hack #13].


The code further refines the input object's description by what input subtype it represents (text input, radio button, etc.). This is accomplished by appending the value of the input object's type property (text, in this case) to the string input, which creates the final string input text.

In other words, this type property returns "text" only if the object represents an <input type="text" ...> HTML element. Then the string is forced to lowercase and submitted to the server with the user's content:

formObjTyp =obj.tagName;
if(formObjTyp == "input" || formObjTyp == "INPUT"){
    formObjTyp = formObjTyp + " "+formObj.type;
}
formObjTyp = formObjTyp.toLowerCase(  );
var url = "http://www.parkerriver.com/s/webforms?objtype="+
          encodeURIComponent(formObjTyp)+"&val="+ encodeURIComponent(val);
httpRequest("GET",url,true);

The global JavaScript function encodeURIComponent( ) is a method for ensuring that certain characters, such as spaces, are encoded when they are included in URLs. Otherwise, your program may send a partial or truncated URL to the server and generate an error. The entire URL might look like this in a real case:

http://www.parkerriver.com/s/webforms?objtype=input%20text&
val=Hello%20There!

What's Next?

The httpRequest( ) function wraps the code that initializes and uses the request object, which works behind the scenes so that the user doesn't have to manually send the data. "Use the Request Object to POST Data to the Server" [Hack #2], among others, describes this function in detail.

So what happens to the submitted data next? That depends on your application. The next hack explores a related but different topic: using JavaScript and Ajax to take an HTTP response and insert data into an existing text field or textarea.

Users can put tons of information in a large textarea, so in these cases use the POST method rather than GET with the request object. For example, you can write the httpRequest( ) function as httpRequest("POST",url,true), and the request object's send( ) method will have the POST querystring as a parameter:

request.send(val=Hello%20there%20and%20a%20lot%20of%20other%20stuff);
                  
                  
               



Previous Page
Next Page