<mark> my words! highlight a search
Introduction
I wanted to add an extra touch to a search feature that allowed users to filter a list of people attending an event.
The user was able to:
- See a full list of attendees.
- Enter a name into a search.
- See the list of attendees is filtered, based on their search.
This worked well, but it felt a bit sudden and jarring. The UI needed something that made it different to it's ordinary state.
Proposed change
If a user searched for "Ma" then the attendee - "Matthew Wyatt" - should appear among the results. The "Ma" section of the attendee's name should be wrapped in a <mark>
tag to highlight it.
Foundations
Feel free to skip to the highlighting - I'm going to show the components I built prior to highlighting the results, just as practice.
1. Search input
I started off by building a text input that, upon change, updated the component's state.
It looked something like this:
// search state
const [search, setSearch] = useState(null);
return (
<input
type="text"
placeholder="Search attendees..."
// null on mount, but updates onChange
value={search}
onChange={(e) => setSearch(e.target.value)}
/>
)
2. Filtering data
Based on the value the user put in the search, which is stored in the state variable 'search', I needed to filter through the full list of attendees and return attendees that match that value.
// full, unfiltered data array
const attendees = [{...}, {...}]
// new filtered array based on the search
const attendeesMatchingSearch = attendees.filter(
// to lowercase to unify the two
({ name }) =>
name.toLowerCase().includes(search.toLowerCase())
);
I then had a new array of attendees whose name matched the search value.
Highlighting
I created a new function in my /helpers
folder. I kept the naming fairly generic, just incase I wanted to use it again elsewhere. With a more generic name, I wasn't restricted to only filtering by a name or by a search.
/helpers/highlightTextInString.js
:
const highlightTextInString = (text, string) => {
const regEx = new RegExp(`(${string})`, "i");
// split the name into an array where the search matches.
// ["Ma", "tthew Wyatt"]
const textParts = text.split(regEx);
return textParts.map((part, index) =>
part.match(regEx) ? (
<mark key={index}>
{part}
</mark>
) : (
part
)
);
};
I imported that helper function and I looped through all the attendees. If there was a search value, I would call the new helper function to highlight the part of the attendees name.
{ attendees.map({ name } => {
search
? highlightTextInString(name, search)
: name
)}