【问题标题】:Contenteditable surround element by tag; can't get cursor out of newly surrounded selectionContenteditable 按标签环绕元素;无法将光标移出新包围的选择
【发布时间】:2019-01-03 07:02:39
【问题描述】:

我有一个 div,它在我的学校项目网站上用作用户友好的文本编辑器。但是,如果我选择编辑器内的所有文本并尝试用<code> tags </code> 包围它,我无法将光标移出它,它会不断将我输入的任何内容添加到同一标签中,即<code>

一段时间后我放弃了它,但后来我找到了this 答案并尝试修改那里提到的jsfiddle,希望它能够正常工作。但没有运气。

假设我选择任何一行中的最后一个单词并用<code> 标签包围它,我可以将光标移出它,那里有非包围文本,但最后没有明文可跳转到使光标停留在<code>

如何确保光标不会卡在添加的标签中? 也许一种将光标移出的方法?

jsFiddle

function surroundSelection() {
    var code = document.createElement("code");
    code.style.fontStyle = "italic";
    code.style.color = "#333";
    code.style.background = "#ddd";
    
    if (window.getSelection) {
        var sel = window.getSelection();
        if (sel.rangeCount) {
            var range = sel.getRangeAt(0).cloneRange();
            range.surroundContents(code);
            sel.removeAllRanges();
            sel.addRange(range);
        }
    }
}
div, input {
  padding:10px; 
  
}

div {border:1px solid;}
<input type="button" onclick="surroundSelection()" value="Surround">
<div contenteditable="true">One two three four</div>

【问题讨论】:

    标签: javascript jquery html css


    【解决方案1】:

    这里有一个有点古怪的解决方法。不再使用环绕,而是将内容复制到新节点中。然后我们在末尾添加一个空格。即使选择了所有文本,现在也可以在代码块之后单击。

    编辑:添加 sel.collapseToEnd();根据评论(谢谢)。它通过将用户直接置于正常的打字上下文中来改善体验。但是,空间仍然是必需的,没有它就无法工作。

    编辑:现在在开头和结尾添加空间。

    编辑:删除间隔以发送到服务器。

    function surroundSelection() {
      let code = document.createElement("code");
      code.style.fontStyle = "italic";
      code.style.color = "#333";
      code.style.background = "#ddd";
      const newSpan = () => {
        let span = document.createElement('span');
        span.classList.add('codespacer');
        span.innerHTML = '&nbsp;';
        return span;
      }
    
      if (window.getSelection) {
        let sel = window.getSelection();
        if (sel.rangeCount) {
          let range = sel.getRangeAt(0).cloneRange();
          code.innerHTML = range.toString();
          range.deleteContents();
          range.insertNode(newSpan());
          range.insertNode(code);
          range.insertNode(newSpan());
          sel.removeAllRanges();
          sel.addRange(range);
          sel.collapseToEnd();
        }
      }
    }
    
    
    function getContentForSave() {
      let codeSection = document.querySelector('#codeSection');
      // Copy into a temporary element (otherwise the text will change in the UI)
      let tempSection = document.createElement('div');
      tempSection.innerHTML = codeSection.innerHTML;
      console.log(`Before: ${tempSection.innerHTML}`);
      
      // Remove all codespacers 
      let spacers = tempSection.querySelectorAll('.codespacer');
      for(let space of spacers) {
        tempSection.removeChild(space);
      }
      console.log(`After: ${tempSection.innerHTML}`);
      
      return tempSection.innerHTML;
    }
    div,
    input {
      padding: 10px;
    }
    
    div {
      border: 1px solid;
    }
    <input type="button" onclick="surroundSelection()" value="Surround">
    <div contenteditable="true" id="codeSection">One two three four</div>
    <input type="button" onclick="getContentForSave();" value="Test For Save">

    【讨论】:

    • 啊,太好了,我不知道你可以将节点插入范围——这绝对比我这样做的方式更优雅。您也可以添加sel.collapseToEnd() 将光标移动到最后一行,这将允许用户立即继续编写普通文本
    • @DerekNguyen 添加了代码以反映您的好建议。
    • 如果选择的内容是div中的第一个,我们可以这样做吗?例如:如果您选择“一个”然后将其包围然后尝试在此之前输入,它不会让您。
    • 好的,在节点中您尝试将其保存到数据库中?如果是这样,您可能希望在写入/读取时进行编码/解码。或者,我们可以在保存之前去掉这些间隔,它们只是为了帮助编辑。
    • 我编辑了代码。但是,是的。我也阅读了另一篇文章并同意 Taplar。您应该查看服务器并在此处找出真正的错误。通常,如果您要保存 HTML 等内容,最好在保存时对其进行编码,然后在返回演示文稿时对其进行解码。值得研究。
    【解决方案2】:

    我认为你可以克服它的一种方法是在代码后面附加一个空白空间textNode,然后将光标移动到它的位置。这样用户可以立即继续编写普通文本。

    以下是细分:

    • 使用document.createTextNode('\u00A0') 创建一个包含空格字符的textNode。请注意,文字 ' ' 在我的浏览器 (Chrome 70) 上不起作用。
    • 将新的空文本节点添加到div
    • 使用range.setEnd()将新的空文本节点添加到当前range,这样我们就可以使用selection.collapseToEnd()方法将光标移动到最后。

    一共 (fiddle)

    + function addPad() {
    +   var $input = document.querySelector('div');
    +   var pad = document.createTextNode('\u00A0');
    +   $input.appendChild(pad);
    +   return pad;
    + }
    
      function surroundSelection() {
        var sel = window.getSelection();
        if (!sel || !sel.rangeCount) return;
    
        var code = document.createElement("code");  
        var range = sel.getRangeAt(0).cloneRange();
        range.surroundContents(code);
        sel.removeAllRanges();
        sel.addRange(range);
    
    +   // create empty node + add it to div
    +   var padNode = addPad();
    
    +   // update the current range to include the empty node.
    +   // since we only add 1 space, the `offset` is set to 1.
    +   range.setEnd(padNode, 1);
    
    +   // move the cursor to very end of range
    +   sel.collapseToEnd();
      }
    

    【讨论】:

      猜你喜欢
      • 2011-12-07
      • 2015-06-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-01
      • 2015-04-25
      • 2021-11-13
      • 1970-01-01
      相关资源
      最近更新 更多