Drupal 8/9: Highlighting Views Search Results

Drupal 8/9: Highlighting Views Search Results

How to highlight search keywords (exposed filter values) in Drupal 8 / 9 views without using javascript.

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

You might be tempted to try to modify the views search results markup by implementing template_preprocess_search_result(), 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(), 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
  // 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.

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() 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 yellow background.

If you liked this article, you'll love our courses on Drupal 8/9 Site Building, Module Development, and Theme Development.

TrainingCloud: Drupal and PHP training for (aspiring) developers