17

I have a React application which displays some spans:

<span>Hello</span> <span>my</span> <span>name</span> <span> is </span> ...

I would like the user to select the text with the mouse like so Hello my Name is

..and then get the selected value, or highlight the text etc. How would I do this in React? I am not sure what event handlers to use and how to get hold of the current selection! A minimal example or a hint would be appreciated!

George Welder
  • 3,787
  • 11
  • 39
  • 75
  • Possible duplicate of [Get the Highlighted/Selected text](http://stackoverflow.com/questions/5379120/get-the-highlighted-selected-text) – Lyubomir Apr 03 '17 at 12:38
  • I don't think this is a duplicate, as I am asking specifically about React here - but I guess there is no React specific solution then? – George Welder Apr 03 '17 at 13:11
  • there is no need for specific react solution, as there is more powerful and general solution that works across all JS frameworks / approaches - https://developer.mozilla.org/en-US/docs/Web/API/Window/getSelection – Lyubomir Apr 03 '17 at 13:17
  • I understand. The only thing I'm saying is that this then does not make it a duplicate as my question aimed at React specifically. The answer then should be that there are not React specific handlers for this. But well, I guess I also was not clear enough in my question. Thanks a lot anyways. – George Welder Apr 03 '17 at 13:27
  • 1
    now I got you. Right, sorry! I've edited my answer to reflect that. – Lyubomir Apr 03 '17 at 13:28
  • Oh I also just edited yours! I hope I didn't overwrite anything! – George Welder Apr 03 '17 at 13:30
  • 1
    I've taken yours, no worries, all good. Have a nice day! – Lyubomir Apr 03 '17 at 13:30

10 Answers10

23

Use onMouseUp and onDoubleClick events to detect to call method, that will determine selection using JavaScript Selection API.

Use window.getSelection() to get selection object.

To get selected text, use window.getSelection.toString().

To get coordinates of selected text area for rendering popup menu, use selection.getRangeAt(0).getBoundingClientRect().

As an example implementation, take a look at react-highlight library.

AntonAL
  • 16,692
  • 21
  • 80
  • 114
  • 2
    What if the user selects the text using Shift + arrows in the keyboard without interacting with mouse? – Shahin Ghasemi Apr 11 '20 at 08:42
  • There is no such use case, because when browsing web pages you don't have keyboard controlled cursor for non editable areas. – AntonAL Apr 18 '20 at 09:30
  • @AntonAL I'm afraid you're wrong. Try it: double click on the word "JavaScript" in the answer above to select it and then press Shift+Cursor Right; you will see the selection expand without further mouse interaction. – Stefan Reisner Apr 26 '23 at 07:18
14

There is no React-specific solution for this. Just use window.getSelection API.


To output highlighted text run window.getSelection().toString()

Agu V
  • 2,091
  • 4
  • 25
  • 40
Lyubomir
  • 19,615
  • 6
  • 55
  • 69
10

Here's an example in React using a functional component:

const Post = () => {
    const handleMouseUp() {
        console.log(`Selected text: ${window.getSelection().toString()}`);
    }
    return (
        <div onMouseUp={handleMouseUp}>Text</div>
    );
}

As Lyubomir pointed out the window.getSelection() API does the trick!

jacobedawson
  • 2,929
  • 25
  • 27
  • 1
    I just copy that as a function in my component ! It works perfectly just npx create react app and delete app.js and add this : let doit = () => { document.onmouseup = () => { console.log(window.getSelection().toString()); }; }; – Farbod Aprin Feb 03 '22 at 23:02
6

I think that it's the right way...

 document.onmouseup = () => {
   console.log(window.getSelection().toString());
 };
JuliSmz
  • 996
  • 1
  • 12
  • 26
3

Step 1: - Initially Mouse will capture every time you enter something

<textarea type="text" 
          className="contentarea__form__input" 
          onMouseUpCapture={this.selectedText}>
</textarea>

Step 2: To make sure it only catches your selected text I used if-else condition

selectedText = () => {
    window.getSelection().toString() ? console.log(window.getSelection().toString()) : null;
}
Sachin Shukla
  • 335
  • 3
  • 3
2

Due to this bug in Firefox, I was unable to get it working with window.getSelection() as suggested by other answers.

So I had to do the following (in React):

constructor(props) {
    this.textAreaRef = React.createRef();
}

getSelection() {
    const textArea = (this.textAreaRef.current as HTMLTextAreaElement);
    console.log(textArea.value.substring(
            textArea.selectionStart,
            textArea.selectionEnd
        )
    );
}

render() {
    <textarea onMouseUp={() => this.getSelection()}
                        ref={this.textAreaRef}>
    </textarea>
}
Seva
  • 1,631
  • 2
  • 18
  • 23
1

I just made a custom hook for this (in typescript if needed):


    export const useSelectedText = () => {
        const [text, setText] = useState('')
        const select = () => {
            const selected = window.getSelection() as Selection
            setText(selected.toString())
        }
        return [select, text] as const
    }
0

This can be done using react-text-annotate library: https://www.npmjs.com/package/react-text-annotate

Vishal Sinha
  • 121
  • 1
  • 8
0

We can do using state simply to select and highlight them.

import styled from 'styled-components';

const ClipBoardLink = styled.span<{ selection: boolean }>`
  background: ${(p) => (p.selection ? '#1597E5' : '')};
  margin-left: 0.5rem;
  color: ${(p) => (p.selection ? '#fff' : '')};
`;

const GroupOrderModel = () => {
  const [isCopied, setIsCopied] = useState(false);

  const handleIsClipboardCopy = async (text: string) => {
    setIsCopied(true);

    navigator.clipboard.writeText(text);

    setTimeout(() => {
      setIsCopied(false);
    }, 5000);
  };

  return (
      <ClipBoardLink selection={isCopied} onClick={async () => await handleIsClipboardCopy("Click me!")}>Click me!</ClipBoardLink>
   );
}
Dharman
  • 30,962
  • 25
  • 85
  • 135
Mohamed Jakkariya
  • 1,257
  • 1
  • 13
  • 16
0

You need to grab the first and last indices, and figure out the words from there.

const [highlightStart, setHighlightStartIndex] = useState();
const [highlightEnd, setHighlightEnd] = useState();
const paragraph = "some text in paragraph blah blah blah";
const paragraphArr = paragraph.split(" ").map((word, index) => (
    <span
      key={index}
      id={`paragraph_${index}`}
      onMouseDown={(e) => handleHighlightStart(index)}
      onMouseUp={(e) => handleHighlightEnd(index)}
    >
      {word}{" "}
    </span>
  ));


const handleHighlightStart = (index) => {
    console.log("highlightStart", index);
    setHighlightIndexStart(index + 1); //because start word is the one after cursor starts
  };

  const handleHighlightEnd = (index) => {
    console.log("highlightEnd", index);
    setHighlightIndexEnd(index - 1); //because end word is the last one before cursor ends
  };

You can figure out the selected text from the indices. You would also need to take care of cases when the user highlights backward.

guest
  • 2,185
  • 3
  • 23
  • 46