AJAX Form Submit

Here is a simple example using the Yahoo! User Interface (YUI 2) javascript library to hijack a form and use AJAX to submit and process. Using unobtrusive javascript to hijack the form's submit event allows for a progressive enhancement web design strategy. In other words, our web application begins content, then semantic markup (XHTML), then a presentation layer (CSS), and finally the behaviour layer (Javascript/AJAX). This way our application is compatible with non-javascript browsers from the get-go and there is no need to build separate versions of the application, duplicate logic, etc. The idea is that we plan for AJAX from the beginning, and implement that AJAX at the end.

This example can be seen live here: AJAX Form Submit Example. It might be good to view the source of this example and have it open while reading this post.

AJAX Form Submit Markup

We first write the content for our page and then markup that content with semantic, valid (X)HTML. No inline CSS, no inline javascript, just clean (X)HTML. For simplicity, I'm only showing the markup for the form here.

<form id="exampleForm" method="post" action="request.php">
  <div>
    Name: <input type="text" name="name"/>
    <input type="submit" value="Submit"/>
  </div>
</form>
<div id="ajaxDiv">&nbsp;</div>

Notice how this is a pretty standard HTML form. There are no onsubmit or onclick attributes or any other inline javascript.

AJAX Form Submit Server-Side PHP

Before we bring in the javascript and AJAX, we want to write the server-side code to handle the standard form's POST request. For this example we will be using the same PHP script to handle both the standard HTTP POST request as well as the AJAX (XMLHttpRequest or XHR) POST request. This keeps it simple. A more complicated application may have a different server-side strategy.

If the post is not and XHR request (which it is not yet because we have not yet added the behavior layer) then the script will output the results as full XHTML page. To see an example of this in action. disable javascript in your web browser and try the AJAX Form Submit Example. If the PHP script detects an XHR request, it will still output the message but will omit the full XHTML document structure (<html>, <body>, etc.) and instead allow the javascript to insert that markup into the existing page.

To put that another way, the PHP processing script outputs a full XHTML document for standard HTTP POST requests and outputs a partial XHTML snippet for XHR requests.

<?php
// use to detect an XHR request
define('IS_AJAX', !empty($_SERVER['HTTP_X_REQUESTED_WITH']) 
                  && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest');

// only show document head markup if it's an XHR request
if (!IS_AJAX) {
    echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" ';
    echo '"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> ';
    echo '<html xmlns="http://www.w3.org/1999/xhtml">';
    echo '<head><title>Form Submission</title></head><body>';
}

// this is the same for HTTP and XHR requests
if (empty($_POST['name'])) die("You did not specify your name!");
$name = htmlentities($_POST['name']);
echo "<p>Hello <strong>$name</strong>, how are you?</p>";

// only if it's an XHR request
if (!IS_AJAX) {
    echo '</body></html>';
}
?>

We should now have a completely functioning form that has clean markup and functions even in non-javascript browsers.

Hijack Form with Javascript

We can now write the javascript which will intercept or "hijack" the form's submit event and send an XHR request instead. In the AJAX Form Submit Example this javascript is all included in the <head> of the document, however, this could also be placed into an external javascript file. For my example, I am using the YUI 2 Connection Manager and the Event Utility. These javascript files must be included before our own javascript:

<script type="text/javascript" src="http://yui.yahooapis.com/combo?2.8.1/build/yahoo/yahoo-min.js&2.8.1/build/event/event-min.js&2.8.1/build/connection/connection-min.js"></script> 

Since our server-side PHP script detects the type of request, we don't change where we are sending the request. Essentially what we are doing is replacing the behavior associated with a form being submitted (the browser sending a POST request to a new URL) with our own javascript. We want our javascript to be setup as the handler for the submit event before the event occurs--ideally as soon as the markup is available to our script. YUI provides a nice function for doing just that, onDOMReady().

The onDOMReady() function will run as soon as the DOM is in a usable state. This is preferred over using something like a load event as the onDOMReady function is not going to overwrite existing code.

// hijack the form once the DOM is in a ready state
YAHOO.util.Event.onDOMReady(function() {
    hijackForm("exampleForm", "ajaxDiv");
}); 

In the above snippet, we have setup a call to hijackForm() to run when the DOM is in a usable state. This is a function that we will write next. Notice that we are passing 2 strings to the hijackForm() function. The first string is the id attribute of the <form> element, and the second string is the id of a <div> element which will be loaded with the response from the XHR request.

And now we can write the hijackForm() function.

function hijackForm(formId, updateId) {
    var formObj = document.getElementById(formId);

    // this is the callback for the form's submit event
    var submitFunc = function (e) {
    
        // prevent default form submission
        YAHOO.util.Event.preventDefault(e);
        
        // define a YUI callback object
        var callback = {
            success: function(o) { 
                document.getElementById(updateId).innerHTML = o.responseText; 
            },
            failure: function(o) { 
                alert("AJAX request failed!");
            }
        }
        
        // connect to the form and submit it via AJAX
        YAHOO.util.Connect.setForm(formObj);
        YAHOO.util.Connect.asyncRequest(formObj.method, formObj.action, callback);
    }
    
    // call our submit function when the submit event occurs
    YAHOO.util.Event.addListener(formObj, "submit", submitFunc);
}

When the hijackForm() function is called (when the DOM is ready), 2 variables are defined and the YAHOO.util.Event.addListener() method is called. The first variable fromObj stores the form DOM object which was obtained from the specified form id. The second variable submitFunc stores a function which we will explain in more detail in a moment. The important part to understand is that in the hijackForm() method we have only defined the function stored in the submitFunc variable. The code within the submitFunc function does not run when hijackForm() is called.

That's where the call to YAHOO.util.Event.addListener() comes in to play. This method will connect the function we defined in the submitFunc variable to the form's submit event. In other words, when either the user clicks the submit button or the form's submit() javascript method is called, our submitFunc function will run. When the page is loaded and displayed to the user, the form has already been hijacked and our submitFunc code is waiting or "listening" for that submit event to occur.

The AJAX Form Submit Function

When the user clicks that submit event and our function stored in the submitFunc variable runs, a few things happen. First, the default handler for the form's submit event is supressed. We don't want the browser to send a new HTTP request taking the user to a new page.

YAHOO.util.Event.preventDefault(e);

Next, we define a YUI Callback Object to pass to the asyncRequest() method later. This callback object will define a callback function to run if the XHR request succeeds and another if the XHR request fails. In the callback function for success we update the innerHTML of the <div> object passed to the hijackForm() method as the second parameter.

document.getElementById(updateId).innerHTML = o.responseText;

And finally, we connect the form to the YUI Connection object and send our AJAX request. Notice how we are using the <form> element's existing 'method' and 'action' attributes as parameters to the asyncRequest() method. The same request URL and method (POST) that was used for an HTTP request will also be used by the XHR request.

YAHOO.util.Connect.setForm(formObj);
YAHOO.util.Connect.asyncRequest(formObj.method, formObj.action, callback);

And there you have it. I tried to keep things simple in this example. In a real-world application there would likely be a lot more error handling in both the server-side design and the AJAX stuff.

Did you enjoy AJAX Form Submit? If you would like to help support my work, A donation of a buck or two would be very much appreciated.
blog comments powered by Disqus
Linux Servers on the Cloud IN MINUTES