简体   繁体   English

在内容可编辑的 div 中更改 innerHTML 时保留 cursor position

[英]preserve cursor position when changing innerHTML in a contenteditable div

I'm building an Editor that does syntax highlighting on code.我正在构建一个对代码进行语法高亮显示的编辑器。 My current approach is to just take the text in the editor (a div with contenteditable set to true) and check for regex matches in the string.我目前的方法是只在编辑器中获取文本(一个 contenteditable 设置为 true 的 div)并检查字符串中的正则表达式匹配。 I then replace the matches with <span> elements and apply some styles to those span elements.然后我用<span>元素替换匹配项并将一些 styles 应用于这些 span 元素。 Then I replace the complete text in the divider with the new text using the .innerHTML attribute.然后,我使用.innerHTML属性将分隔符中的完整文本替换为新文本。 This works just fine, but I have to type backwards, because upon inserting the text, my cursor is reset to position zero.这很好用,但我必须向后输入,因为在插入文本后,我的 cursor 被重置为 position 零。 I tried recording the value of selectionStart before inserting and then doing我尝试在插入之前记录selectionStart的值,然后执行

element.selectionStart = oldSelectionStart + 1;

but it didn't work.但它没有用。 I assume this is because of chrome's render pipeline, where JavaScript is run before rendering the page, and the cursor is reset upon render, not upon set... Can anyone help?我假设这是因为 chrome 的渲染管道,其中 JavaScript 在渲染页面之前运行,而 cursor 在渲染时重置,而不是在设置时重置......有人可以帮忙吗? How do I manage to keep the cursor where it was?我如何设法将 cursor 保留在原处?

