我有一个用于聊天应用程序的内容编辑器。当我标记用户时,我找到当前的 caretPosition 并向该位置添加一个跨度,如下所示;
<span class=\"highlight\" contenteditable=\"false\">${userName}</span>
但是,当我用这个跨度替换标签时,我失去了可编辑内容的焦点,并且插入符号位置也丢失了。我尝试过查看这些答案,
但是在这两种情况下焦点都会丢失,如果我在 setCursorPosition 函数之前设置 el.focus() ,插入符号只会转到可编辑内容的开头。我的代码;
getCaretPositionInnerHTML() {
var target = document.createTextNode("\u0001");
document.getSelection().getRangeAt(0).insertNode(target);
var position = this.contenteditable.nativeElement.innerHTML.indexOf("\u0001");
target.parentNode.removeChild(target);
return position;
}
saveMention(event, user): void { //lose focus after this
event.stopPropagation();
let tempString = this.contenteditable.nativeElement.innerHTML;
let index = this.getCaretPositionInnerHTML();
let replacement = this.getSpanFromUserFn(user);// returns the span in question
let currentWordLength = this.getCurrentWord(tempString, index);// replace the current word with my span
let newString = tempString.substring(0, index - currentWordLength.length) + replacement + tempString.substring(index + 1);
this.contenteditable.nativeElement.innerHTML = newString;
}
ngOnChanges(changes: SimpleChanges) {
if (this.caretPosition) {
console.log(changes);
this.selection.removeAllRanges();
let range = this.setCursorPosition(this.element.nativeElement, document.createRange(), { pos: this.caretPosition, done: false});
this.element.nativeElement.focus();
range.collapse(true);
this.sel.addRange(range);
}
//find the child node and relative position and set it on range
setCursorPosition(parent, range, stat) {
if (stat.done) return range;
if (parent.childNodes.length == 0) {
if (parent.textContent.length >= stat.pos) {
range.setStart(parent, stat.pos);
stat.done = true;
} else {
stat.pos = stat.pos - parent.textContent.length;
}
} else {
for (var i = 0; i < parent.childNodes.length && !stat.done; i++) {
this.currentNode = parent.childNodes[i];
this.setCursorPosition(this.currentNode, range, stat);
}
}
return range;
}
我碰巧遇到了一个用例,这就是我最终的结果:
let cursorPosition;
const getCursorLocation = (ele) => {
var target = document.createTextNode("\u0001");
document.getSelection().getRangeAt(0).insertNode(target);
var position = ele.innerHTML.indexOf("\u0001");
target.parentNode.removeChild(target);
cursorPosition = position;
console.log('position', position);
return position;
};
//find the child node and relative position and set it on range
const findingRange = (ind, nodes, position) => {
if (nodes[ind].textContent.length >= position) {
if (nodes[ind].childNodes.length > 0) {
return findingRange(0, nodes[ind].childNodes, position);
}
const node =
nodes[ind].nodeName === "#text" ? nodes[ind] : nodes[ind].firstChild;
return { node, offset: position };
}
return findingRange(
ind + 1,
nodes,
position - nodes[ind].textContent.length
);
};
const updateInput = (input) => {
const userInputBox = document.getElementById("listening-input-change");
if (userInputBox) {
const currentText = userInputBox.innerHTML;
const position = cursorPosition;
const newText =
currentText.substring(0, position) +
input +
currentText.substring(position);
userInputBox.innerHTML = newText;
const range = document.createRange(); //Create a range
const sel = window.getSelection(); //get the selection object
const startingNode = findingRange(
0,
userInputBox.childNodes,
currentText.substring(0, position).replace(/<[^>]+>/g, "").length +
input.length
);
range.setStart(startingNode.node, startingNode.offset);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
userInputBox.focus();
}
};
.listening-input-change {
background: #fff;
border: 1px solid #d4d6db;
border-radius: 12px;
color: #000;
font-size: 14px;
font-weight: 400;
height: 100%;
padding: 12px 12px 40px;
}
<div
contentEditable="true"
id='listening-input-change'
class="listening-input-change"></div>
<button onclick="updateInput('example text inserted')">set caret</button>
<script>
document.getElementById("listening-input-change").addEventListener("input", function () {
const position = getCursorLocation(this);
});
</script>
let cursorPosition;
const getCursorLocation = (ele) => {
var target = document.createTextNode("\u0001");
document.getSelection().getRangeAt(0).insertNode(target);
var position = ele.innerHTML.indexOf("\u0001");
target.parentNode.removeChild(target);
cursorPosition = position;
console.log('position', position);
return position;
};
//find the child node and relative position and set it on range
const findingRange = (ind, nodes, position) => {
if (nodes[ind].textContent.length >= position) {
if (nodes[ind].childNodes.length > 0) {
return findingRange(0, nodes[ind].childNodes, position);
}
const node =
nodes[ind].nodeName === "#text" ? nodes[ind] : nodes[ind].firstChild;
return { node, offset: position };
}
return findingRange(
ind + 1,
nodes,
position - nodes[ind].textContent.length
);
};
const updateInput = (input) => {
const userInputBox = document.getElementById("listening-input-change");
if (userInputBox) {
const currentText = userInputBox.innerHTML;
const position = cursorPosition;
const newText =
currentText.substring(0, position) +
input +
currentText.substring(position);
userInputBox.innerHTML = newText;
const range = document.createRange(); //Create a range
const sel = window.getSelection(); //get the selection object
const startingNode = findingRange(
0,
userInputBox.childNodes,
currentText.substring(0, position).replace(/<[^>]+>/g, "").length +
input.length
);
range.setStart(startingNode.node, startingNode.offset);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
userInputBox.focus();
}
};
函数递归地查找范围的起始节点和偏移量。
然后在更新插入所需的文本后在 updateInput 内调用它。
.listening-input-change {
background: #fff;
border: 1px solid #d4d6db;
border-radius: 12px;
color: #000;
font-size: 14px;
font-weight: 400;
height: 100%;
padding: 12px 12px 40px;
}