running-woods.jpeg

<mark> my words! highlight a search

Headshot of Matthew Wyatt
#dev

2 min read

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
)}

That's all he wrote.Β  πŸ¦”

Headshot of Matthew Wyatt

Thanks for taking the time to read my drivel.
Matthew 😊