我想知道是否可以在关闭的 HTML 标签和打开的 HTML 标签之间设置插入符号。
这是我的 HTML:
<div contentEditable="true">
<div> Hello
<span style="color: red;"> Foo </span>
<span style="color: blue;">
Bar
<span style="color: green;">
From Baz
</span>
</span>
</div>
</div>
因此,如果光标位于绿色标记 <span style="color: green;">From Baz</span>
的末尾(如果光标位置在anchorNode的末尾如何获取?),通过按向右箭头键,它移动到蓝色的父跨度。
即使使用 setStart
在绿色范围开始的范围变量上,它将光标设置为蓝色范围的末尾(Bar
),然后写入它将是蓝色的(预期是在绿色范围的开始处)。
问题是,是否可以控制标签之间的光标位置?
更新
1 - 我需要纯 Javascript 解决方案。 2 - 它的行为也因浏览器而异。因此,它需要一个仅依赖于 Javascript 的解决方案,换句话说,问题将是“如何控制游标?”
最佳答案
这是一种使用零宽度非连接符
(
) 字符的方法。它解决了问题,但添加了一个额外的字符,它不会改变外观,但在使用箭头左右移动插入符时出现问题,希望它有所帮助。
<div contentEditable="true">
<div> Hello
<span style="color: red;">Foo </span>
<span style="color: blue;">‌Bar
<span style="color: green;">‌From Baz</span>
</span>
</div>
</div>
编辑
这是另一种没有上述问题的方法:
<div contentEditable="true">
<div> Hello<span style="color: red;">
Foo</span><span style="color: blue;">
Bar<span style="color: green;">
From Baz</span>
</span>
</div>
</div>
编辑2
您还可以使用 Javascript 执行以下操作:
function RemoveSpaces(myQuery, parentNodeName){
var myElement = document.querySelector(myQuery);
var iterator = document.createNodeIterator(myElement, NodeFilter.SHOW_TEXT, node => {
if (node.parentElement.nodeName == parentNodeName) {
return NodeFilter.FILTER_ACCEPT;
}
return NodeFilter.FILTER_REJECT;
});
var toBeRemoved = [];
let next = iterator.nextNode();
while(next) {
toBeRemoved.push(next);
next = iterator.nextNode();
}
toBeRemoved.forEach(n => RemoveFunction(n));
}
function RemoveFunction(c){
if (!c.nodeValue.replace(/\s/g, '').length){
c.remove();
}
else{
c.nodeValue = "\n" + c.nodeValue.trim();
}
}
RemoveSpaces('div[contenteditable="true"] div', 'DIV');
RemoveSpaces('div[contenteditable="true"] div', 'SPAN');
<div contentEditable="true">
<div>Hello
<span style="color: red;"> Foo </span>
<span style="color: blue;">
Bar
<span style="color: green;">
From Baz
</span>
</span>
</div>
</div>
编辑3
对于评论中提到的问题,我们可以将第一种方式(
)与第三种方式JS结合起来。
所以我们的想法是在每个子 span 之后添加
,这样我们就可以利用它的弱点(添加一个不可见的空间,需要额外的右箭头)作为它的优势,所以当用户点击绿色跨度末尾的向右箭头,他将追寻父(蓝色)跨度中的
字符。
所以我们生成的 HTML 应该是这样的:
<div>
Hello<span style="color: red;">
Foo</span>‌<span style="color: blue;">
Bar<span style="color: green;">
From Baz</span>‌</span>‌</div>
这是实现该目标的完整代码:
function RemoveSpaces(myQuery, parentNodeName){
var myElement = document.querySelector(myQuery);
var iterator = document.createNodeIterator(myElement, NodeFilter.SHOW_TEXT, node => {
if (node.parentElement.nodeName == parentNodeName) {
return NodeFilter.FILTER_ACCEPT;
}
return NodeFilter.FILTER_REJECT;
});
var toBeRemoved = [];
let next = iterator.nextNode();
while(next) {
toBeRemoved.push(next);
next = iterator.nextNode();
}
toBeRemoved.forEach(n => RemoveFunction(n));
}
function RemoveFunction(c){
if (!c.nodeValue.replace(/\s/g, '').length){
c.remove();
}
else{
c.nodeValue = "\n" + c.nodeValue.trim();
}
}
RemoveSpaces('div[contenteditable="true"] div', 'DIV');
RemoveSpaces('div[contenteditable="true"] div', 'SPAN');
//Added Code
var childrenspans = document.querySelector("div[contenteditable='true']").getElementsByTagName("span");
var newNode = document.createTextNode('\u200c');
childrenspans[childrenspans.length-1].parentNode.insertBefore(newNode, childrenspans[childrenspans.length-1].nextSibling);
<div contentEditable="true">
<div>Hello
<span style="color: red;"> Foo </span>
<span style="color: blue;">
Bar
<span style="color: green;">
From Baz
</span>
</span>
</div>
</div>
Note that we are using
'\u200c'
instead of'‌'
, it's because of thecreateTextNode
, and we have to use it's Unicode which isu200c
.
编辑4
如评论中所述,Edit 3 代码有效,但在绿色跨度后添加文本后,使用第一种方法会遇到与之前相同的问题
这意味着文本中间有一个额外的
。
我们可以在每次用户输入内容时跟踪输入,并且我们可以删除
字符(如果它位于空格字符旁边)。
它并不能完全解决问题,但会有所帮助。 这是代码:
function RemoveSpaces(myQuery, parentNodeName){
var myElement = document.querySelector(myQuery);
var iterator = document.createNodeIterator(myElement, NodeFilter.SHOW_TEXT, node => {
if (node.parentElement.nodeName == parentNodeName) {
return NodeFilter.FILTER_ACCEPT;
}
return NodeFilter.FILTER_REJECT;
});
var toBeRemoved = [];
let next = iterator.nextNode();
while(next) {
toBeRemoved.push(next);
next = iterator.nextNode();
}
toBeRemoved.forEach(n => RemoveFunction(n));
}
function RemoveFunction(c){
if (!c.nodeValue.replace(/\s/g, '').length){
c.remove();
}
else{
c.nodeValue = "\n" + c.nodeValue.trim();
}
}
RemoveSpaces('div[contenteditable="true"] div', 'DIV');
RemoveSpaces('div[contenteditable="true"] div', 'SPAN');
var childrenspans = document.querySelector("div[contenteditable='true']").getElementsByTagName("span");
var newNode = document.createTextNode('\u200c');
childrenspans[childrenspans.length-1].parentNode.insertBefore(newNode, childrenspans[childrenspans.length-1].nextSibling);
//Added Code
function saveCaretPosition(context){
var selection = window.getSelection();
var range = selection.getRangeAt(0);
range.setStart( context, 0 );
var len = range.toString().length;
return function restore(x){
var pos = getTextNodeAtPosition(context, len);
selection.removeAllRanges();
var range = new Range();
if(x)
range.setStart(pos.node ,pos.position-1);
else
range.setStart(pos.node ,pos.position);
selection.addRange(range);
}
}
function getTextNodeAtPosition(root, index){
var lastNode = null;
var treeWalker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT,function next(elem) {
if(index >= elem.textContent.length){
index -= elem.textContent.length;
lastNode = elem;
return NodeFilter.FILTER_REJECT
}
return NodeFilter.FILTER_ACCEPT;
});
var c = treeWalker.nextNode();
return {
node: c? c: root,
position: c? index: 0
};
}
function RemoveCharCodeIfBesideCharacter(str, charCode, besideCharacter){
function SortNumber(a, b) {return a - b;}
var indexesToRemove = [];
for (var i = 0; i < str.length; i++) {
var strchar = str.charCodeAt(i);
if(strchar == charCode && i != 0 && i != str.length-1){
if(str.charAt(i-1) == besideCharacter || str.charAt(i+1) == besideCharacter)
indexesToRemove.push(i);
}
}
indexesToRemove.sort(SortNumber);
var removeCount = 0;
for(var i=0; i < indexesToRemove.length; i++){
str = str.slice(0, indexesToRemove[i]-removeCount) + str.slice(indexesToRemove[i]+1-removeCount);
removeCount++;
}
return str;
}
document.querySelector("div[contenteditable='true']").addEventListener("input", function() {
var restore = saveCaretPosition(this);
var before = this.innerHTML;
this.innerHTML = RemoveCharCodeIfBesideCharacter(this.innerHTML, "8204", " ");
restore(before != this.innerHTML);
}, false);
<div contentEditable="true">
<div>Hello
<span style="color: red;"> Foo </span>
<span style="color: blue;">
Bar
<span style="color: green;">
From Baz
</span>
</span>
</div>
</div>
关于javascript - 关闭标签和打开标签之间的光标位置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56290441/