Search code examples
javascripthtmlangularckeditor

How to search and highlight rich text using Angular


I've been working with CKEditor, is a rich text editor that allow us apply "bold", "italic", "headings", "lists", and so on.

This tool transform the written message into an HTML code with their corresponding HTML tags.

I store the result and then show it to users in the same way they enriched it.

But now I have to implement a search box to highlight the content of comments.

If it were plain text, it would be very simple, I would have wrapped the text in a span with some class to highlight the text. "<span class="highlight ">Plain Text</span>"

But the tricky part is that the text we have to highlight is composed by many nested HTML nodes, for example: "<h1>Hi, <span>I am <strong>a rich </strong><i>text</i></span></h1>"

I researched how open-source browsers like Chromium deal with it and other sources trying to understand a little more.

And I had success implementing it image-highlight-nested-nodes-successfully

In this Stackblitz you can see how I achieved the goal but with a catch. (The input focus is lost after typing one character, so you will have to click again in the input to add the next character) https://stackblitz.com/edit/angular-s5tfjy?file=src%2Fmain.ts

The directive that manages the highlighting makes use of "Selection.collapse()" https://developer.mozilla.org/en-US/docs/Web/API/Selection/collapse

And that collapses the current selection to the beginning of the document's body, effectively removing the focus from my input field.

So every time I enter a character in the input, the focus is lost. I have tried everything to keep the focus in place, but no luck so far.

Any ideas?


Solution

  • In the end I managed to solve it by using a combination of "@HotListener" and relying on the "activeElement" property provided by the document.

    Not the ideal solution I had in mind, but it does the trick.

    1. The general idea is to use the "@HostListener('window:keydown', ['$event'])" to store the active element, that is our input.
    2. Then the directive does its magic (and also the focus get lost from our input).
    3. And when the entire process has been completed, we restore the focus on the input we had initially stored with the "@HotListener".

    I have made this Fork so that you can see the differences between the one with the error and this one, which is already corrected: https://stackblitz.com/edit/angular-exmdu3?file=src/main.ts

    *This will be good to have with some debounce time but that's another matter.