1

I want to mirror the text selection in a contenteditable div in one browser in another browser. The simplified and relevant section of the code is:

In the source browser:

function someFunc() {
  if (window.getSelection().isCollapsed)
       return;
  var range = window.getSelection().getRangeAt(0);
  
  //Send range over websockets to another browser
  sendToAnotherBrowser(...) //Send range

}

In the remote browser, I execute the following code:

 var sel = window.getSelection();
 sel.addRange(range);  //Where range is the range transmitted over webSockets

I get the following error:

 "Failed to execute 'addRange' on 'Selection': parameter 1 is not of type 'Range'

QUESTION: How to transmit the "Range" object over websockets? It is not a JSON object to be stringified and sent. Can I serialize it somehow, change it to base64 and then reverse the process on the receiving end?

Sunny
  • 9,245
  • 10
  • 49
  • 79

1 Answers1

1

You can't serialize the object itself, buy you can create an object that contains the data of the Range object, send it to the socket and build a new Range object from there.

const sendRange = range => {
  const {
    startContainer,
    endContainer,
    startOffset, 
    endOffset, 
    collapsed
  } = range;
  const package = JSON.stringify({
    startNodeId: startContainer.id,
    endNodeId: endContainer.id,
    startOffset, 
    endOffset
    collapsed
  });
  sendToAnotherBrowser(package);
}

const range = window.getSelection().getRangeAt(0);
sendRange(range);

And receive it somewhere else where you build a new object.

const buildRange = package => {
  const { 
    startNodeId,
    endNodeId,
    startOffset, 
    endOffset, 
    collapsed 
  } = JSON.parse(package);
  const selection = window.getSelection();
  const range = document.createRange();
  const startNode = document.getElementById(startNodeId);
  const endNode = document.getElementById(endNodeId);
  range.setStart(startNode, startOffset);
  range.setEnd(endNode, endOffset);
  if (collapsed) {
    range.collapse();
  }
  selection.addRange(range);
}
Emiel Zuurbier
  • 19,095
  • 3
  • 17
  • 32
  • Right track but setStart() expects two arguments. The first is startNode. I thought I would get it from range but in the remote browser it complains: 'Failed to execute 'setStart; on 'Range': parameter 1 is not of type 'Node' Any suggestions? – Sunny Aug 29 '20 at 15:18
  • Also, why are only these properties and not other properties of range required in remote browser? – Sunny Aug 29 '20 at 15:25
  • I overlooked that first argument. In this case I suggest that you give your selection elements `id` attributes and send the ids over to the remote browser. There do a `getElementById` query and select the same elements. As for your second question: according to example with the [`Range constructor](https://developer.mozilla.org/en-US/docs/Web/API/Range/Range) is will be sufficient to have just a beginning and and end of the range to update the selection. Although you should probably test this to make sure if it works. – Emiel Zuurbier Aug 29 '20 at 15:32
  • I am accepting your answer as you pointed me in the right direction. Unfortunately I cannot get ids of the nodes because these are created in a contenteditable div using execCommand to change fonts, color, font-size, etc. These nodes created do not have ids. May be you have some suggestions to get around this? – Sunny Aug 29 '20 at 15:40
  • Also, range object does not have startNode and endNode properties. There is a selectedNode property. – Sunny Aug 29 '20 at 15:48
  • 1
    Fixed that, that should have been `startContainer` and `endContainer`. But as for your former question; It seems that you should look into ways on how to recognize and distinguish text nodes within a different document. Or develop your own system that charts two points in your own document and translates that to a selection in the other document. – Emiel Zuurbier Aug 29 '20 at 15:54
  • 1
    Or only send the selected text and do a regex to find the position of that text in your remote browser, something like that. I hope you can figure something out. – Emiel Zuurbier Aug 29 '20 at 15:55