Search code examples
angularrenderer

equivalent of surroundContents in angular2


i want to use the equivalent of surroundContents() in angular with renderer2

having the caret in a certain position in a contenteditable div i want onClick() to wrap the previous charater in a span (I'm assuming that on click the caret position is not changing with preventdefault)

onChaining(event) {
      const caretPosition = window.getSelection().getRangeAt(0);
      const range = document.createRange();
      range.setStart(caretPosition.commonAncestorContainer, caretPosition.startOffset - 1);
      range.setEnd(caretPosition.commonAncestorContainer, caretPosition.endOffset );

      const wrap= this.renderer.createElement('span');
      range.surroundContents(wrap);

}

i want to use renderer2 instead of surroundContents and have that new dom reference so that i can roll back and delete the wrap if needed.

The position of the caret is not known (in a text node) so no previous reference exists.


Solution

  • Well it seems Renderer2 expects parent objects to have method appendChild(). So you cannot use something like this.renderer.appendChild(range,wrap) and same with insertBefore. But still it is possible to achieve an undoable behavior. According the documentation surrondContents:

    This method is nearly equivalent to newNode.appendChild(range.extractContents()); range.insertNode(newNode); So we repeat the mentioned behavior and will keep the range and a clone of a selected text node.

    changes: {
      range: Range;content: Node
    }[] = [];
    
    render() {
      const caretPosition = window.getSelection().getRangeAt(0);
      const range = document.createRange();
      range.setStart(
        caretPosition.commonAncestorContainer,
        caretPosition.startOffset - 1
      );
      range.setEnd(
        caretPosition.commonAncestorContainer,
        caretPosition.endOffset
      );
    
      const wrap = this.renderer.createElement("span");
      this.renderer.setStyle(wrap, "background-color", "red");
      const contents = range.extractContents();
    
      this.changes.push({
        range: range,
        content: contents.cloneNode(true)
      });
      this.renderer.appendChild(wrap, contents);
      range.insertNode(wrap);
    }
    
    undo() {
      const action = this.changes.pop();
      const range = action.range;
      const node = range.extractContents();
      range.insertNode(action.content);
    }

    And here is the Stackblitz sample