The Table component features the Search:[Fuzzy match] feature.
It would be nice if search could be limited to one, or a set of, specific columns but that is not my main issue today.
In my case the main search target is an HTML column. The value displayed is the result of an expression which chooses between one of two other (hidden) columns.
The displayed text is often a longish paragraph. If the search term is a short string the search “works”; rows which do not contain the search term are hidden and rows which do contain it remain displayed.
However, it would be useful to use some sort of html markup to “highlight” the search term within the paragraph. Visually scanning for a search term is tedious.
This seems like a generally useful enhancement which could be (optionally) applied, by the table code, to markdown and html columns as well as strings and multi-line string columns.
Has this already been requested and “is in the pipeline”? (I searched here in the community forum and did not find anything).
And: is there a recommended workaround or hack I could implement myself quickly?
You can do this by modifying your data to include HTML markup that highlights the search term. Here's how:
Define a JavaScript Query to Highlight Search Terms. In my example I named it highlightSearch.
function highlightSearchTerm(text, searchTerm) {
if (!searchTerm) return text;
const regex = new RegExp(`(${searchTerm})`, 'gi');
return text.replace(regex, '<span style="background-color: yellow;">$1</span>');
}
const searchTerm = textInput1.value; // Get search term from input
const rawData = getBookExtracts.data; // Fetch book extracts from resource query
const processedData = rawData.map(row => ({
...row,
extract: highlightSearchTerm(row.extract, searchTerm) // Apply highlighting
}));
return processedData;
This uses your query and alters it to feed the table. In this case you would set your data source to {{ highlightSearch.data }}
On your text input textInput1 add an event handler on change to trigger highlightSearch.
That should be it. Now the table will update when typing in textInput1.
I "asked ChatGPT" (o3-mini-high) and its first solution was quite similar to this one. However the complication is that the input text we are trying to highlight is itself HTML. We are not consuming plain text and producing HTML with highlighting. The "simple" solution broke when the search term was found inside a link. For example:
got mangled when the search term was "science". The next solution ChatGPT suggested was to:
...avoid trying to patch HTML using regular expressions and instead parse the HTML into a DOM structure, traverse only the text nodes, and then reassemble the HTML. This ensures that you don’t accidentally modify text that appears inside tags (such as URLs in attributes).
That second effort failed because it would find only the first instance of a search term and then miss any subsequent appearances. GPT accepted the blame and explained:
...modifying the DOM while using a TreeWalker can cause some text nodes (or parts of them) to be skipped—so even though the regex is global, only the first match in each original text node gets replaced. One solution is to avoid modifying the DOM on the fly and instead recursively process every text node.
ChatGPT describes the solution which finally worked:
Recursive Processing:
The function processNode walks through all nodes in the container. For every text node it finds, it replaces all matches (because the regex is global) and then replaces that text node with a new element (a <span>) containing the marked-up HTML.
Avoiding DOM Walker Pitfalls:
By not relying on a TreeWalker that gets confused when nodes are replaced during iteration, this approach ensures that every text node is examined and every occurrence of the search string is replaced—even if multiple occurrences appear in the same text node.
I am not expert enough to judge if ChatGPT has produced the best solution here but it has convinced me this is a non-trivial problem and any solution is going to have to interact with React, the structure of the underlying data, and, while performance is OK, it's not super awesome. One interaction is the "fuzzy" match algorithm inside the table code that is deciding what rows are hidden, or displayed, and the regexp matcher in this ad hoc code that is deciding what gets highlighted. They can disagree and that produces flicker and confusing results visually. In a perfect world the 'match' would be found once, by only one algorithm making one pass over the data.
Good reasons why Retool should take this on as an embedded feature, of tables and potentially other components that display a lot of text.
I'm glad you at least found something you can work with. From the top of my head you could easily exclude links in the search.
Just for fun I added your link and the word science in another row and it worked just fine. Did you try my sample app?
If you want help finding a better solution we can use the demo app I sent. Make any changes you would like, populate the table with data similar to what you have and share it.
I was suspicious that ChatGPT was making things more complicated than need be. However, I've moved on to another problem so don't want to go back and work on this at the moment. However I do appreciate your effort.
Glad you were able to leverage LLMs to work through a very tricky custom JS implementation
Absoultely amazing work @Shawn_Optipath to find a really cool work around and to share the app for Roland and other users! We really appreciate the help
I am sure tons of Retoolers will be able to use that to improve their table searching, we might need to add that to our tips and tricks page and feature it as well!
I agree this is likely something that can be better handled by Retool as an embedded feature. But it seems like @Shawn_Optipath was able to find a work around for working with links/HTML components