1

I have any array vals_array generated from .load(dictionary.txt) and now I want to compare each word in a contenteditable div with text wraps in <p>, and if match any word from the array, it will wrap that word in a

Here is the text to array code:

var vals_array = [];

$.each(splitCntEditableTxt,function(key,val){
  var nameFound = $.inArray(val.trim().toUpperCase(), dictionary);
  if (nameFound === -1){
    //alert(val + " No Match"); //DEBUG
  } else {
    //alert(val + " found in array"); //DEBUG
    //$("#notes").append('<span class="OMG">'+val+'</span>');
    vals_array.push(val);
  }
});

and here is the code I'm thinking of using to compare filtered text and match them one by one from the array:

$('#notes').findText({query: vals_array}).each(function (){
  //wrap matched word with <span>
});

The problem is, the text in <p> sometimes have : , - spacing, \n \r and other non-word elements. So, how do I first filter out text in

leaving only pure words and then compare them with the array of vals_array and if matched wrap it with a ?

Thank you very much~!!!

Colin
  • 1,112
  • 1
  • 16
  • 27

1 Answers1

1

You can use String#replace to replace words with links. replace takes two arguments:

  1. A regex or string to replace.
  2. A replacement or function that returns a replacement.

This means we can:

  1. Read the textContent of your <p>,
  2. Match all words using /\w+/g
  3. For every word, check if it's in the dictionary,
  4. If it is, return a <span>,
  5. If it isn't, just return the word
  6. Write it back to the <p>'s innerHTML

The example below shows you these steps. It uses some modern javascript features, so you might have to transpile it to es5. Also, it doesn't handle the caret position very well, so you'll have to fix that yourself. (Currently, it just places it at the end of the content)

The <p> in the example replaces the words: foo, bar and baz. You can add other words to the array at the top of the snippet.

// The array source you mentioned
const wordsToMatch = ["foo", "bar", "baz"];

// For quick lookups, we make a Set
const matchingDict = new Set(wordsToMatch);

// Check if a word is in the dictionary and possibly return
// a <span> element
const convertWord = word => matchingDict.has(word)
  ? `<span class="highlight">${word}</span>`
  : word;

// Check the textContent of an event target for words in dictionary 
// and update accordingly
const handleChange = e => {
  e.target.innerHTML = e.target.textContent
    .replace(/\w+/g, convertWord);
  setRangeToEnd(e.target);
};

// Attach the logic to the contenteditable <p>
document
  .querySelector("p")
  .addEventListener("input", delay(300, handleChange));
  
  
  
// A utility to only run our text-adaptations x ms after the last change
function delay(dt, fn) {
  let to = null;
  
  return e => {
    clearTimeout(to);
    to = setTimeout(() => fn(e), dt);
  }
}

function setRangeToEnd(target) {
  const sel = window.getSelection();
  const range = document.createRange();
  range.selectNodeContents(target);
  range.collapse(false);
  sel.removeAllRanges();
  sel.addRange(range);
}
p { border: 1px solid grey; padding: .5em; }

.highlight {
  background: yellow;
  padding: .25rem;
  border-radius: 1em .5em .75em 0.25em;
}
<div>
Type in the text area below. Use the words <code>foo</code>, <code>bar</code> or <code>baz</code> to trigger a link insertion.</div>

<p contenteditable>
  Lorem ipsum dolor
</p>
user3297291
  • 22,592
  • 4
  • 29
  • 45