As developers, we're always asked to do more for our users. They want their Web pages faster, smaller, and with more features. This means that you have to start working more in JavaScript and jQuery on the client-side. By doing more client-side coding, you reduce post-backs to the server, thereby increasing performance. In this first article of a series on working within HTML and the Web API, I'll show you how to add, edit, and delete data in an HTML table using JavaScript and jQuery, but no post-backs. In subsequent articles, you'll learn how to take that data and use the Web API to retrieve and modify this data.

To demonstrate the concepts for this article, I created a page called Paul's Training Videos (Figure 1). This page allows me to list all of my training videos on Pluralsight, and eventually, to add, edit, and delete them. I'm using Bootstrap in order to have a nice appearance for my Web page, but it's not required.

Add a Product

The HTML page I use to illustrate these concepts is shown in Listing 1. This HTML creates an empty table with a <thead> for the headers of each column in the table. There are three columns: product name, introduction date, and URL. Note that there's no <tbody> for this table. I've left the <tbody> element off on purpose to illustrate how you check for that in jQuery, and add the <tbody> element, if necessary.

Figure 1: List products by adding rows to a table when the page loads.
Figure 1: List products by adding rows to a table when the page loads.

Add Product Rows to the Table

At the bottom of this HTML page, I created a <script> tag with a function called productsAdd(). This function uses the append() method to add a <tr> with the appropriate number of <td> tags to form a row for your table. Use a jQuery selector to locate the ID attribute of your table and the <tbody> tag, and append a <tr> and the <td> tags as shown in the following code snippet.

function productsAdd() {
    $("#productTable tbody").append("<tr>" + 
        "<td>My First Video</td>" +
        "<td>6/11/2015</td>" +
        "<td>www.pluralsight.com</td>" +
        "</tr>");
}

The HTML shown in Listing 1 didn't include a <tbody> tag within the table. If you run the code shown in the snippet above, no rows are appended to your table because the result of the selector $("productTable tbody") comes back as null. You should always have a <tbody> in all of your tables. If you don't, it's no problem because you can add one programmatically. Modify the code shown above to check to see if the selector of your table ID and the <tbody> returns something. If it doesn't, append a <tbody> tag to the table prior to calling the append() with the rows you wish to add. You do this with the following code.

if ($("#productTable tbody").length == 0) {
    $("#productTable").append("<tbody></tbody>");
}

Listing 1: HTML Table Markup

<div class="container">
    <div class="row">
        <div class="col-sm-6">
            <h2>List Products</h2>
        </div>
    </div>
    <div class="row">
        <div class="col-sm-6">
            <table id="productTable" class="table table-bordered table-condensed table-striped">
                <thead>
                    <tr>
                        <th>Product Name</th>
                        <th>Introduction Date</th>
                        <th>URL</th>
                    </tr>
                </thead>
            </table>
        </div>
    </div>
</div>

The complete method to add a couple of rows to the HTML table is shown in Listing 2

Listing 2: JavaScript function to add rows to an HTML table

<script>
    // Add products to <table>
    function productsAdd() {
        // First check if a <tbody> tag exists, add one if not
        if ($("#productTable tbody").length == 0) {
            $("#productTable").append("<tbody></tbody>");
        }
        
        // Append product to the table
        $("#productTable tbody").append("<tr>" +
            "<td>Extending Bootstrap with CSS, JavaScript and jQuery</td>" +
            "<td>6/11/2015</td>" +
            "<td>http://bit.ly/1SNzc0i</td>" +
            "</tr>");
            
        $("#productTable tbody").append("<tr>" +
            "<td>Build your own Bootstrap Business Application Template in MVC</td>" +
            "<td>1/29/2015</td>" +
            "<td>http://bit.ly/1I8ZqZg</td>" +
            "</tr>");
}
</script>

You can call the above function when the document is loaded by adding the jQuery document.ready function just before the ending </script> tag.

$(document).ready(function () {
    productsAdd();
});

Add Rows Dynamically

Let's make the page a little more dynamic by gathering the product data from the user and adding that data to the table. Add three input fields for the user to input data to add to the product table. The user enters a product name, introduction date, and the URL of the video, as shown in Figure 2. After entering the data into these fields, that data is retrieved from the input fields and added to a new row in the HTML table.

Figure 2: Add products to table with user input.
Figure 2: Add products to table with user input.

In addition to the new input fields, a <button> is added that when clicked, adds the data from the fields into the table. This button, shown at the bottom of Figure 2, is a normal HTML button with a function named productUpdate called from its onclick event.

