Inline Cell Editing for the TableListJS HTML5 Component

735 VIEWS

In earlier posts, A Scrollable, Selectable HTML Table with JavaScript and CSS, and Adding Searching to the TableListJS HTML5 Component, I introduced an attractive HTML5 list component that uses CSS and JavaScript to create a customized, scrollable and selectable multi-column list (see Figure 1 below for an overview). In the second article, I added searching and filtering to the list to help find entries that match text entered—something particularly useful when scrolling through a list with many entries.


Figure 1 – The searchable, scrollable multi-column TableListJS HTML5 component

In this installment, I’ll add inline editing so you can edit the contents of the list by double-clicking on a row, or hitting the Enter key. You can also navigate the list with the keyboard, using the up and down arrow keys, or the tab key when editing text in a row with multiple columns. Let’s dive in.

Improved List Creation

Previously, the sample HTML pages that illustrated how to use the TableListJS component contained embedded JavaScript code to add rows to the list. It was a little tedious, in that it assumed (and hard-coded) how many columns existed in the table, and the code was mixed with the HTML. To make this more elegant and extensible, I created a function called addRow() that accepts an array of strings. Each string is placed in a matching column of the table. It also uses each column’s matching header entry to set the column width to match.

For example, the table HTML defined in Listing 1 defines a table with a header that has three columns, each with a width attribute.

Listing 1 – HTML to add a table with three header columns, with widths specified

The addRow() function introspects the columns and creates a row with matching column widths automatically. The result is a nicely formatted table that matches the widths you specify once in the table header. The JavaScript code that handles this is shown in Listing 2, below.

Listing 2 – The addRow() function inserts a new row in the HTML table, and sets column widths.
The DOM is traversed to locate the table header row, and the child nodes that make up that row (the

elements, or columns) are stored in the variable headerRow. Next, a call to insertRow() adds a new row to the table, and the row is given a unique ID for manipulation later. Each text element in the given array is then added to the new row, each as a new column referred to as the variable cell. Each column’s width is set to match that of its matching header row column.

Making Each Row Editable

Making a row editable, triggered either by a mouse double-click or by selecting the row and hitting the Enter key, is accomplished by replacing each row’s column text elements with HTML input elements, as shown in Listing 3.

Listing 3 – Replacing a row’s text elements with HTML input elements to accept human input.

First, a loop traverses each of the columns within the selected row. The text for each column is extracted, an input node is created, and the node is seeded with the existing text. Additional attributes are set as well, including the width, tab index (for keyboard navigation), an ID, and a key event handler. This event handler captures the Enter key from within each cell. When Enter is pressed, the handler places the row back into “normal” mode, where it replaces each of the input elements with text elements to restore the row to normal display. Finally, keyboard focus is set for the input element within the first column in the row (shown in Listing 4).

Listing 4 – To revert from edit mode to normal mode, clearRowEditMode() is called

In summary, to revert back to normal mode, each column’s text is extracted and simply set as the column’s innerHTML entry. Only one row may be edited at a time, and is tracked with the editRow variable. Figure 2 shows the list with a row in edit mode.

Figure 2 – A TableListJS list with a row that’s in edit mode.

Keyboard and Mouse Handling

The next steps involve enabling navigation and editing of rows with the mouse. Keyboard handlers are added at the DOM Document level, and look for arrow keys for navigation up and down (changing the selected row as the user keys through), and the Enter key to toggle the selected row’s edit state. The document.body.onkeydown function handler is set to call the function, moveSelectedRow(), see Listing 5.

Listing 5 – The document.body.onkeydown handler to navigate via arrow keys.

First, some basic checks are done to ensure there’s already a selected row, one of the two arrow keys was pressed, and that the user cannot scroll past the top or bottom of the list. Otherwise the currently selected row’s ID is extracted and then incremented or decremented depending on the arrow key pressed, and the new row is retrieved by ID. Since the ID is the row’s index in the array of rows in the table, the newly selected row is easy to retrieve.

Next, a check is made that the row is visible. (Remember that the table is scrollable and might contain more rows than can be displayed.) This is done in the function isElementInViewport(), where the child row’s rectangle coordinates are compared to the parent table’s bounding rectangle coordinates. If the newly selected row is not currently visible, which will occur when the user navigates beyond the first or last row displayed, the function will return from the onkeydown handler without returning a value. This will cause the normal keyboard handling to occur, which scrolls the list by default, and selects it (due to CSS-defined handling of rows marked as selected—see previous article here). If the row is already visible, the function returns false, effectively intercepting normal keyboard handling, and the newly selected row will just be highlighted.

