1

I have two textareas written in HTML like this:

<textarea id="checked-words"></textarea>
<br />
<textarea id="words" onkeyup="displayColoredText()"></textarea>
<div id="text-to-insert"></div>

(into the div element, I will insert text, using JavaScript)

My task is to write into the div section the text from the second textarea and make red the occurrences of strings from the first textarea.
Example
If the first textarea contains the following words: aaa, aab and the second contains aaab, all of the characters have to be red. If the second one contains abaa, none of the characters will be red in the div section.

Here is my JavaScript function which displays and colorize the text:

function displayColoredText() {
//Displays the colored text below the second textarea

//Find the two textareas
var firstTextArea = document.getElementById('checked-words');
var secondTextArea = document.getElementById('words');

//Split by spaces
var checkedWords = firstTextArea.value.split(" ");
var text = secondTextArea.value.split(" ");

var textToInsert = secondTextArea.value;

for(i in checkedWords) {
    console.log(checkedWords.length);
    textToInsert = textToInsert.replace(new RegExp(checkedWords[i], 'g'), '<span class="insertRed">' + checkedWords[i] + '</span>');
}

document.getElementById('text-to-insert').innerHTML = textToInsert;
}

My problem is, that an already replaced text won't be considered, for example, if the first textarea contains aaa and aab and if the second one contains aaab, only the first three characters will be red, instead of the whole string. How can I resolve this?

EDIT: Screenshot of the problem enter image description here

Fogarasi Norbert
  • 578
  • 1
  • 7
  • 26
  • 1
    When you're pulling out the text for the comparison, you'll have to find a way to parse out the HTML tags, so that you only have the plain text for comparison. – Sean Apr 07 '18 at 11:56
  • And how can I achieve this? – Fogarasi Norbert Apr 07 '18 at 11:57
  • This question https://stackoverflow.com/q/5002111/3765016 weighs up some different methods of doing it. – Sean Apr 07 '18 at 12:04
  • you can simplify your question as `if the first textarea contains aaa and aab and if the second one contains aaab, only the first three characters will be red, instead of the whole string.`because this is your core logic and problem,am i right? – xianshenglu Apr 07 '18 at 12:13
  • In this specific example, yes. In general, my problem is that if a string was already colorized, it's parts cannot be considered as a beginning in the next iteration. Hope you understand. – Fogarasi Norbert Apr 07 '18 at 12:17
  • what do you mean by the textarea contains aaa and aab, does that mean in textarea it is like aaa aab? – Night Ryder Apr 07 '18 at 12:17
  • Yes, exactly (instead of `and` is only a space in the textarea). I added a screenshot, hope that clarifies – Fogarasi Norbert Apr 07 '18 at 12:18
  • Your original input from the second text area is pure text, not HTML, so this is the "state" of the data you want to do this in. Each match of one of your search values will give you a "range" or interval, from the match starting position, to that plus length of the search string (minus one). You can either try and figure out where those intervals overlap, and then combine two such overlapping intervals into one, until there are no overlaps left. [...] – CBroe Apr 07 '18 at 12:54
  • Or you make it a little simpler, and do this on a character basis - remember all of the individual positions where a match occurred, avoid/remove duplicates while or after doing so, and then either wrap those characters into a span containing a single one each, or combine the consecutive ones into one single element, up to you ... – CBroe Apr 07 '18 at 12:54
  • You could use this plugin to highlight the texts which is simple to use: https://markjs.io/ . Get the values from textarea and pass it to the plugin function and you will have the texts highlighted – Night Ryder Apr 07 '18 at 13:00
  • also https://github.com/padolsey/findAndReplaceDOMText – Slai Apr 07 '18 at 15:20

2 Answers2

2

Your original input from the second text area is pure text, not HTML, so this is the "state" of the data you want to do this in.

This would be my way of implementing it as mentioned in comments, recording which positions have a match first, and then simply looping over all characters in the end to wrap them in a span each:

function displayColoredText() {
  //Displays the colored text below the second textarea

  //Find the two textareas
  var firstTextArea = document.getElementById('checked-words');
  var secondTextArea = document.getElementById('words');

  //Split by spaces
  var checkedWords = firstTextArea.value.split(" ");
  var text = secondTextArea.value;
  // empty array with keys 0 to length-1 set to undefined
  var markedMatches = new Array(secondTextArea.value.length);

  for (var i = 0, l = checkedWords.length; i < l; ++i) {
    var checkedWord = checkedWords[i],
      start = 0,
      matchPos;
    // check for match from current starting position
    while ((matchPos = text.indexOf(checkedWord, start)) !== -1) {
      // mark positions from current match start to that plus length of match
      for (var k = matchPos, m = matchPos + checkedWord.length; k < m; ++k) {
        markedMatches[k] = true;
      }
      // advance starting position to continue searching
      start = matchPos + 1;
    }
  }

  var textToInsert = '';
  for (var i = 0, l = text.length; i < l; ++i) {
    // wrap in span if markedMatches contains true at this position
    textToInsert += (markedMatches[i] ? '<span class="match">' + text[i] + '</span>' : text[i]);
  }
  document.getElementById('text-to-insert').innerHTML = textToInsert;
}

https://jsfiddle.net/t9xjzkaw/

As I said, you could get more sophisticated in collecting the matches as intervals, or putting multiple adjoining matching characters into a single span element, instead of wrapping each one on its own ... but this does the basic job.

CBroe
  • 82,033
  • 9
  • 81
  • 132
0

Your problem is how your strings get replaced. Your first string is 'aaa aab'. After replacing for 'aaa' in 'aaab', you get '<span class="insertRed">aaa</span>b'. Trying to find 'aab' in this string will come up with no results. You have to replace from your original string and somehow combine the two. I'm not sure how to do this, but I hope this sets you on the right track.

EDIT: I think this will work: Instead of replacing the text in the string, place the beginning coordinate in an array and the end coordinate in a second array. Keep doing this for every word found. Then at all of the beginning coordinates, insert the string '<span class="insertRed">'. At all of the end coordinates, insert the string '<span>'. Here is the JS:

function displayColoredText() {
    //Displays the colored text below the second textarea
    //arrays with coordinates
    var beginnings = [];
    var ends = [];
    //Find the two textareas
    var firstTextArea = document.getElementById('checked-words');
    var secondTextArea = document.getElementById('words');

    //Split by spaces
    var checkedWords = firstTextArea.value.split(" ");
    var text = secondTextArea.value.split(" ");

    var textToInsert = firstTextArea.value;

    for(i in checkedWords) {
        console.log(checkedWords.length);
        if (firstTextArea.value.indexOf(checkedWords[i]) != -1) {
            beginnings.push(firstTextArea.value.indexOf(checkedWords[i]));
            ends.push(firstTextArea.value.indexOf(checkedWords[i]) + checkedWords[i].length);
        }
    }
    beginnings.sort(function(a, b){return b-a});
    ends.sort(function(a, b){return b-a});
    for (b in beginnings) {
        textToInsert = textToInsert.slice(0, ends[b]) + "</span>" + textToInsert.slice(ends[b]);
        textToInsert = textToInsert.slice(0, beginnings[b]) + '<span class="insertRed">' + textToInsert.slice(beginnings[b]);
    }
    document.getElementById('text-to-insert').innerHTML = textToInsert;
}

This code is untested, so tell me if something doesn't work and I will change it. Basically, what I am doing is instead of replacing occurrences, I find them first, place them in arrays, and insert the correct text at those coordinates. I hope this helps!

Benjamin Hollon
  • 191
  • 2
  • 9