<button type="button" 
        id="updateButton" 
        class="btn btn-primary" 
        onclick="productUpdate();">Add</button>

Add a Row from the Input Fields

Once the user adds the data in the input field, they click on the Add button. In response to this click event, the productUpdate() function is called, as shown in the following code snippet.

function productUpdate() {
    if ($("#productname").val() != null && $("#productname").val() != '') {
        // Add product to Table
        productAddToTable();

        // Clear form fields
        formClear();

        // Focus to product name field
        $("#productname").focus();
    }
}

If the Product Name field is filled in with some data, then the productAddToTable function is called to build the new row for the table. Once this function is run, formClear() is called to clear the input fields to prepare for the next row to be added. Finally, input focus is given to the Product Name Input field.

The productAddToTable() function, shown in Listing 3, is similar to the code I wrote earlier when I hard-coded the values. The difference in this method is that it uses jQuery to retrieve the values from the text boxes and build the <td> elements from those values.

Listing 3: Retrieve values from input fields and build a row for a table

function productAddToTable() {
    // First check if a <tbody> tag exists, add one if not
    if ($("#productTable tbody").length == 0) {
        $("#productTable").append("<tbody></tbody>");
    }

    // Append product to the table
    $("#productTable tbody").append("<tr>" +
        "<td>" + $("#productname").val() + "</td>" +
        "<td>" + $("#introdate").val() + "</td>" +
        "<td>" + $("#url").val() + "</td>" +
        "</tr>");
}

The formClear() function uses a jQuery selector to find each input field and set the value of each to a blank string. Setting the value to a blank clears the input field so that the user can enter new data.

function formClear() {
    $("#productname").val("");
    $("#introdate").val("");
    $("#url").val("");
}

Delete a Product

Once you've added a few products, you'll most likely need to delete one or more of those products. Add a Delete button to each row of the table, as shown in Figure 3. This requires you to modify the <thead> element by adding a new <th> element with the word Delete as shown in the following snippet.

<table id="productTable" class="table table-bordered table-condensed table-striped">
    <thead>
        <tr>
            <th>Product Name</th>
            <th>Introduction Date</th>
            <th>URL</th>
            <th>Delete</th>
        </tr>
    </thead>
</table>
Figure 3: Add a Delete button to allow a user to delete a row from the table.
Figure 3: Add a Delete button to allow a user to delete a row from the table.

Add a Delete Button to Each Row

Modify the productAddToTable() function (Listing 4) to include a button control as you add each row of data. In the JavaScript you write to build the <tr> and the <td> elements, add one more <td> that includes the definition for a <button> control. This button control uses some Bootstrap classes for styling and a Bootstrap glyphicon to display an “X” to symbolize the delete function (see Figure 3). The button also needs its onclick event to call the function productDelete(). To this function, pass the keyword this, which is a reference to the button itself.

Listing 4: Build a delete button dynamically in your JavaScript code

function productAddToTable() {
    // First check if a <tbody> tag exists, add one if not
    if ($("#productTable tbody").length == 0) {
        $("#productTable").append("<tbody></tbody>");
    }

    // Append product to the table
    $("#productTable tbody").append(
        "<tr>" +
        "<td>" + $("#productname").val() + "</td>" +
        "<td>" + $("#introdate").val() + "</td>" +
        "<td>" + $("#url").val() + "</td>" +
        "<td>" +
        "<button type='button' 
                 onclick='productDelete(this);' 
                 class='btn btn-default'>" +
        "<span class='glyphicon glyphicon-remove' />" +
        "</button>" +
        "</td>" +
        "</tr>");
}

Delete a Row

The deleteProduct() function accepts the parameter that's a reference to the delete button. From this control, you can use the jQuery function parents() to locate the buttons containing <tr> tag. Once you locate the <tr> tag, use the remove() function to eliminate that row from the table, as shown in the following code:

function productDelete(ctl) {
    $(ctl).parents("tr").remove();
}

Edit a Product

You've learned how to add and delete rows from an HTML table. Now, turn your attention to editing rows in an HTML table. Just like you added a Delete button to each row in your table, add an Edit button as well (Figure 4). Once more, you need to modify the <thead> element by adding a new <th> element with the word Edit, as shown in the following code snippet.

<table id="productTable" class="table table-bordered table-condensed table-striped">
    <thead>
        <tr>
            <th>Edit</th>
            <th>Product Name</th>
            <th>Introduction Date</th>
            <th>URL</th>
            <th>Delete</th>
        </tr>
    </thead>