A row’s edit state can be toggled by either selecting a row and pressing the Enter key, or double-clicking the mouse on a row not already in edit mode. A row.ondoubleclick handler is added for each row in the addRow() function (shown earlier in Listing 3). This function is also triggered if the user hits the Enter key when a row is already selected. All of the keyboard and mouse handlers, which are defined within the addRow() function, are shown in Listing 6.

Listing 6 – The keyboard and mouse handlers in the addRow() function.

Next, let’s take a look at how to use a database to load the list, and then persist updates after a user edits at least one row.

Using JSP and JavaDB to Populate the Table List

With the TableListJS HTML5 component able to handle user selection and edit activity, we’ll combine the JavaScript code explored so far with some JavaServer Page (JSP) code to communicate with a database. In this case, we’ll use JavaDB since it comes with the JDK, with a sample database included with it, and it’s easy to use. Additionally, the sample code and NetBeans project (available here) uses Glassfish, which comes with the Java EE SDK.

First, the HTML file that we’ve been using to demonstrate the TableListJS component—index.html—is converted to index.jsp, and some Java imports are added to the top of the file (see Listing 7).


Listing 7 – The JavaServer Page header information

Global data objects are declared with special tags towards the top of the file as well, as shown in Listing 8. These variables are available to all of the code within the JSP file at any time.

Listing 8 – Global Java variables declared within a JSP file.

Java code that executes as part of the HTML/JSP page is contained within <% %> tags, and executed snippets used to fill in parts of the HTML dynamically are placed within <%= %> tags. For example, take the HTML below (as part of a JSP):

The customer’s name is “<%= customer.name %>”

…will be dynamically updated to include the customer’s name:

The customer’s name is Fixate

There are other ways to code JavaServer Pages, such as with custom tag libraries, but I often prefer this old-school approach where all the code is inline with HTML as part of the JSP.

In this example, when the page is first loaded (and optionally when specific request parameters are included as part of the URL), the database connection code loads the sample data in preparation for loading the TableListJS HTML5 table (see Listing 9).

Listing 9 – Database code as part of the JavaServer Page to load sample data

The TableListJS component is loaded the same as in previous examples, except this time we use the data loaded from the database, as shown in Listing 10.

Listing 10 – A mix of JSP and JavaScript code to load the TableListJS component

An array is created per database row, which in turn becomes a row in the TableListJS component. The result is an interactive, attractive list loaded with real data from a database, as shown in Figure 2 below.

Figure 3 – The TableListJS component loaded with real data from a database.

The code in Listing 10 is interesting because it mixes three paradigms: HTML, JavaScript, and Java as part of a JSP. But it’s important to remember where each set of code gets executed. For example, JavaScript and HTML get executed at the client, whereas the JSP code shown inline with it will be executed at the server, prior to the page being delivered to and loaded by the browser.

This can be confusing at times, but to help enable the interaction, I find it useful to use a mix of session variables and URL request parameters. Be careful to take security into consideration since these parameters are human readable as part of the URL. Just as an example, when a table row is edited, I’ve added a JavaScript event handler so that the page can trigger a database update. Since the notification is executed in the browser, and the database is updated with code on the server, the data is extracted at the client and the browser is redirected to a URL containing this data, as shown in Listing 11.

Listing 11 – When the JavaScript notification is executed, JSP code is triggered via a browser update with URL request parameters.

Note that there certainly are more robust and secure mechanisms that can be used here, but they’re out of scope for this article.

As each row is edited, the updated Customer records are placed in a collection to be applied to the database once the user submits them via the Next button. Once clicked, the JSP code in Listing 12 applies the changes.

Listing 12 – Updating the database with the edited Customer data.

Although the code to load the table was shown in Listing 10 above, there was one important snippet that was left out. In order to make the table a submittable component as part of an HTML form tage,a hidden HTML select component is included (see Listing 13).

Listing 13 – The hidden list. If you make the component will make it work.

Listing 14 – The hidden HTML select list is kept in sync with the table selection.

You can download the full set of code for this example application from GitHub here, including the JSP, JavaScript, and CSS files.

Conclusion: Multi-Paradigm Coding

The end result is a mix of Java, HTML, JavaScript, and CSS code in what some would refer to as full-stack programming. What I like about using JavaServer Pages with JavaScript is that all of the code is part of the web pages themselves, visible and readable to everyone regardless of their role in the application. As a result, you can choose the best tool for the job, and cross-pollinate skillsets across team members instead of creating silos.

I hope you liked this three-part series, and find the TableListJS component useful within your own projects.

Do you think you can beat this Sweet post?

If so, you may have what it takes to become a Sweetcode contributor... Learn More.

Eric Bruno is a contributing editor to multiple publications with more than 20 years of experience in the information technology community. He is a highly requested speaker and writer for topics spanning the technology spectrum, from mobile to the data center.


Discussion

Click on a tab to select how you'd like to leave your comment

Leave a Comment

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

Menu