Eshan Rajapakshe
Eshan Rajapakshe

Reputation: 161

Highlight searched text inside the table

I'm trying to create a Filter/Search Table in react + material-ui. I'm using filter method to filter the table data by a input. Now I need to highlight the matching searched keywords in the table. I couldn't find any way to do this in my scenario.

I have state like this. Stored all table records and filter method. (I have simplify the code)

const [records, setRecords] = useState(tableService.getAllTableData)
const [filterFn, setFilterFn] = useState({ fn: items => { return items; } })

And the input field and filter methord

    const handleSearch = e => {
        let target = e.target;
        const searchFields = ["fullName", "email", "mobileNumber"];
        setFilterFn({
            fn: items => {
                if (target.value == "")
                    return items;
                else
                    return items.filter(item => {
                        return searchFields.some(field => item[field].toLowerCase().includes(target.value.toLowerCase()))
                    })
            }
        })
    }

    <Input
        label = "Search"
        onChange = { handleSearch }
    />

And I'm rendering data this way.

const recordsAfterPagingAndSorting = () => {
    return stableSort(filterFn.fn(records), getComparator(order, orderBy)).slice(page * rowsPerPage, (page + 1) * rowsPerPage)
}

<TableBody>
    recordsAfterPagingAndSorting().map(record => (
    <TableRow key={record.id}>
        <TableCell size='small'>{record.fullName}</TableCell>
        <TableCell size='small'>{record.email}</TableCell>
        <TableCell size='small'>{record.mobileNumber}</TableCell>
        <TableCell size='small'>{record.selectBoxTitle}</TableCell>
    </TableRow>
)
</TableBody>

What is the proper way to do this? Can I handle the word highlighting logic inside the filter method?

Upvotes: 1

Views: 1429

Answers (2)

Nishanth Shankar
Nishanth Shankar

Reputation: 1976

What you've written helps you identify what are the records that contain this search phrase. The part you're missing about highlighting is that you need the portion that matched and the portion around it.

For instance:
If someone searched for 'HAN' your name 'Eshan Rajapakshe' should be filtered but to highlight the filtered portion you need something like ['Es', 'han', ' Rajapakshe'] and to get the match info you can either compare it with the lowercased search phrase or add that logic to the array itself [{text:'Es', match:false}, {text:'han', match: true}, {text:' Rajapakshe', match: false}]

You can get the above using the package I have published

or

The first array can be obtain by a simple split using regex 'Eshan'.split(new RegExp(`(${search_phrase})`, 'gi'))

In your case you can call the module in your function. You'll have to change your render logic accordingly

import getParts from 'ec-highlighter-core'
...

const handleSearch = e => {
  let target = e.target
  const searchFields = ['fullName', 'email', 'mobileNumber']
  setFilterFn({
    fn: items => {
      let filteredItems
      // Get filtered items
      if (target.value === '') filteredItems = items
      else
        filteredItems = items.filter(item => {
          return searchFields.some(field =>
            item[field].toLowerCase().includes(target.value.toLowerCase())
          )
        })
      // Get the arrays for highlighting
      return filteredItems.map(item => {
        const out = item
        searchFields.forEach(field => {
          out[field] = getParts(item[field], target.value)
        })
        return out
      })
    }
  })
}

<Input
    label = "Search"
    onChange = { handleSearch }
/>

Upvotes: 2

Eshan Rajapakshe
Eshan Rajapakshe

Reputation: 161

I found this alternative solution. Maybe not the react way. onKeyUp event and select all dom element with querySelectorAll

//onKeyUp function
    const searchedWordsHighlight = (event) => {
        var searchedPara = document.querySelectorAll('.searchCell-body');
        var words = event.target.value;
        var regex = RegExp(words, 'gi') // case insensitive
        var replacement = '<b>' + words + '</b>';
        for (let i = 0; i < searchedPara.length; i++) {
            const element = searchedPara[i].innerText;
            if (words != "") {
                let newHTML = element.replace(regex, replacement)
                searchedPara[i].innerHTML = newHTML;
            } else {
                let newHTML = element.replace()
                searchedPara[i].innerHTML = newHTML;
            }
        }
    }
    
//Input field
    <Input
        label = "Search"
        onChange = { handleSearch }
        onKeyUp={searchedWordsHighlight}
    />
//Table
    <TableRow key={record.id}>
        <TableCell className="searchCell-body" size='small'>{record.fullName}</TableCell>
        <TableCell className="searchCell-body" size='small'>{record.email}</TableCell>
        <TableCell className="searchCell-body" size='small'>{record.mobileNumber}</TableCell>
        <TableCell size='small'>{record.selectBoxTitle}</TableCell>
    </TableRow>

Upvotes: 0

Related Questions