</table>
Figure 4: Add an Edit button to allow a user to edit a single row in the table.
Figure 4: Add an Edit button to allow a user to edit a single row in the table.

Adding a Row with an Edit Button

Just like you built a button in JavaScript for deleting a row, you build a button for editing too (Listing 5). The onclick event calls a function named productDisplay(). You'll pass in the keyword this to the productDisplay() function so you can reference the edit button and thus retrieve the row of data in which this button is located.

Listing 5: Build an Edit button in JavaScript

function productBuildTableRow() {
    var ret = "<tr>" +
        "<td>" +
        "<button type='button' onclick='productDisplay(this);' class='btn btn-default'>" +
        "<span class='glyphicon glyphicon-edit' />" +
        "</button>" +
        "</td>" +
        "<td>" + $("#productname").val() + "</td>" +
        "<td>" + $("#introdate").val() + "</td>" +
        "<td>" + $("#url").val() + "</td>" +
        "<td>" +
        "<button type='button' onclick='productDelete(this);' class='btn btn-default'>" +
        "<span class='glyphicon glyphicon-remove' />" +
        "</button>" +
        "</td>" +
        "</tr>"

    return ret;
}

Display Data in Input Fields

When the user clicks on the Edit button in the table, store the current row of the table in a global variable. Define a variable called _row within the <script> tag but outside of any function, so it's available to any function within this page.

<script>
    // Current product being edited
    var _row = null;
</script>

In Listing 5, this was passed into the productDisplay() function from the onclick event of the button. This is a reference to the Edit button control. Write the productDisplay() function to calculate the current row by getting the <tr> tag that's the parent for the Edit button. This is accomplished using the following jQuery selector.

_row = $(ctl).parents("tr");

Retrieve all the <td> columns in an array from the current row using the children() function of the _row variable.

var cols = _row.children("td");

Use the appropriate columns in the table to retrieve each input field, such as product name, introduction date, and URL. The val() function is used to place the data into each text box from each column of data. Finally, so you know that you're in Edit mode as opposed to Add mode, change the text of the updateButton to Update. The complete productDisplay() function is shown in the following code.

function productDisplay(ctl) {
    _row = $(ctl).parents("tr");
    var cols = _row.children("td");
    $("#productname").val($(cols[1]).text());
    $("#introdate").val($(cols[2]).text());
    $("#url").val($(cols[3]).text());
    
    // Change Update Button Text
    $("#updateButton").text("Update");
}

Updating the Data

When the user clicks on the updateButton, the productUpdate() function is called. What the text is in the updateButton determines whether or not you're adding a row of data to the table or editing an existing one. Remember, when you click on the Edit button, it changes the text of the Update button. Modify the productUpdate() function to check the text in the updateButton and perform the appropriate function based on that text, as shown in the following code.

function productUpdate() {
    if ($("#updateButton").text() == "Update") {
        productUpdateInTable();
    }
    else {
        productAddToTable();
    }
    
    // Clear form fields
    formClear();
    
    // Focus to product name field
    $("#productname").focus();
}

There are a couple of ways you can update a product. You already saved the row in the _row variable, so you can reference each cell individually in that row and update each cell in the table using the val() function of each input field. Another method is to add the changed data to the row immediately after the current row, then delete the current row from the table. I've chosen to use the latter, as this allows me to reuse the productBuildTableRow() function written earlier. The last thing to do is to clear the input fields, and change the text back to Add on the Update button. The updating of the product table is shown in the code below.

function productUpdateInTable() {
    // Add changed product to table
    $(_row).after(productBuildTableRow());
    
    // Remove old product row
    $(_row).remove();
    
    // Clear form fields
    formClear();
    
    // Change Update Button Text
    $("#updateButton").text("Add");
}

Use Data Dash Attributes

In this article, I've been concentrating on working with client-side code. At some point, you are going to have to send the data back to the server and retrieve data from it as well. Most of us assign a primary key (unique number) to each row of data. Let's now modify the page to use data- attributes to keep track of primary keys on the rows of data in the HTML page.

Add Two Variables

You need to create two new global variables in your HTML page; _nextId and _activeId. These are used to keep track of the next ID to assign a new added row and to keep track of the current ID of the row you're editing. The code to do this is shown below.

<script>
    // Next ID for adding a new Product
    var _nextId = 1;
    
    // ID of Product currently editing
    var _activeId = 0;
</script>

You can delete the _row variable that you created earlier, as you're now going to use these ID variables to keep track of which row is being added or edited.

Building a Row for the Table

