Using jquery to make all forms ajax-powered

Hi,

I recently wrote a piece of hacky javascript to automatically make all my forms powered by AJAX + JSON. I still had to write functions to handle this JSON data, but it saved me time over retrieving values from the form and performing an AJAX request for every form. Plus, if I didn’t need to use JSON and just used DOM to change the innerHTML of something, I’d have saved more time yet.

This is my (commented!) javascript code (note that it requires jquery):

function getSubmits(form) {
    return $(form).children("input[type=submit],button[type=submit]");
}

//default json handler: just return a div with the result as its innerHTML
function default_json_handler(json) {
    var div = $('<div/>');
    div.html (json);
    return div;
}

function ajaxify() {
    var forms = $('form'); //ALL the forms!
    var sidx  = 0;

    forms.unbind();

    forms.each (function(idx, form) {
        //We'll hold the handler function in the form's data-json-handler
        //attribute. window['function_name'] will tell us if the function
        //exists
        var jsonh   = window[$(form).attr('data-json-handler')]
                        || default_json_handler;
        var submits = getSubmits(form);
        submits.unbind();

        var method  = $(form).attr('method');
        var url     = $(form).attr('action');

        submits.click(
            function(e) {
                //the data that we'll submit through ajax
                var params     = [];

                //This may be useful to let your script
                //know that the data is being posted through
                //AJAX; for example I use this to return JSON
                //instead of XML.
             
                params['ajax'] = [1];

                if(!$(this).attr('data-sidx')) {
                    $(this).attr('data-sidx', sidx++);
                }

                //since we clicked a submit button, we'll be submitting
                //its value if it has a name...
                if (name = $(this).attr('name')) {
                    params[name] = [encodeURIComponent($(this).val())];
                }

                //get the rest of the input values
                params  = getInputValues($(this), params);
                //get the request string
                params  = getRequestStr(params);

                //We use this ID so that we have a specific result div for
                //each of our submit buttons
                var div = $('#result' + $(this).attr('data-sidx'));

                if (!div[0]) {
                    div = $('<div/>',{id: 'result' + $(this).attr('sidx')})
                    .appendTo(form);
                }
               
                $.ajax(url, {
                  dataType  :'json',
                  method    :method,
                  data      :params,
                  success   :function(data) {
                    div.empty();
                    div.hide();
                    //call the json handler and append the result.
                    div.append(jsonh(data));
                    div.fadeIn(800);
                    div.show(500, function() {
                        $('html, body').animate({
                            scrollTop: $(div).offset().top
                        }, 1000);
                    });
                  },
                  beforeSend :function() {
                    div.empty()
                    div.append(
                        $("<img src='static/img/loading.gif' />")
                    );
                  }
                });

                e.stopPropagation();
                e.preventDefault();
                return false;
            }
        );
    });
}

//gets a form's input values

function getInputValues(btn, params) {
    //get all the input values, but only checked
    //radios and checkboxes!
    var inputs = btn.parent().find(
        //all inputs that match the criteria
        'input[type!=submit]' +  // We've already handled this
            '[type!=radio]' +    // we handle this below 
            '[type!=checkbox]' + // likewise
            '[type!=button],' +  // their value isn't submitted...
                                 // unless they're submit buttons which
                                 // we've handled.

        ':checked,' +           // elements with the :checked state.
        'select,' +             // selects
        'textarea'              // textareas
    );

    inputs.each(function(idx, input) {
        var name  = $(input).attr('name');
        var value = $(input).val();

        if (name) {
            if (!params[name]) {
                params[name] = []; // We use an array here so that we can handle
                                   // Multiple elements with the same name (checkboxes?)
            }

            params[name].push(encodeURIComponent(value));
        }
    });

    return params;
}

// gets the request string from an array of parameters.
function getRequestStr(params) {
    paramstr ='';

    for (var key in params) {
        for (var i in params[key]) {
            paramstr += (paramstr?'&':'') + key + '=' + params[key][i];
        }
    }

    return paramstr;
}

//calls the ajaxify function on document load
$(document).ready(ajaxify);

This is how our form looks like:

<!-- Pay attention to data-json-handler="result"! It's what we'll use
to handle the json result from ajax! !-->
<form method="post" action="script.php" data-json-handler="result">
<label>
    Text input:<br/>
    <input type="text" name="text" />
</label><br/>

<label>
    Password input:<br/>
    <input type="password" name="password" />
</label><br/>

<label>
    Textarea:<br/>
    <textarea name="textarea"></textarea>
</label><br/>

<label>
    Email input:<br/>
    <input type="email" name="email" />
</label><br/>

<label>
    Date input:<br/>
    <input type="date" name="date" />
</label><br/>


<label>
   <input type="radio" name="radio" value="1" /> Radio option 1
</label><br/>

<label>
   <input type="radio" name="radio" value="2" /> Radio option 2
</label><br/>

<label>
   <input type="radio" name="radio" value="3" /> Radio option 3
</label><br/>


Checkboxes:<br/>

<label>
   <input type="checkbox" name="check[]" value="1" /> Checkbox option 1
</label><br/>

<label>
   <input type="checkbox" name="check[]" value="2" /> Checkbox option 2
</label><br/>

<label>
   <input type="checkbox" name="check[]" value="3" /> Checkbox option 3
</label><br/>

Select:<br/>
<select name="select">
    <option value="">Please select...</option>
    <option value="Cow">Moo!</option>
    <option value="Pig">Oing oing!</option>
    <option value="Cat">Meow!</option>
    <option value="Dog">Woof!</option>
</select>
<br>
<input type="submit" name="submit" value="Submit with name!" />
<button type="submit" name="submit2" type="button" value="submit 2!">Submit &lt;button&gt; with name!</button>
<input type="submit" value="Submit without name!" />

</form>

<script src="jquery.js"></script>
<script src="ajaxify.js"></script>
<script src="jsonhandlers.js"></script>

And our jsonhandlers.js script will have the functions that actually handle the json result. For example:

function result(data) {

    var table = $("<table><tr><th>Name</th><th>Email</th>");
    for (var i = 0; i < data.length; i++) {
        table.append($("<tr><td>" + data[i].name + "</td><td>" + data[i].email + "</td></tr>"));
    }
    
    return table;
}

Of course, script.php will have to return appropriate data. For this example, I just used:

<?php
     echo json_encode(
        array (
            (object)
            array (
                'name' => 'User 1',
                'email' => 'user1@example.com'
            ),
            (object)
            array (
                'name' => 'User 2',
                'email' => 'user2@example.com'
            ),
        )
    );
?>

But in the real world you’d use the input to get data.

You can now see how the form works. You’ll notice when you submit it you get your results in a table, and if you inspect the HTTP request you should see all the data that is sent:

form

You can verify that all the input values are sent to the PHP page.

tl;dr: What our javascript code does is go through every form, and add a handler to the submit buttons. When they are clicked, it uses jquery to find the right elements and get their value. It then constructs a string to perform an AJAX request, including ajax=1 so that our server script knows it was submitted with ajax and act accordingly. After the result is acquired, the function set on the form’s data-json-handler attribute is called and it’s up to you how you handle the ajax.

If you want to change the JSON result behaviour just change the data-json-handler of each form!

That’s all for now. Hopefully this was helpful and not too confusing!

One thought on “Using jquery to make all forms ajax-powered

Leave a Comment

Your email address will not be published. Required fields are marked *