Search code examples
javascriptcontenteditable

How to programatically highlight text within a contenteditable div


I am trying to find a way to programatically select specific text within a contenteditable div and not having any luck. For textarea and input fields, I do this easily as follows;

      const el = document.getElementById("some_input_field");
      const words = "Find me";
      searchText = el.value;  //Would use el.textContent for contenteditable div, not innerHTML
      if (searchText !== "") {
        foundPosition = findText(searchText, words); //Returns the start and end index of the first match ie: [5,14]
        if (foundPosition != []) {
          el.focus();
          el.setSelectionRange(foundPosition[0], foundPosition[1]); //This is where i want to highlight the text
        } else {
          console.log("Word(s) not found in text");
        }
      } else {
        console.log("No text to search");
      }

but I have not been able to figure out how to do this in a contenteditable div. There are many examples of selecting the entire contents but not specific text based on indexes. I know how to get highlighted text in a div but I just can't figure out how I can highlight it programatically. I've spent a bunch of time just highlighting a piece of text in the div and looking through the properties to see if I could use that to figure this out but I'm hitting a dead end. I can't imagine there isn't a way to do this. I'm hoping someone has done this and can offer some assistance. Thanks,


Solution

  • You can do it like this (went a little overboard with the example).

    const div = document.querySelector("div");
    const btn = document.querySelector("button");
    const inp = document.querySelector("#txt");
    const chkCase = document.querySelector("#chkCase");
    
    btn.addEventListener("click",function() {
      let txt = inp.value;
      if(txt) {
        find(txt,div);
      }
    });
    
    function find(needle, haystack) {
      let sel = window.getSelection();
      let range = document.createRange();
      let state = chkCase.checked;
      let hayTxt = haystack.textContent;
    
      if(!state) {
        hayTxt = hayTxt.toLowerCase();
        needle = needle.toLowerCase();
      }
    
      if(hayTxt.indexOf(needle) >= 0) {
        range.setStart(haystack.childNodes[0], hayTxt.indexOf(needle));
        range.setEnd(haystack.childNodes[0], hayTxt.indexOf(needle) + needle.length);
    
        sel.removeAllRanges();
        sel.addRange(range);
      }
    }
    <div contenteditable="true">Where's Waldo? Where in the world is Carmen Sandiego? They both say "Find me". Well, maybe not Carmen.</div>
    <button type="button">Find</button>
    <input type="text" name="txt" id="txt" value="Find me">
    <input type="checkbox" name="chkCase" id="chkCase" value="1" checked>Case sensitive?

    I know this wasn't in your original code, but I wanted to give you an example which would work with whatever you enter within your contenteditable element - that's what the inputs and the button for.