This article describes how to highlight search keywords (exposed filter values) in Drupal 8 / 9 / 10 views, without using JavaScript.
If you're in a hurry, you can skip to the solution, summary, or full source code on Gitlab.
Problem
You used exposed filters to filter a view, and you want to highlight certain filter values in the views result like so:
You might be tempted to try to modify the views search results markup by implementing template_preprocess_search_result(), but that 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 _bodyvalue
→ /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 wrap 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 on Gitlab.
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.