Search code examples
javascriptjquerycontenteditabletextselection

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


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?


Solution

  • 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);
    }