Notebooks: Preventing "previousSibling of Null" Errors on Cell Selection (#19153)

* Defensive programming

* PR comments
This commit is contained in:
Chris LaFreniere
2022-04-20 17:37:38 -07:00
committed by GitHub
parent 0718876300
commit 6c4bf812cd

View File

@@ -212,33 +212,34 @@ export class TextCellComponent extends CellView implements OnInit, OnChanges {
this.cellModel.markdownCursorPosition = selection?.getPosition(); this.cellModel.markdownCursorPosition = selection?.getPosition();
} }
} }
const selection = window.getSelection();
const range = selection?.rangeCount > 0 ? selection.getRangeAt(0) : undefined;
// On preview mode change, get the cursor position (get the position only when the selection node is a text node) // On preview mode change, get the cursor position (get the position only when the selection node is a text node)
if (window.getSelection() && window.getSelection().focusNode?.nodeName === '#text' && window.getSelection().getRangeAt(0)) { if (selection.focusNode?.nodeName === '#text' && range) {
let selection = window.getSelection().getRangeAt(0);
// Check to see if the last cursor position is still the same and skip // Check to see if the last cursor position is still the same and skip
if (selection.startOffset !== this.cellModel.richTextCursorPosition?.startOffset) { if (range.startOffset !== this.cellModel.richTextCursorPosition?.startOffset) {
// window.getSelection gives the exact html element and offsets of cursor location // window.getSelection gives the exact html element and offsets of cursor location
// Since we only have the output element reference which is the parent of all html nodes // Since we only have the output element reference which is the parent of all html nodes
// we iterate through it's child nodes until we get the selection element and store the node indexes // we iterate through it's child nodes until we get the selection element and store the node indexes
// in the startElementNodes and endElementNodes and their offsets respectively. // in the startElementNodes and endElementNodes and their offsets respectively.
let startElementNodes = []; let startElementNodes = [];
let startNode = selection.startContainer; let startNode = range.startContainer;
let endNode = selection.endContainer; let endNode = range.endContainer;
while (startNode !== this.output.nativeElement) { while (startNode && startNode !== this.output.nativeElement) {
startElementNodes.push(this.getNodeIndex(startNode)); startElementNodes.push(this.getNodeIndex(startNode));
startNode = startNode.parentNode; startNode = startNode.parentNode;
} }
let endElementNodes = []; let endElementNodes = [];
while (endNode !== this.output.nativeElement) { while (endNode && endNode !== this.output.nativeElement) {
endElementNodes.push(this.getNodeIndex(endNode)); endElementNodes.push(this.getNodeIndex(endNode));
endNode = endNode.parentNode; endNode = endNode.parentNode;
} }
// Create cursor position // Create cursor position
let cursorPosition: ICaretPosition = { let cursorPosition: ICaretPosition = {
startElementNodes: startElementNodes, startElementNodes: startElementNodes,
startOffset: selection.startOffset, startOffset: range.startOffset,
endElementNodes: endElementNodes, endElementNodes: endElementNodes,
endOffset: selection.endOffset endOffset: range.endOffset
}; };
this.cellModel.richTextCursorPosition = cursorPosition; this.cellModel.richTextCursorPosition = cursorPosition;
} }
@@ -267,8 +268,8 @@ export class TextCellComponent extends CellView implements OnInit, OnChanges {
getNodeIndex(n: Node): number { getNodeIndex(n: Node): number {
let i = 0; let i = 0;
// walk up the node to the top and get it's index // walk up the node to the top and get its index
n = n.previousSibling; n = n?.previousSibling;
while (n) { while (n) {
i++; i++;
n = n.previousSibling; n = n.previousSibling;