Highlight specific words in textarea angular 8

I am trying to select a few words from the textarea and create bootstrap chips.

enter image description here

I am able to create the chips for selected words. I am trying to highlight the selected words with different background colors.

export class SearchComponent implements OnInit {

  constructor() { }

  selectedText = [];
  regexFromMyArray: RegExp;

  // tslint:disable-next-line:typedef
  showSelectedText() {
    let text = '';
    if (window.getSelection) {
      text = window.getSelection().toString();
      if (text) {
        // console.log(this.selectedText.includes(text));
        if (this.selectedText.includes(text) === false)
        {
          this.selectedText.push(text);
          this.regexFromMyArray = new RegExp(this.selectedText.join('|'), 'ig');
          console.log(this.regexFromMyArray);
        }
        }
    }
    // console.log(this.selectedText);
    return this.selectedText;
  }

  // tslint:disable-next-line:typedef
  removeItem(array, item){
    for (const i in array){
      if (array[i] === item){
        array.splice(i, 1);
        break;
      }
    }
  }

  // tslint:disable-next-line:typedef
  deleteChip(el) {
    // el.style.display = 'none';
    document.getElementById(el).style.display = 'none';
    this.removeItem(this.selectedText, el);
  }



  ngOnInit(): void {
  }

}

I am not sure how to highlight the words in the selectedText array. I want to highlight all chip words. Like “Contrary”, “Ipsum”, “classical”, “literature”.

Help will be appreciated.

Answer

This answer is based on the link provided by @RobinDijkhof in there comment.

We will set up the css exactly as provided

*,
*::before,
*::after {
  box-sizing: border-box;
}

.container,
.backdrop,
textarea {
  width: 460px;
  height: 180px;
}

.highlights,
textarea {
  padding: 10px;
  font: 20px/28px "Open Sans", sans-serif;
  letter-spacing: 1px;
}

.container {
  display: block;
  margin: 0 auto;
  transform: translateZ(0);
  -webkit-text-size-adjust: none;
}

.backdrop {
  position: absolute;
  z-index: 1;
  border: 2px solid #685972;
  background-color: #fff;
  overflow: auto;
  pointer-events: none;
  transition: transform 1s;
}

.highlights {
  white-space: pre-wrap;
  word-wrap: break-word;
  color: transparent;
}

textarea {
  display: block;
  position: absolute;
  z-index: 2;
  margin: 0;
  border: 2px solid #74637f;
  border-radius: 0;
  color: #444;
  background-color: transparent;
  overflow: auto;
  resize: none;
  transition: transform 1s;
}

mark {
  border-radius: 3px;
  color: transparent;
  background-color: #b1d5e5;
}

.perspective textarea {
  transform: perspective(1500px) translateX(155px) rotateY(45deg) scale(1.1);
}

textarea:focus,
button:focus {
  outline: none;
  box-shadow: 0 0 0 2px #c6aada;
}

Now to the task will be to convert the JQuery code to Angular. We will build a component that can be used like

<app-textarea-highlight [(ngModel)]='txt'
  [highlightTexts]='highlightTexts'

></app-textarea-highlight>

Where the values are

  highlightTexts = ["text", "demo", "div"];
  txt = "This demo shows how to highlight bits of text within a textarea. Alright, that's a lie. You can't actually render markup inside a textarea. However, you can fake it by carefully positioning a div behind the textarea and adding your highlight markup there. 
}

To enable using property binding we will implement ControlValueAccessor

Below is the code

@Component({
  selector: "app-textarea-highlight",
  templateUrl: "./textarea-highlight.component.html",
  styleUrls: ["./textarea-highlight.component.css"],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => TextareaHighlightComponent),
      multi: true
    }
  ]
})
export class TextareaHighlightComponent
  implements  ControlValueAccessor {
  constructor() {}
  @Input() highlightTexts: string[] = [];
  @ViewChild("backdrop") $backdrop: ElementRef<HTMLDivElement>;
  @ViewChild("textarea") $textarea: ElementRef<HTMLTextAreaElement>;
  textValue: string = "";
  get highlightedText () {
    return this.applyHighlights(this.textValue)
  }

  applyHighlights(text) {
    text = text ? text
      .replace(/n$/g, "nn") : '';
    this.highlightTexts.forEach(x => {
      text = text
      .replace(new RegExp(x, 'g'), "<mark>$&</mark>");
    })
    return text;
    
  }
  handleScroll() {
    var scrollTop = this.$textarea.nativeElement.scrollTop;
    this.$backdrop.nativeElement.scrollTop = scrollTop;

    var scrollLeft = this.$textarea.nativeElement.scrollLeft;
    this.$backdrop.nativeElement.scrollLeft = scrollLeft;
  }

  onChanges: ($value: any) => void;
  onTouched: () => void;

  writeValue(value: any): void {
    if (value !== undefined) {
      this.textValue = value;
    }
  }
  registerOnChange(fn: any): void {
    this.onChanges = fn;
  }
  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }
}

The final step is to set the html

<div class="container">
  <div #backdrop class="backdrop">
    <div class="highlights" [innerHTML]="highlightedText"></div>
  </div>
  <textarea
    [(ngModel)]="textValue"
    (scroll)="handleScroll()"
    #textarea
  ></textarea>
</div>

See Working Demo Here