1

Let's say I have a random string (span) of characters, each in their own span. A user can select these. I want to know which ones they select. The window.GetSelection API is useful here. It returns a Selection. However, I can't seem to figure out how I can get which characters the user selected, i.e. a NodeList of character spans. I do not mean simply which string it is (which you can get with .toString()) but also which character spans the selection belongs to.

I know there is Selection.anchorNode and Selection.focusNode as the respective start and end nodes but I want to get all of the selected nodes.

In the snippet below you can have a look.

function getSelectionNodes() {
  const selection = window.getSelection();
  let anchor = selection.anchorNode,
    focus = selection.focusNode;
  anchor = anchor.nodeType === Node.TEXT_NODE ? anchor.parentNode : anchor
  focus = focus.nodeType === Node.TEXT_NODE ? focus.parentNode : focus

  const anchorIdx = getIndex(anchor),
    focusIdx = getIndex(focus);

  console.log(anchor);
  console.log(focus);
};

function getIndex(el) {
  return Array.from(el.parentNode.children).indexOf(el)
}
<span onmouseup="getSelectionNodes()">
<span>d</span>
<span>d</span>
<span>s</span>
<span>r</span>
<span>e</span>
<span>g</span>
<span>p</span>
<span>j</span>
<span>l</span>
<span>u</span>
</span>

Note a particular difficulty: If you select a couple of letters and a seeming "space" after them, you'll notice that the anchor will not be a character span, but the parent span because we selected the "space between" the character spans, which is a text node with the wrapper span. This seems problematic. If I could get the starting and ending character span, I could use an while loop with nextSibling or previousSibling to get what I want, I think.

Bram Vanroy
  • 27,032
  • 24
  • 137
  • 239
  • 1
    This question is (sort of) being discussed on [meta](https://meta.stackoverflow.com/q/415017/17242583). –  Dec 30 '21 at 17:36
  • As said on Meta, avoid `onmouseup` attributes. In this scope, `getSelection` has nothing to do with your function `getSelection`, but refers to `document.getSelection`. Related: [JS function named `animate` doesn't work in Chrome, but works in IE](/q/28173800/4642212). – Sebastian Simon Dec 30 '21 at 18:37
  • (1/2) I think this will help you by visualizing the html that the browser copies when you select and copy text: (1) Copy and paste this script into your JS console (but don't execute it yet): `setTimeout(async () => console.log(await (await (await navigator.clipboard.read())[0].getType('text/html')).text()), 3000);` (2) Select a range of text in the page across multiple elements and cmd/ctrl+c to copy (3) Focus the console again and execute what you pasted from step 1 (4) Within 3 seconds, click in the document again to focus it (5) Look at the console to see the html – jsejcksn Dec 31 '21 at 09:14
  • (2/2) [`Clipboard.read()`](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/read) – jsejcksn Dec 31 '21 at 09:15
  • The meta conversation no longer exists. Did you find a solution for this? Conceptually it seems very simple, consisting of three phases: working from the inside of the start element to the common ancestor, picking up everything wholly in the common ancestor, then working from the inside of the end element to the common ancestor. I've spent a day and a half on this (it's over a hundred lines of code in three separate functions); each time I think I'm done I find another edge case that break the whole thing. Attempts to refactor are a mess. Adding to this, it's not clear which end is inclusive – Michael Nov 20 '22 at 03:17

0 Answers0