1

I've tried 4 ways to clone the Selection object but it does not work.

The first 3 methods return a empty Object.

document.onselectionchange = function() {
  let selection = document.getSelection();

  if( !selection.isCollapsed ) {
      var clone1 = Object.assign({}, selection);
      var clone2 = {
        ...selection
      };
      var clone3 = JSON.stringify(selection);
      // This one trows a Error
      // Uncaught DOMException: Failed to execute 'structuredClone' on 'Window': Selection object could not be cloned.
      var clone4 = structuredClone(selection);

  }

  let {anchorNode, anchorOffset, focusNode, focusOffset} = selection;

  // anchorNode and focusNode are text nodes usually
  console.log("from.value", `${anchorNode["data"]}, offset ${anchorOffset}`)
  console.log("fto.value", `${focusNode["data"]}, offset ${focusOffset}`)

}

It only works this way:

document.onselectionchange = function() {
  let selection = document.getSelection();

  if( !selection.isCollapsed ) {
      var clone5 = {};
      clone5.anchorNode = selection.anchorNode;
      clone5.anchorOffset = selection.anchorOffset;
      clone5.focusOffset = selection.focusOffset;
      clone5.focusNode = selection.focusNode;
      console.log("clone5", clone5);
  }

  let {anchorNode, anchorOffset, focusNode, focusOffset} = selection;

  // anchorNode and focusNode are text nodes usually
  console.log("from.value", `${anchorNode["data"]}, offset ${anchorOffset}`)
  console.log("fto.value", `${focusNode["data"]}, offset ${focusOffset}`)

}
Marian07
  • 2,303
  • 4
  • 27
  • 48
  • Cloning the selection object seems as though it may be slightly problematic - out of interest, why are you looking to clone the whole thing? If you only need a few properties, then it may make more sense to just take those properties – OliverRadini Jan 03 '23 at 14:21
  • 1
    @OliverRadini it's not that hard. There are a lot of objects, that take advantage of the prototype inheritance like `screen`, `navigator` or even `window`. `window.PERSISTENT` is one of the inherited properties on the Window Object. `Object.keys(window)` won't return it, but `Object.keys(Window.prototype)` will. – Christopher Jan 03 '23 at 15:35
  • @OliverRadini, I was curious why I was not able to clone it with those methods. A few properties are enough. – Marian07 Jan 03 '23 at 15:58
  • @Christopher indeed, just wanted to verify whether this was an XY problem first – OliverRadini Jan 03 '23 at 17:08

1 Answers1

1

The reason why none of them work is, that the object returned by getSelection() doesn't have any of this properties assigned on its own. All methods you used rely on the fact, that the object has the properties on it's own.

The properties are all inherited via prototype inheritance from the Selection.prototype and have a function defined as getter, which results in a read only value.

Even Object.getOwnPropertyDescriptors(getSelection()) returns an empty object.

But with a for-in loop you can copy it, as it iterates through every enumerable property, including inherited props from the prototype.

const sel = getSelection(), clone = {};
for (const prop in sel) {

    const desc = Object.getOwnPropertyDescriptor(sel.constructor.prototype, prop);
    console.log(
      "prop", prop ,
      "own:", sel.hasOwnProperty(prop),
      "enumerable", sel.propertyIsEnumerable(prop),
      "proto?", sel.constructor.prototype.hasOwnProperty(prop)
    );
    // functions should be avoided
    if (typeof sel[prop] == "function")
        continue;
    console.log("getter?", desc.hasOwnProperty('get'));
    clone[prop] = sel[prop];
}
console.log(sel); // orig
console.log(clone); // clone without functions
Christopher
  • 3,124
  • 2
  • 12
  • 29