我想知道是否可以在关闭的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的解决方案,换句话说,问题将是:“如何控制光标?”
最佳答案
这是使用zero width non-joiner
(‌
)字符的方法。它解决了问题,但添加了一个额外的角色,该角色不会改变外观,但在使用箭头左右移动插入符号时有问题,希望对您有所帮助。
<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)结合使用。因此,我们的想法是在每个子跨度之后添加
‌
,以便我们可以利用其弱点(这是添加一个不可见的空间,需要一个额外的右箭头)作为其强度,因此,当用户点击末尾的右箭头时,绿色跨度,他将跟随父(蓝色)跨度中的‌
字符。因此我们生成的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>
请注意,我们使用的是
'\u200c'
而不是'‌'
,这是由于createTextNode
的缘故,我们必须使用它的Unicode,即u200c
。编辑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>