HTML Table Inline Edit - Part One - CodeProject

:

Introduction

The jQuery library is a very powerful client-side framework. One of its strengths is that jQuery is very good at DOM (Document Object Model) manipulation. This can be demonstrated by implementing HTML table inline editing by jQuery. The code in this article presents a simple implementation of Gridview style inline editing by jQuery. Gridview is an ASP.NET server control with inline editing function. In part two of this article, I am going to show an editor style HTML table inline editing by jQuery.

Using the Code

First, add the following HTML markup to a web page.

<div>
    <div class="errorSummary"></div>
    <table id="tblCats" class="simpleTable">
        <tr>
            <th>Category Id</th>
            <th>Category Name</th>
            <th>Short Description</th>
            <th>Action</th>
        </tr>
    </table>
</div>

Second, add a JavaScript global object to serve as table data source.

// Create a global variable by module pattern
var catObj = (function () {
    // Private variables
    var cat = [];
    var maxCatId = 0;
    // Return object
    return {
        // Public method to get array cat
        GetCats: function () {
            return cat;
        },
        // Public method to add an item to array cat
        AddCat: function (cat_name, short_desc) {
            var cat_id = ++maxCatId;
            cat.push({ cat_id: cat_id, cat_name: cat_name, short_desc: short_desc });
        },
        // Public function to update an item in array cat
        UpdateCat: function (cat_id, cat_name, short_desc) {
            for (var i = 0; i < cat.length; i++) {
                if (cat[i].cat_id == cat_id) {
                    cat[i].cat_name = cat_name;
                    cat[i].short_desc = short_desc;
                    break;
                }
            }
        },
        // Public method to delete an item from array cat
        DeleteCat: function (cat_id) {
            var j = undefined;
            for (var i = 0; i < cat.length; i++) {
                if (cat[i].cat_id == cat_id) {
                    j = i;
                    break;
                }
            }
            if (j != undefined) {
                cat.splice(j, 1);
            }
        }
    };
})();

The construction of this global object uses JavaScript module pattern. The object created by this pattern behaves like an object created by a classic object-oriented language such as C#. This object exposes public methods to manipulate a private array variable. The private variables of this object are protected and cannot be accessed outside of the object itself. A detailed explanation of module pattern can be found in the following link.

Last, add some jQuery code to do inline editing.

$(document).ready(function () {
    // Initialize object catObj
    catObj.AddCat('cat name 1', 'short desc 1');
    catObj.AddCat('cat name 2', 'short desc 2');
    catObj.AddCat('cat name 3', 'short desc 3');
    catObj.AddCat('cat name 4', 'short desc 4');
    catObj.AddCat('cat name 5', 'short desc 5');
    catObj.AddCat('cat name 6', 'short desc 6');
    // Populate table with data from object catObj
    PopulateTable(catObj.GetCats());
});

// Table inline editing function
function DoEdit(thisLink) {
    if ($(thisLink).text() == 'Add') {
        // Add a new record
        var catName = $(thisLink).parent().parent().find('td:eq(1) > input').val();
        var shortDesc = $(thisLink).parent().parent().find('td:eq(2) > input').val();
        if (catName == '' || shortDesc == '') {
            $('div.errorSummary').html('Both Category Name and Short Description cannot be empty!');
            $('div.errorSummary').show();
        }
        else {
            catObj.AddCat(catName, shortDesc);
            PopulateTable(catObj.GetCats());
        }
    }
    else if ($(thisLink).text() == 'Delete') {
        // Delete a record
        var catId = parseInt($(thisLink).parent().parent().find('td:first').html());
        catObj.DeleteCat(catId);
        PopulateTable(catObj.GetCats());
    }
    else if ($(thisLink).text() == "Edit") {
        // Start inline editing. Transform text to input for editing
        var elTr = $(thisLink).parent().parent();
        var catName = elTr.find('td:eq(1)').html();
        var shortDesc = elTr.find('td:eq(2)').html();
        elTr.find('td:eq(1)').html('<input type="text" 
        data-oldvalue="' + catName + '" />');
        elTr.find('td:eq(1) > input').val(catName);
        elTr.find('td:eq(2)').html('<input type="text" 
        data-oldvalue="' + shortDesc + '" />');
        elTr.find('td:eq(2) > input').val(shortDesc);
        elTr.find('td:eq(3)').html('<a href="#">Update</a>
        &nbsp;&nbsp;<a href="#">Cancel</a>');
        elTr.find('td:eq(3) a').click(function (e) {
            e.preventDefault();
            DoEdit(this);
        });
    }
    else if ($(thisLink).text() == 'Cancel') {
        // Cancel inline editing. Transform input back to text.
        var elTr = $(thisLink).parent().parent();
        var catName = elTr.find('td:eq(1) > input').attr('data-oldvalue');
        var shortDesc = elTr.find('td:eq(2) > input').attr('data-oldvalue');
        elTr.find('td:eq(1)').html(catName);
        elTr.find('td:eq(2)').html(shortDesc);
        elTr.find('td:eq(3)').html('<a href="#">Edit</a>
        &nbsp;&nbsp;<a href="#">Delete</a>');
        elTr.find('td:eq(3) a').click(function (e) {
            e.preventDefault();
            DoEdit(this);
        });
    }
    else if ($(thisLink).text() == 'Update') {
        // Update data
        var elTr = $(thisLink).parent().parent();
        var catId = parseInt(elTr.find('td:first').html());
        var catName = elTr.find('td:eq(1) > input').val();
        var shortDesc = elTr.find('td:eq(2) > input').val();
        if (catName == '' || shortDesc == '') {
            $('div.errorSummary').html('Both Category Name and Short Description cannot be empty!');
            $('div.errorSummary').show();
        }
        else {
            catObj.UpdateCat(catId, catName, shortDesc);
            PopulateTable(catObj.GetCats());
        }
    }
}

function PopulateTable(data) {
    // Hide error summary section
    $('div.errorSummary').hide();
    $('#tblCats tr:gt(0)').remove();
    // Populate table with data source from function parameter
    $(data).each(function () {
        var row = '<tr><td>' + this.cat_id + '</td><td>' +
                this.cat_name + '</td><td>' + 
                this.short_desc + '</td><td>' +
                '<a href="#">Edit</a>&nbsp;
                &nbsp;<a href="#">Delete</a></td></tr>'
        $('#tblCats').append(row);
    });
    $('#tblCats').append('<tr><td></td>
    <td><input type="text" />' +
            '</td><td><input type="text" />
            </td><td><a href="#">Add</a></td></tr>');
    $('#tblCats a').click(function (e) {
        e.preventDefault();
        DoEdit(this);
    });
}

The above code shows jQuery is very good at selecting and manipulating DOM elements. For example, look at this line of code.

var catName = $(thisLink).parent().parent().find('td:eq(1) > input').val();

This line of code can be explained as this:

  • Wraps a link element inside a jQuery object.
  • Gets the link's parent which is a td element.
  • Gets the td's parent which is a tr element.
  • Finds the input element which is the immediate child of the second td element of this tr.
  • Gets the value of the input element.

Since in many cases, a jQuery function returns a jQuery object, the above functions can be chained together to get a concise and clean programming style.

If the data source is on the server side, the code in this article can be modified to use Ajax calls or form post actions to manipulate server side data source.

Points of Interest

One of jQuery strengths is manipulating DOM.