7

I have a contenteditable div (with id 'editor1') that allows users to input text. There is then a function that allows them to color any highlighted text. My js uses window.getSelection().getRangeAt(0), but the issue with this is that they can highlight words outside of the div and their color will change as well. So far; I've tried:

        function red(){
    {       
        var getText = document.getElementById("editor1").innerHTML;
        var selection = getText.getSelection().getRangeAt(0);
        var selectedText = selection.extractContents();
        var span = document.createElement("span");
        span.style.color = "red";
        span.appendChild(selectedText);
        selection.insertNode(span);
    }
    }

Fiddle: https://jsfiddle.net/xacqzhvq/

As you can see, if I highlight "this will become red as well", I can use the button to make that red too. How can I only color the highlighted text only within the editor1 div?

cosmo
  • 751
  • 2
  • 14
  • 42
  • Which browser it is? On chrome 51 it works – avck Aug 04 '16 at 05:16
  • 1
    I know the function works; but I don't want anything outside of the div "editor1" to be able to change colour as well. Scroll down in the fiddle; there's a line of text outside the div that can also be colored red using the button - I don't want that; I only want any text INSIDE the div to be able to be colored. – cosmo Aug 04 '16 at 05:17

3 Answers3

10

You are able to get the node element from the selection using .baseNode. From there you can get the parent node and use that for comparison.

function red(){
    // If it's not the element with an id of "foo" stop the function and return
    if(window.getSelection().baseNode.parentNode.id != "foo") return;
    ...
    // Highlight if it is our div.
}

In the example below I made the div have an id that you can check to make sure it's that element:

Demo


As @z0mBi3 noted, this will work the first time. But may not work for many highlights (if they happen to get cleared). The <span> elements inside the div create a hierarchy where the div is the parent elements of many span elements. The solution to this would be to take traverse up through the ancestors of the node until you find one with the id of "foo".

Luckily you can use jQuery to do that for you by using their .closest() method:

if($(window.getSelection().baseNode).closest("#foo").attr("id") != "foo") return;

Here is an answer with a native JS implemented method of .closest().

Community
  • 1
  • 1
Spencer Wieczorek
  • 21,229
  • 7
  • 44
  • 54
  • 1
    You may have to recursively check the parentNode until you reach the body as the contenteditable will create multiple tags in different hierarchy. – z0mBi3 Aug 04 '16 at 05:23
  • @z0mBi3 Good point, since this is just a *"highlight once"* thing it would work. I'd advise OP to use jQuery's `.closest()` function. I'll mention that in a edit. – Spencer Wieczorek Aug 04 '16 at 05:29
  • Firefox does not have baseNode, but Chrome and FF both have focusNode, so I used that. – Duncanmoo Oct 05 '20 at 11:53
  • that window.getSelection().baseNode.parentNode.id When creating bold The parent element of the text is obtained and find it Text Therefore, he did not reach the main father element who kissed him So replace the first line with this line and it works successfully Replace window.getSelection().baseNode.parentNode.id != "Foo" To window.getSelection().focusNode.parentElement.closest("#Foo").id != "Foo" – borma425 Jul 31 '21 at 12:48
  • @SpencerWieczorek https://stackoverflow.com/a/68521823/14592575 – borma425 Jul 31 '21 at 12:49
  • @YehiaAli The second part of this explicitly answers this, just replace the `` example with ``. I plugged in `if($(window.getSelection().baseNode).closest("#editor").attr("id") != "editor") return;` directly into a snippet of your question and it worked fine. Keep in mind native `.closest()` had very little support (No Edge/IE) and was very new elsewhere at the time of this question. That said I would agree that `focusNode` is better to use than `baseNode`. – Spencer Wieczorek Jul 31 '21 at 13:45
3

Try This Code :

function addBold(){
 
if(window.getSelection().focusNode.parentElement.closest("#editor").id != "editor") return;

 





  const selection = window.getSelection().getRangeAt(0);
  
  let selectedParent = selection.commonAncestorContainer.parentElement;
  

  let mainParent = selectedParent;
  
  if(selectedParent.closest("b"))
  {
  //Unbold
    var text = document.createTextNode(selectedParent.textContent);
    mainParent = selectedParent.parentElement;
    mainParent.insertBefore(text, selectedParent);
    mainParent.removeChild(selectedParent);
    mainParent.normalize();
  }
  else
  {
    const span = document.createElement("b");
    span.appendChild(selection.extractContents());
    selection.insertNode(span);
    mainParent.normalize();
  }
  

  if (window.getSelection) {
    if (window.getSelection().empty) {  // Chrome
      window.getSelection().empty();
    } else if (window.getSelection().removeAllRanges) {  // Firefox
      window.getSelection().removeAllRanges();
    }
  } else if (document.selection) {  // IE?
    document.selection.empty();
  }




};
<div id="editor" contenteditable="true">

You are the programmers of the future 

</div>


<button onclick="addBold()">Bold</button>

I got the code and added my edits from those following answers :

Bold/unbold selected text using Window.getSelection()

getSelection().focusNode inside a specific id doesn't work

borma425
  • 336
  • 5
  • 17
2

Are you looking for this,

  //html
  <body>
     <p id='editor1'>asdf</p>
     <button onclick='red()'>
     RED
     </button>
  </body>

  //JavaScript

    window.red = function(){
        //var getText = document.getElementById("editor1").innerHTML;
        var selection = window.getSelection().getRangeAt(0);
        var selectedText = selection.extractContents();
        var span = document.createElement("span");
        span.style.color = "red";
        span.appendChild(selectedText);
        selection.insertNode(span);
    }

Plunker: https://plnkr.co/edit/FSFBADoh83Pp93z1JI3g?p=preview

Gopinath Shiva
  • 3,822
  • 5
  • 25
  • 48
  • I already have the working function, I've edited my question to highlight the issue. – cosmo Aug 04 '16 at 05:05
  • I am sorry I am not sure of what is your problem. Do you mean to say incase you highlight both inside div and outside div and on click of button you want the elements inside div only to be highlighted Red? – Gopinath Shiva Aug 04 '16 at 05:12
  • That's exactly the issue. I've linked a fiddle that shows the exact problem:https: //jsfiddle.net/xacqzhvq/ – cosmo Aug 04 '16 at 05:14