Of all the HTML elements, the table is one of the most versatile – and most complex. Frankly, its intricate tag structure can make dynamically updating a cell’s contents a bit of an ordeal. That being said, thanks to jQuery’s outstanding DOM manipulation functions, working with tables need not be a nightmare. I demonstrated how to create and modify a table’s structure in the Working with Tables Using jQuery and Dynamic Table Manipulation Using jQuery tutorials. Today, I’d like to cover how to get, set, and update a cell’s contents on the fly.
Fetching a Cell’s Contents
The main challenge when working with cell contents is that they may contain just about any type of HTML element, in addition to text – including other tables! Consider the following two tables:
Table 1: Table 2: <table> <table> <tr> <tr> <th>City 1</th> <th>City 1</th> <th>City 2</th> <th>City 2</th> <th>City 3</th> <th>City 3</th> </tr> </tr> <tr> <tr> <td>New York</td> <td><span>New York</span></td> <td>LA</td> <td><span>LA</span></td> <td>Seattle</td> <td><span>Seattle</span></td> </tr> </tr> </table> </table>
A cell can contain “New York” or <span>New York</span>. Therefore, you have to be very specific when asking for them; do you want elements or just the text?
jQuery has two related functions for that very purpose: text() and html(). The former only retrieves the text content, while the latter returns any HTML elements contained within the target element. Likewise, I followed suit with two separate functions named getCellTextAt() and getCellContentsAt(). Both employ the :eq() selector to select the TR and TD using specific index numbers:
table.getCellTextAt = function(rowIndex, colIndex) { return this.find('tr:eq(' + rowIndex + ') t' + (rowIndex == 0 ? "h" : "d") + ':eq(' + colIndex + ')').text(); }; table.getCellContentsAt = function(rowIndex, colIndex) { return this.find('tr:eq(' + rowIndex + ') t' + (rowIndex == 0 ? "h" : "d") + ':eq(' + colIndex + ')').html(); };
Setting a Cell’s Contents
Both jQuery’s text() and html() functions accept arguments for setting values. The only caveat that I would mention is that you want to make certain that you remove any existing elements before setting a cell’s contents. Therefore, I elected to use the html() method to clear the contents by passing in an empty string. Once the cell has been emptied out, I use append() to add the new contents. The reason is that it accepts more data types than html() – DOM element, text node, array of elements and text nodes, HTML string, or jQuery object, as compared to just a string.
table.setCellContentsAt = function(rowIndex, colIndex, newContents) { this.find('tr:eq(' + rowIndex + ') t' + (rowIndex == 0 ? "h" : "d") + ':eq(' + colIndex + ')').html('').append(newContents); }; table.setCellTextAt = function(rowIndex, colIndex, newText) { this.find('tr:eq(' + rowIndex + ') t' + (rowIndex == 0 ? "h" : "d") + ':eq(' + colIndex + ')').text(newText); };
Finding and Replacing Cell Text
Although find & replace operations are nothing new, applying them to table cells adds a whole lot of complexity to the proceedings – complexities that become magnified when you introduce options such as:
- replacing the first or all matched cell values
- matching against the entire cell value or any part (partial match)
- case insensitivity
- replacing only the matched portion of the string or the entire cell contents
In the findAndReplace() function below, these map to the following option variables:
- first: boolean, true limits operation to first match. Defaults to false.
- exact: boolean, true requires entire cell value to match. Defaults to false.
- caseSensitive: boolean, true makes the search case sensitive. Defaults to false.
- replaceMatchedTextOnly: boolean, true makes the replace only apply to the matched portion of a cell’s contents. Defaults to false.
Here is the full function code. Look it over, and then I’ll highlight some of the key portions:
table.findAndReplace = function(search, replace, options) { //set default options var first = false, exact = false, caseSensitive = false, replaceMatchedTextOnly = false; //override option defaults if (options) { if (options['first']) first = !!options['first']; if (options['exact']) exact = !!options['exact']; if (options['caseSensitive']) caseSensitive = !!options['caseSensitive']; if (options['replaceMatchedTextOnly']) replaceMatchedTextOnly = !!options['replaceMatchedTextOnly']; } var matches; if (exact) { if (!caseSensitive) { matches = $("td").filter(function() { return $(this).text().trim().toLowerCase() == search.toLowerCase(); }); } else { //escape single quotes matches = $("td:contains('" + search.replace(/'/g,'\$1') + "')"); } } else { matches = $("td").filter(function() { var match = $(this).text().trim(); if (!caseSensitive) { search = search.toLowerCase(); match = match.toLowerCase(); } return match.indexOf(search) != -1; }); } if (first) matches = matches.first(); if (replaceMatchedTextOnly) replace = matches.text().replace(search, replace); matches.text(replace); return matches; };
The :contains CSS selector matches all cells whose values contain the search value. For more specific criteria, we turn to the jQuery filter() method. It accepts a function so that we can limit our matching to exactly what we want.
Whittling down the matches to the first one is accomplished using the jQuery first() function. It would probably be more efficient to iterate over each row and cell so that we could eject once a match was encountered, but it does the job.
Here are some examples that illustrate how to use the findAndReplace() function:
//Replace all cell contents that contain "new" with "Old"... var matches = table.findAndReplace('new', 'Old', {caseSensitive:true}); //Replace all cell contents that contain "New" or "new" with "Old"... matches = table.findAndReplace('New', 'Old'); //Replace first cell contents that contain "UK" with "England"... var options = { caseSensitive:true, first:true }; matches = table.findAndReplace('UK', 'England', options); //Replace all matched portions of cell contents that contain "UK" with "England"... options = { caseSensitive:true, replaceMatchedTextOnly:true }; matches = table.findAndReplace('UK', 'England', options);
I have created a demo of all that we’ve learned so far with regards to working with tables. Open the console for information on what’s happening in the code.
Conclusion
Once all of the code has been streamlined a bit, I’ll be including it in version 2 of my jQuery tables library. Check out my website for updates.