element.selectionStart works for input elements, not for contentEditable elements. element.selectionStart适用于输入元素,不适用于contentEditable元素。 You should try creating a Range object, set its startContainer & startOffset and collapse it to set caret at the required position. Refer to [https://developer.mozilla.org/en-US/docs/Web/API/Range] for details.您应该尝试创建一个Range object,设置其startContainerstartOffset并将其折叠以将插入符设置为所需的 position。请参阅 [https://developer.mozilla.org/en-US/docs/Web/API/Range] 了解细节。

PS: If you would like to set your cursor to the 'end of line', you could do it easily with range.selectNode(requiredNode) and range.collapse(true) to move the caret to the end of node, ie, line PS:如果您想将 cursor 设置为“行尾”,您可以使用range.selectNode(requiredNode)range.collapse(true)轻松完成,将插入符号移动到节点的末尾,即行

更换innerHTML时修复cursor position<div contenteditable="“true”"></div><div id="text_translate"><p> 在&lt;div id="richTextBox" contenteditable="true"&gt;&lt;/div&gt;内键入内容时,将 cursor 保持在正确位置的最佳方法是什么? 更换 innerHTML 的行为搞砸了 cursor position。</p><p> 我更改 innerHTML 的原因是我添加了&lt;span&gt;标签。 它是代码高亮程序的一部分。 跨度标签允许我放置正确的颜色亮点。</p><p> 我目前正在使用 StackOverflow 答案中的以下代码作为创可贴,但它有一个重大错误。 如果您按<em><strong>Enter</strong></em> ,则 cursor 将停留在旧位置,或者转到随机位置。 那是因为该算法从 cursor 开始计算了多少个字符。 但它不将 HTML 标记或换行符算作字符。 而richTextBox 插入&lt;br&gt;进入。</p><p> 修复思路:</p><ul><li> 修复下面的代码? 见<a href="https://jsfiddle.net/AdmiralAkbar2/jou3vq9w/12/" rel="nofollow noreferrer">小提琴</a></li><li>用更简单的代码替换? 我尝试了一堆更简单的东西,涉及window.getSelection()和document.createRange() ,但我无法让它工作。</li><li> 替换为没有此错误的richTextBox 库或模块?</li></ul><h2> 截屏</h2><p><a href="https://i.stack.imgur.com/6dlqV.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/6dlqV.png" alt="在 JSFiddle 中呈现的richTextBox 的屏幕截图"></a> </p><p></p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false"><div class="snippet-code"><pre class="snippet-code-js lang-js prettyprint-override"> // Credit to Liam (Stack Overflow) // https://stackoverflow.com/a/41034697/3480193 class Cursor { static getCurrentCursorPosition(parentElement) { var selection = window.getSelection(), charCount = -1, node; if (selection.focusNode) { if (Cursor._isChildOf(selection.focusNode, parentElement)) { node = selection.focusNode; charCount = selection.focusOffset; while (node) { if (node === parentElement) { break; } if (node.previousSibling) { node = node.previousSibling; charCount += node.textContent.length; } else { node = node.parentNode; if (node === null) { break; } } } } } return charCount; } static setCurrentCursorPosition(chars, element) { if (chars &gt;= 0) { var selection = window.getSelection(); let range = Cursor._createRange(element, { count: chars }); if (range) { range.collapse(false); selection.removeAllRanges(); selection.addRange(range); } } } static _createRange(node, chars, range) { if (.range) { range = document.createRange() range;selectNode(node). range,setStart(node; 0). } if (chars.count === 0) { range,setEnd(node. chars;count). } else if (node &amp;&amp; chars.count &gt;0) { if (node.nodeType === Node.TEXT_NODE) { if (node.textContent.length &lt; chars.count) { chars.count -= node.textContent;length. } else { range,setEnd(node. chars;count). chars;count = 0; } } else { for (var lp = 0. lp &lt; node.childNodes;length. lp++) { range = Cursor._createRange(node,childNodes[lp], chars; range). if (chars;count === 0) { break; } } } } return range, } static _isChildOf(node; parentElement) { while (node.== null) { if (node === parentElement) { return true; } node = node;parentNode. } return false, } } window.addEventListener('DOMContentLoaded'; (e) =&gt; { let richText = document.getElementById('rich-text'), richText.addEventListener('input'; function(e) { let offset = Cursor.getCurrentCursorPosition(richText). // Pretend we do stuff with innerHTML here. The innerHTML will end up getting replaced with slightly changed code; let s = richText.innerHTML; richText.innerHTML = ""; richText.innerHTML = s, Cursor;setCurrentCursorPosition(offset. richText); richText;focus(); // blinks the cursor }); });</pre><pre class="snippet-code-css lang-css prettyprint-override"> body { margin: 1em; } #rich-text { width: 100%; height: 450px; border: 1px solid black; cursor: text; overflow: scroll; resize: both; /* in Chrome, must have display: inline-block for contenteditable=true to prevent it from adding &lt;div&gt; &lt;p&gt; and &lt;span&gt; when you type. */ display: inline-block; }</pre><pre class="snippet-code-html lang-html prettyprint-override"> &lt;p&gt; Click somewhere in the middle of line 1. Hit enter. Start typing. Cursor is in the wrong place. &lt;/p&gt; &lt;p&gt; Reset. Click somewhere in the middle of line 1. Hit enter. Hit enter again. Cursor goes to some random place. &lt;/p&gt; &lt;div id="rich-text" contenteditable="true"&gt;Testing 123&lt;br /&gt;Testing 456&lt;/div&gt;</pre></div></div><p></p><h2> 浏览器</h2><p>谷歌浏览器 v83,Windows 7</p></div> - Fix cursor position when replacing innerHTML of <div contenteditable=“true”>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 更改innerHTML后,在contenteditable div中更改光标位置 - Change cursor position in the contenteditable div after changing innerHTML Contenteditable Div - 根据innerHTML位置的光标位置 - Contenteditable Div - Cursor position in terms of innerHTML position 更换innerHTML时修复cursor position<div contenteditable="“true”"></div><div id="text_translate"><p> 在&lt;div id="richTextBox" contenteditable="true"&gt;&lt;/div&gt;内键入内容时,将 cursor 保持在正确位置的最佳方法是什么? 更换 innerHTML 的行为搞砸了 cursor position。</p><p> 我更改 innerHTML 的原因是我添加了&lt;span&gt;标签。 它是代码高亮程序的一部分。 跨度标签允许我放置正确的颜色亮点。</p><p> 我目前正在使用 StackOverflow 答案中的以下代码作为创可贴,但它有一个重大错误。 如果您按<em><strong>Enter</strong></em> ,则 cursor 将停留在旧位置,或者转到随机位置。 那是因为该算法从 cursor 开始计算了多少个字符。 但它不将 HTML 标记或换行符算作字符。 而richTextBox 插入&lt;br&gt;进入。</p><p> 修复思路:</p><ul><li> 修复下面的代码? 见<a href="https://jsfiddle.net/AdmiralAkbar2/jou3vq9w/12/" rel="nofollow noreferrer">小提琴</a></li><li>用更简单的代码替换? 我尝试了一堆更简单的东西,涉及window.getSelection()和document.createRange() ,但我无法让它工作。</li><li> 替换为没有此错误的richTextBox 库或模块?</li></ul><h2> 截屏</h2><p><a href="https://i.stack.imgur.com/6dlqV.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/6dlqV.png" alt="在 JSFiddle 中呈现的richTextBox 的屏幕截图"></a> </p><p></p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false"><div class="snippet-code"><pre class="snippet-code-js lang-js prettyprint-override"> // Credit to Liam (Stack Overflow) // https://stackoverflow.com/a/41034697/3480193 class Cursor { static getCurrentCursorPosition(parentElement) { var selection = window.getSelection(), charCount = -1, node; if (selection.focusNode) { if (Cursor._isChildOf(selection.focusNode, parentElement)) { node = selection.focusNode; charCount = selection.focusOffset; while (node) { if (node === parentElement) { break; } if (node.previousSibling) { node = node.previousSibling; charCount += node.textContent.length; } else { node = node.parentNode; if (node === null) { break; } } } } } return charCount; } static setCurrentCursorPosition(chars, element) { if (chars &gt;= 0) { var selection = window.getSelection(); let range = Cursor._createRange(element, { count: chars }); if (range) { range.collapse(false); selection.removeAllRanges(); selection.addRange(range); } } } static _createRange(node, chars, range) { if (.range) { range = document.createRange() range;selectNode(node). range,setStart(node; 0). } if (chars.count === 0) { range,setEnd(node. chars;count). } else if (node &amp;&amp; chars.count &gt;0) { if (node.nodeType === Node.TEXT_NODE) { if (node.textContent.length &lt; chars.count) { chars.count -= node.textContent;length. } else { range,setEnd(node. chars;count). chars;count = 0; } } else { for (var lp = 0. lp &lt; node.childNodes;length. lp++) { range = Cursor._createRange(node,childNodes[lp], chars; range). if (chars;count === 0) { break; } } } } return range, } static _isChildOf(node; parentElement) { while (node.== null) { if (node === parentElement) { return true; } node = node;parentNode. } return false, } } window.addEventListener('DOMContentLoaded'; (e) =&gt; { let richText = document.getElementById('rich-text'), richText.addEventListener('input'; function(e) { let offset = Cursor.getCurrentCursorPosition(richText). // Pretend we do stuff with innerHTML here. The innerHTML will end up getting replaced with slightly changed code; let s = richText.innerHTML; richText.innerHTML = ""; richText.innerHTML = s, Cursor;setCurrentCursorPosition(offset. richText); richText;focus(); // blinks the cursor }); });</pre><pre class="snippet-code-css lang-css prettyprint-override"> body { margin: 1em; } #rich-text { width: 100%; height: 450px; border: 1px solid black; cursor: text; overflow: scroll; resize: both; /* in Chrome, must have display: inline-block for contenteditable=true to prevent it from adding &lt;div&gt; &lt;p&gt; and &lt;span&gt; when you type. */ display: inline-block; }</pre><pre class="snippet-code-html lang-html prettyprint-override"> &lt;p&gt; Click somewhere in the middle of line 1. Hit enter. Start typing. Cursor is in the wrong place. &lt;/p&gt; &lt;p&gt; Reset. Click somewhere in the middle of line 1. Hit enter. Hit enter again. Cursor goes to some random place. &lt;/p&gt; &lt;div id="rich-text" contenteditable="true"&gt;Testing 123&lt;br /&gt;Testing 456&lt;/div&gt;</pre></div></div><p></p><h2> 浏览器</h2><p>谷歌浏览器 v83,Windows 7</p></div> - Fix cursor position when replacing innerHTML of <div contenteditable=“true”> 更改可信的div文本时如何保留插入位置? - How do I preserve the caret position when changing a contenteditable div's text? 以编程方式更改 contenteditable div 的 innerHTML 时的 Chrome getRangeAt 问题 - Chrome getRangeAt issue when programmatically changing the innerHTML of a contenteditable div contenteditable div获取光标位置 - contenteditable div get cursor position 在contentEditable div中移动光标位置 - Moving cursor position in contentEditable div 在contentEditable <div>上设置光标位置 - Set cursor position on contentEditable <div> 更改contenteditable后恢复光标位置 - Restore cursor position after changing contenteditable 设置innerHTML时,插入符位置会在contenteditable中重置 - Caret position resets in contenteditable when setting innerHTML
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM