【问题标题】:Place cursor(caret) in specific position in <pre> contenteditable element将光标(插入符号)放在 <pre> contenteditable 元素中的特定位置
【发布时间】:2017-08-30 20:02:40
【问题描述】:

我正在制作一个 Web 应用程序来测试正则表达式。我有一个输入,我在其中输入正则表达式和一个 contenteditable pre 元素,我在其中输入找到并突出显示匹配项的文本。

示例:假设正则表达式为 ab,如果用户在 pre 元素中键入 abcab,则正则表达式和文本都会发送到我实现的 api 并返回

    &lt;span style='background-color: lightgreen'&gt;ab&lt;/span&gt;c&lt;span style='background-color: lightgreen'&gt;ab&lt;/span&gt;

并且这个字符串被设置为前元素的innerHTML

每次用户编辑前置元素的内容时都会执行此操作(确切地说是keyup事件)。我遇到的问题(希望你能解决)是每次设置 interHTML 时,插入符号都放在开头,我希望它放在 de 用户输入的最后一个字符之后。关于如何知道插入符号的放置位置以及如何将其放置在所需位置的任何建议? 谢谢。

更新为了更好地理解......一个明确的案例: 正则表达式是 ab 并且在我们拥有的 contenteditable 元素中:

    &lt;span style='background-color: lightgreen'&gt;ab&lt;/span&gt;c&lt;span style='background-color: lightgreen'&gt;ab&lt;/span&gt;

然后我在第一个 a 和第一个 b 之间输入一个 c,所以现在我们有了:

    acbc&lt;span style='background-color: lightgreen'&gt;ab&lt;/span&gt;

此时插入符号已经回到 contenteditable 元素的开头,它应该放在我输入的 c 之后。这就是我想要实现的目标,希望现在更清楚。

更新2

function refreshInnerHtml() {
  document.getElementById('textInput').innerHTML = "<span style='background-color: lightgreen'>ab</span>c<span style='background-color: lightgreen'>ab</span>";
}
      &lt;pre contenteditable onkeyup="refreshInnerHtml()" id="textInput" style="border: 1px solid black;" &gt;&lt;/pre&gt;

【问题讨论】:

标签: javascript html


【解决方案1】:

从这里获得这些功能的帮助 ->

Add element before/after text selection

我创造了一些我认为你会喜欢的东西。 我基本上将一些临时标签放入当前光标所在的 html 中。然后我渲染新的 HTML,然后用带有 data-cpos 属性的 span 替换标签。然后我使用它重新选择光标。

var insertHtmlBeforeSelection, insertHtmlAfterSelection;

(function() {
    function createInserter(isBefore) {
        return function(html) {
            var sel, range, node;
            if (window.getSelection) {
                // IE9 and non-IE
                sel = window.getSelection();
                if (sel.getRangeAt && sel.rangeCount) {
                    range = window.getSelection().getRangeAt(0);
                    
                    range.collapse(isBefore);
        
                    // Range.createContextualFragment() would be useful here but is
                    // non-standard and not supported in all browsers (IE9, for one)
                    var el = document.createElement("div");
                    el.innerHTML = html;
                    var frag = document.createDocumentFragment(), node, lastNode;
                    while ( (node = el.firstChild) ) {
                        lastNode = frag.appendChild(node);
                    }
                    range.insertNode(frag);
                }
            } else if (document.selection && document.selection.createRange) {
                // IE < 9
                range = document.selection.createRange();
                range.collapse(isBefore);
                range.pasteHTML(html);
            }
        }
    }
    
    insertHtmlBeforeSelection = createInserter(true);
    insertHtmlAfterSelection = createInserter(false);
})();




