Drupal 8/9: Highlighting Views Search Results
Tags: drupal, php
Published: 2021-04-07 15:07:05

## Problem
You used exposed filters to filter a view, and you want to highlight certain filter values in the view's results, like so:

![Screenshot of a view with certain words highlighted](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4vmqa2cb382p5e6zhqxh.png)

You might be tempted to try to modify the views search results markup by implementing [template_preprocess_search_result()](https://api.drupal.org/api/drupal/core%21modules%21search%21search.pages.inc/function/template_preprocess_search_result/8.9.x), but **this will not work for views** because views-based searches don't use the built-in search engine.

## Solution

### Approach 

One approach is to implement [template_preprocess_views_view_field()](https://api.drupal.org/api/drupal/core%21modules%21views%21views.theme.inc/function/template_preprocess_views_view_field/8.9.x), which lets you alter the content that is passed to the template file used for rendering each field in each row of a views result.

### Steps

* Implement `template_preprocess_views_view_field()` in such a way that it wraps the specified exposed filter value in `<mark></mark>` tags.
* add css to give `<mark></mark>` tags a yellow background.

### Implementation

Here's an example implementation using a custom module named views_search_highlight.

In this example:

* Our view's identifier (machine name) is my_search_demo.
* We're exposing a filter on the body field.
* The exposed filter's identifier is body_value

```php
  // File: modules/custom/views_search_highlight/views_search_highlight.module

    /**
     * @file
     * Contains code to implement highlighting exposed views filter values.
     */
    
    use Drupal\Core\Render\Markup;
    
    /**
     * Implements hook_preprocess_views_view_field().
     *
     * This function is called for each field in each row of each view result
     *   (unless overridden by a more specific preprocessor elsewhere).
     */
    function views_search_highlight_preprocess_views_view_field(&$variables) {
    
      /** @var \Drupal\views\ViewExecutable $view */
      $view = $variables['view'];
    
      /** @var \Drupal\views\Plugin\views\field\EntityField $field */
      $field = $variables['field'];
    
      // Specify for which view(s) and field(s) you want to enable highlighting.
      if ($view->id() == 'my_search_demo' && $field->field == 'body') {
    
        $keyword = \Drupal::request()->get('body_value');
        if (!is_null($keyword)) {
    
          /** @var \Drupal\views\ResultRow $row */
          $row = $variables['row'];
    
          // Retrieve the field's rendered content.
          $content = $field->advancedRender($row)->__tostring();
    
          // Apply highlighting to field value.
          $highlighted = views_search_highlight_apply_highlight($keyword, $content);
    
          // Update field output (must be a Markup object, not a string of html).
          $variables['output'] = Markup::create($highlighted);
    
          // Attach css. Skip this if you're setting the style in your theme's css.
          $view->element['#attached']['library'][] = 'view_search_highlight/highlight';
        }
      }
    }
    
    /**
     * Search for a given keyword in a given source string and wraps the keyword
     * in <mark></mark> tags.
     *
     * @param string $keyword
     *   The keyword to highlight.
     * @param string $source
     *   The content in which to highlight the keyword.
     *
     * @return string
     *   String with highlighted sections (if any).
     */
    function views_search_highlight_apply_highlight($keyword, $source) {
      $highlighted = str_replace($keyword, "<mark>{$keyword}</mark>", $source);
      return $highlighted;
    }
```

### Note 1
Comments like `/** @var \Drupal\views\ResultRow $row */` are optional. They are type hints so your code editor knows of which class the objects are an instance, and provide automatic code completion.

### Note 2
The `views_search_highlight_apply_highlight()` function is very basic. Feel free to modify its logic to suit your needs.

### Note 3

To keep things short I'm not showing the `view_search_highlight/highlight` library definition, nor the css file. You can find these in this module's [full source code](https://gitlab.com/jpoesen/views-search-highlight).

## Going further

Basic keyword highlighting is now implemented.  
Here are some ideas to improve this module:  

### Make it smarter
You could improve this code by making the replacement function smarter (use word boundaries, allow/enforce partial matches or full matches, etc). If you need some inspiration, have a look at the core search module's [template_preprocess_search_result()](https://api.drupal.org/api/drupal/core%21modules%21search%21search.pages.inc/function/template_preprocess_search_result/8.9.x) function.

### Make it more generic
You could change the logic to always highlight filter values if the filter identifier name starts with "highlight_". 

That way you don't need to modify the code each time you want to have highlighting on exposed filter values in other views.

---

## Summary

Implement `hook_preprocess_views_view_field()` in a custom module or theme:

* Retrieve the search keyword from the request object.
* Retrieve the fully rendered field content.
* Inside the field content, replace the keyword to highlight with the keyword wrapped in `<mark></mark>` tags.
* Add css to style `<mark></mark>` with a <mark>yellow background</mark>.

---

If you liked this article, you'll love [our courses](https://trainingcloud.io/courses) on Drupal 8/9 Site Building, Module Development, and Theme Development. 

![TrainingCloud: Drupal and PHP training for (aspiring) developers](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2wav7v6hn30d0q9vfy3c.png)