These two new variables are used to build the row to add or update in your HTML table. Let's modify the productBuildTableRow() function (Listing 6) to accept a parameter called id to which you pass either of those two variables. This unique number is added into a data- attribute on both the Edit and the Delete buttons.

Listing 6: Use data- attributes to hold the primary key for each row

function productBuildTableRow(id) {
    var ret = "<tr>" +
        "<td>" +
        "<button type='button' onclick='productDisplay(this);' class='btn btn-default' data-id='" + id + "'>" +
        "<span class='glyphicon glyphicon-edit' />" +
        "</button>" +
        "</td>" +
        "<td>" + $("#productname").val() + "</td>" +
        "<td>" + $("#introdate").val() + "</td>" +
        "<td>" + $("#url").val() + "</td>" +
        "<td>" +
        "<button type='button' onclick='productDelete(this);' class='btn btn-default' data-id='" + id + "'>" +
        "<span class='glyphicon glyphicon-remove' />" +
        "</button>" +
        "</td>" +
        "</tr>"

    return ret;
}

Getting the Current ID

When the user clicks on the Edit button in the table, call the productDisplay() function passing in the Edit button. Extract the data- attribute containing the unique ID and assign it to the _activeId variable, as shown in the following code.

function productDisplay(ctl) {
    var row = $(ctl).parents("tr");
    var cols = row.children("td");
    _activeId = $($(cols[0]).children("button")[0]).data("id");
    $("#productname").val($(cols[1]).text());
    $("#introdate").val($(cols[2]).text());
    $("#url").val($(cols[3]).text());
    
    // Change Update Button Text
    $("#updateButton").text("Update");
}

Since the Edit button is in the first column of the row clicked on, retrieve the ID from that button using this line of code from the above code snippet.

_activeId = $($(cols[0]).
children("button")[0]).data("id");

The jQuery .data() function is passed the suffix of the data- attribute to retrieve. Because you used data-id as the key for your data- attribute, you simply pass id to the .data() function and it returns the value assigned to that attribute.

Update a Row

When the user clicks on the updateButton, the productUpdate() function is still called. However, you need to modify this function to use the _activeId variable and pass that value to the productUpdateInTable() function you wrote earlier.

function productUpdate() {
    if ($("#updateButton").text() == "Update") {
        productUpdateInTable(_activeId);
    }
    else {
        productAddToTable();
    }
    
    // Clear form fields
    formClear();
    
    // Focus to product name field
    $("#productname").focus();
}

Change the productUpdateInTable() function to find the row in the table that contains this unique ID. This function uses that ID to locate the button that contains the data- attribute within your table. The code snippet below shows just the changed lines in this updated function.

function productUpdateInTable(id) {
    // Find Product in <table>
    var row = $("#productTable button[data-id='" + id + "']")
        .parents("tr")[0];
    // Add changed product to table
    $(row).after(productBuildTableRow(id));
        // Remove original product
    $(row).remove();
    ...
}

The jQuery selector uses the ID parameter passed into this function. So the selector might look like $("#productTable button[data-id='2']). This selector returns the unique row in the table for the product that has a button with a data-id equal to 2. Once you locate the row, replace that row in the table with the contents that the user entered in the input fields on this form.

Adding a New Row

Each time you add a new row to the table, place the value in the _nextId variable into the data- attributes for the new row. Change the productAddToTable() function as shown below.

function productAddToTable() {
    // Does tbody tag exist? add one if not
    if ($("#productTable tbody").length == 0) {
        $("#productTable").append("<tbody></tbody>");
    }
    
    // Append product to table
    $("#productTable tbody").append(productBuildTableRow(_nextId));
    
    // Increment next ID to use
    _nextId += 1;
}

Notice how you pass the _nextId variable to the productBuildTableRow() function. After this new product row is created, increment the _nextId variable so that the next product you add gets the next unique ID.

Summary

In this article, you learned to create Add, Edit, and Delete rows in an HTML table using JavaScript and jQuery. Having these skills makes your Web pages more responsive and leads you to the next step, which is to use the Web API to send the modified data back to the server. In fact, in my next article (CRUD in HTML, JavaScript, and jQuery Using the Web API) this is exactly what you'll learn to do. The nice thing about using these techniques is that you don't have to post back the complete page and have the complete page redrawn just to get new records or modify records. You simply send the data back and forth and redraw those portions of the page that have changed. This saves time and transfers less data across the Internet. This is very important on mobile devices that may not be connected to WiFi, as you don't want to use your users' data minutes transferring a lot of the same HTML when all you need is just a little bit of data.