function refreshInnerHtml() {
  var
    tag_start = '⇷',  //lets use some unicode chars unlikely to ever use..
    tag_end = '⇸',
    sel = document.getSelection(),
    input = document.getElementById('textInput');
   
  //remove old data-cpos
  [].forEach.call(
    input.querySelectorAll('[data-cpos]'),
    function(e) { e.remove() });
  
  //insert the tags at current cursor position
  insertHtmlBeforeSelection(tag_start);
  insertHtmlAfterSelection(tag_end);
  
  //now do our replace
  let html = input.innerText.replace(/(ab)/g, '<span style="background-color: lightgreen">$1</span>');
  input.innerHTML = html.replace(tag_start,'<span data-cpos>').replace(tag_end,'</span>');
  
  //now put cursor back 
  var e = input.querySelector('[data-cpos]');
  if (e) {
    var range = document.createRange();
    range.setStart(e, 0);
    range.setEnd(e, 0);
    sel.removeAllRanges();
    sel.addRange(range);  
  }
}

refreshInnerHtml();
Type some text below, with the letters 'ab' somewhere within it. <br>

<pre contenteditable onkeyup="refreshInnerHtml()" id="textInput" style="border: 1px solid black;" >It's about time.. above and beyond</pre>

【讨论】:

    【解决方案2】:

    <html>
    	<head>
    	<script>	
    		function test(inp){
    			document.getElementById(inp).value = document.getElementById(inp).value; 
    		}
    
    	</script>
    	</head>
    	<body> 
    		<input id="search" type="text" value="mycurrtext" size="30" 
           onfocus="test(this.id);" onclick="test(this.id);" name="search"/>
    	</body> 
    </html>

    我很快做了这个,它把光标放在输入框中字符串的末尾。 onclick 用于当用户手动点击输入时,onfocus 用于当用户在输入上进行选项卡时。​​p>

    <input id="search" type="text" value="mycurrtext" size="30" 
       onfocus="test(this.id);" onclick="test(this.id);" name="search"/>
    
    function test(inp){
            document.getElementById(inp).value = document.getElementById(inp).value;  
        }
    

    【讨论】:

    • 他使用的是 contenteditable 而不是 INPUT 元素。所以他可以使用颜色等。
    【解决方案3】:

    为了方便选择,我添加了另一个 span 标记,其中没有任何内容,并为其赋予了 data-end 属性以便于选择。

    然后,我只需从中创建一个范围并使用 window.getSelection addRange 来应用它。

    更新:修改为在第一个 ab 之后放置插入符号

    var e = document.querySelector('[data-end]');
    var range = document.createRange();
    range.setStart(e, 0);
    range.setEnd(e, 0);
    
    document.querySelector('[contenteditable]').focus();
    var sel = window.getSelection();
    sel.removeAllRanges();
    sel.addRange(range);
    <div contenteditable="true">
      <span style='background-color: lightgreen'>ab</span><span data-end></span>c<span style='background-color: lightgreen'>ab</span>
    </div>

    【讨论】:

    • 我更新了帖子以使其更清晰。欢迎任何解决方案甚至想法以另一种方式制作我正在尝试的东西。
    • 不是 100% 确定你在做什么。但是如果你在创建 contenteditable 时将&lt;span data-end&gt;&lt;/span&gt; 放置在你想要的位置,它会将插入符号放在那里。在上面的示例中,我放置了它在最后,但我可以把它放在任何地方。事实上,我会将我的 sn-p 修改为放在 ab 之后
    • "我遇到的问题(希望你能解决)是每次设置interHTML时,插入符号都放在开头,我希望它放在最后一个字符之后由用户输入。”那是我认为你仍然不明白的部分。
    • 在上面的 sn-p 中,如果你运行它,插入符号不在行首,这是我告诉它去的。因此,如果您执行您的 innerHTML,请将&lt;span data-end&gt;&lt;/span&gt; 放在您希望插入符号出现的位置,然后运行我在此处显示的代码。它应该做你想做的,..你有没有试过在这里运行我创建的片段?。
    • 事情是每次按下一个键,应用程序都会为innerHTML计算一个新的值,并且这个值是赋值的,所以插入符号又出现在了开头。在您的解决方案的情况下,我将不得不在哪里放置
    猜你喜欢
    • 2015-01-16
    • 1970-01-01
    • 1970-01-01
    • 2014-07-29
    • 2015-05-31
    • 2011-06-17
    • 1970-01-01
    • 1970-01-01
    • 2014-02-09
    相关资源
    最近更新 更多