javascript - 关闭标签和打开标签之间的光标位置

标签 javascript html

我想知道是否可以在关闭的 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;">&zwnj;Bar
      <span style="color: green;">&zwnj;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>&zwnj;<span style="color: blue;">
Bar<span style="color: green;">
From Baz</span>&zwnj;</span>&zwnj;</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 '&zwnj;', it's because of the createTextNode, and we have to use it's Unicode which is 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>

关于javascript - 关闭标签和打开标签之间的光标位置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56290441/

相关文章:

javascript - 我可以在尝试预加载 XHR 数据时添加自定义标题吗?

jquery - 使用 JSON 和 jQuery 生成 HTML 标签

html - div定位: absolute,相对等

html - 用于 mozilla 和 safari 的样式表栏

html - CSS3 : Space between left panel and an iframe div

html - 如何在 Raspberry Pi 4 的 kiosk 模式下使垂直滚动条在 chromium-browser 中更大和/或更靠左?

javascript - AngularJS 路由问题错误的 URL

javascript - Electron < WebView > : Uncaught SyntaxError: Unexpected token

javascript - Chrome/Android杀死了后台运行的websocket

javascript - 如何在引导模式窗口中显示 flash 消息?