5

Here's my problem, in case you're interested: I want to find the text selected by the user and insert a tag at the start and end of that selection (to highlight the text).

The best approach to find the selection, as quoted by the top minds of StackOverflow, is to use window.getSelection().

This, in combination with getRangeAt(0), turns up a list of things, where startContainer and endContainer look especially promising. startContainer.parentNode points directly to the p tag I started the selection in. Great!

However, how do I actually know which element this represents in the React DOM? How do I manipulate the right thing?

Yeats
  • 2,112
  • 3
  • 30
  • 46
  • you should be adding css class to highlight the text instead of tag. and please provide what you already have – Mox Feb 28 '18 at 15:40
  • 2
    @Mox if the selection is just a portion of the text content of an element, or if it spans several elements, adding a class to the parent won't work; *all* the content of the parent tag(s) will be affected. – Pointy Feb 28 '18 at 15:41
  • 2
    @Mox No, I shouldn't. That would require me to clairvoyantly know which text the user will select and already have a tag spanning that length... – Yeats Feb 28 '18 at 15:42
  • if that is the case, in your react component, you will know where that is rendered, simply add a if statement to render a different version with the tag – Mox Feb 28 '18 at 15:42
  • Is your highlighting meant to persist? Or are you just trying to change the color of the default highlighting? – Tyler Feb 28 '18 at 15:44
  • @Mox Well, that's generally how to manipulate the virtual DOM in React, yes... – Yeats Feb 28 '18 at 15:44
  • @Tyler Yes, it will persist. – Yeats Feb 28 '18 at 15:44
  • https://stackoverflow.com/questions/42619553/how-to-get-the-selected-text-from-text-area-in-react -- this may be relevant – Mox Feb 28 '18 at 15:45
  • @Yeats, does the components that will have this persisting highlighting functionality have to re-render often? – Tyler Feb 28 '18 at 16:03

2 Answers2

0
window.addEventListener("mousedown", function(e){console.log(e)});

using this event listener (or better use mouseup), you can find all the data about the target, example: if you copy/paste this piece of code into the console and see the log, you'll find all info about this event including the target, timestamp, shift key, control key, x,y, which mouse button, and all relevant information. if you combine this with what you described earlier you will get the results you need

Mohamad Atiye
  • 24
  • 1
  • 4
  • People use touch screens too, and if I wanted a hacky way to do things I would just add a random class or something to every element and find it that way. – Yeats Feb 28 '18 at 15:47
0

EDIT: As Mox had indirectly mentioned... When React re-renders, all the of the new nodes would be over-written and the highlighting would be lost. This option as it is won't work well with React.

I'm going to leave this answer here since it is an option if the highlighted text is not being re-rendered often.


I believe the [surroundContents][1] method will be of great use to you.

According to the documentation, you can create a new node. Setup the class attribute on that new node. This class should enable the highlighting you want.

Then you can pass the new node into the surroundContents method to place the content selected by the range into your new node.

Here is an example from the documentation:

var range = document.createRange();
var newNode = document.createElement("p");

range.selectNode(document.getElementsByTagName("div").item(0));
range.surroundContents(newNode);

I believe you already have a range object, so you would just need to create a new node to wrap the highlighted text and use the surroundContents method.

I do see 1 major concern with this method.

If a user highlights text from inside a container and outside the same container, a situation like following may occur:

<p>
    some text
<span class="highlighting">
    some more text
</p>
   some other text
</span>

I do not know how surroundContents will handle this, so this may be become a problem for you.

Tyler
  • 957
  • 11
  • 27
  • @Mox Hmmm... Yes I see where you are going since React would easily re-render and get rid of persisting highlighting... – Tyler Feb 28 '18 at 15:58
  • As a heads up to anyone that wants to know ahead of time what will happen when you try to overlap elements as Tyler showed, you will get the following error: Uncaught DOMException: Failed to execute 'surroundContents' on 'Range': The Range has partially selected a non-Text node. – jacobedawson Jul 28 '19 at 12:10