簡體   English   中英

替換 contenteditable div 中的單詞並正確設置插入符號位置

[英]Replace word in contenteditable div and properly set caret position

我一直在搜索許多與范圍和選擇相關的問題(主要由@tim-down回答),但我無法完全得到我需要的東西,盡管我很接近。

我想在當前聚焦的文本節點中搜索單詞foo 如果我找到它 - 用bar替換它並在替換的單詞末尾設置插入符號位置。 例如:

"Lorem ipsum dolor foo amet, consectetur adipiscing elit."

變成:

"Lorem ipsum dolor bar amet, consectetur adipiscing elit."
// -------------------^--- caret position

我的嘗試

我目前所擁有的只是中途 - 它刪除了文本,但沒有添加任何內容。 不過,我不確定這是最好的方法:

 function replacer(search, replace) { var sel = window.getSelection(); if (!sel.focusNode) { return; } var startIndex = sel.focusNode.nodeValue.indexOf(search); var endIndex = startIndex + search.length; if (startIndex === -1) { return; } var range = document.createRange(); range.setStart(sel.focusNode, startIndex); range.setEnd(sel.focusNode, endIndex); range.insertNode(document.createTextNode("bar")); sel.removeAllRanges(); sel.addRange(range); } document.addEventListener("keypress", function() { replacer("foo", "bar"); });
 <div contenteditable="true" style="width: 600px; height: 300px;">Lorem ipsum dolor foo amet, consectetur adipiscing elit.</div>

注意:我只關心與 Chrome 的兼容性。

在代碼注釋和控制台日志中解釋。
查看關於選擇

 function replacer(search, replace) { var sel = window.getSelection(); if (!sel.focusNode) { return; } var startIndex = sel.focusNode.nodeValue.indexOf(search); var endIndex = startIndex + search.length; if (startIndex === -1) { return; } console.log("first focus node: ", sel.focusNode.nodeValue); var range = document.createRange(); //Set the range to contain search text range.setStart(sel.focusNode, startIndex); range.setEnd(sel.focusNode, endIndex); //Delete search text range.deleteContents(); console.log("focus node after delete: ", sel.focusNode.nodeValue); //Insert replace text range.insertNode(document.createTextNode(replace)); console.log("focus node after insert: ", sel.focusNode.nodeValue); //Move the caret to end of replace text sel.collapse(sel.focusNode, 0); } document.addEventListener("keypress", function() { replacer("foo", "bar"); });
 <div contenteditable="true" style="width: 600px; height: 300px;" id='content'>Lorem ipsum dolor foo amet, consectetur adipiscing elit.</div>

sidoshi,這修復了替換后的插入符號位置

 function replacer(search, replace) { var sel = window.getSelection(); if (!sel.focusNode) { return; } var startIndex = sel.focusNode.nodeValue.indexOf(search); var endIndex = startIndex + search.length; if (startIndex === -1) { return; } console.log("first focus node: ", sel.focusNode.nodeValue); var range = document.createRange(); //Set the range to contain search text range.setStart(sel.focusNode, startIndex); range.setEnd(sel.focusNode, endIndex); //Delete search text range.deleteContents(); console.log("focus node after delete: ", sel.focusNode.nodeValue); //Insert replace text range.insertNode(document.createTextNode(replace)); console.log("focus node after insert: ", sel.focusNode.nodeValue); //Move the caret to end of replace text var replacement = document.createTextNode(replace); range.insertNode(replacement); range.setStartAfter(replacement); // Chrome fix sel.removeAllRanges(); sel.addRange(range); } document.addEventListener("keypress", function() { replacer("foo", "bar"); });
 <div contenteditable="true" style="width: 600px; height: 300px;" id='content'>Lorem ipsum dolor foo amet, consectetur adipiscing elit.</div>

這並不是所要求的,而是循環多個替換並像上面一樣處理 DIV 和 textareas。

 var replaceValues = { 'foo' : 'bar', 'aaa': 'bbbb' } $('.macroenabled').keyup(function(e) { // change to keypress if you want it to happen after space for (var search in replaceValues) { if ($(document.activeElement).is(":input")) { var newText = $(e.target).val(); var temp = new RegExp(search, 'gim'); newText = newText.replace(temp, replaceValues[search]); $(e.target).val(newText); } else { // element contenteditable DIV var sel = window.getSelection(); if (!sel.focusNode || ! sel.focusNode.nodeValue) { // Nothing focused continue; } var startIndex = sel.focusNode.nodeValue.indexOf(search); var endIndex = startIndex + search.length; if (startIndex === -1) { // search not found continue; } var range = document.createRange(); range.setStart(sel.focusNode, startIndex); range.setEnd(sel.focusNode, endIndex); range.deleteContents(); //Delete search text var replacement = document.createTextNode(replaceValues[search]); range.insertNode(replacement); range.setStartAfter(replacement); sel.removeAllRanges(); // Chrome fix sel.addRange(range); } } });
 #div1 { border: 1px solid black; }
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div>I am a textarea, try 'aaa' or 'foo'</div> <textarea class="macroenabled"></textarea> <div>I am a content editable DIV, try 'aaa' or 'foo'</div> <div id="div1" contenteditable="true" class="macroenabled"></div>

使用createNodeIterator使用NodeFilter.SHOW_TEXT迭代所有 textNodes,很容易找到所有出現的字符串foo並替換為任何所需的東西,在這種情況下, bar textNode。

第二階段應該與查找和替換函數分離,是插入符號放置請求,由函數setRangeAtEnd實現,該函數接收單個參數node ,該參數可從第一個函數的輸出中獲得, replaceTextWithNode ,它返回在contentEditable 中替換的所有節點的數組。

 const elm = document.querySelector('[contenteditable]'); function replaceTextWithNode( elm, text, replacerNode ){ var iter = document.createNodeIterator(elm, NodeFilter.SHOW_TEXT), textnode, replacedNode, idx, newNode, maxIterations = 100, addedNodes = []; while( textnode = iter.nextNode() ){ if( !maxIterations-- ) break; // get the index of which the text is within the textNode (if at all) idx = textnode.nodeValue.indexOf(text) if( idx == -1 ) continue replacedNode = textnode.splitText(idx) newNode = replacerNode.cloneNode() // clean up the tag's string and put tag element instead replacedNode.nodeValue = replacedNode.nodeValue.replace(text, '') textnode.parentNode.insertBefore(newNode, replacedNode) addedNodes.push(newNode) } return addedNodes } function setRangeAtEnd( node ){ node = node.firstChild || node; const sel = document.getSelection() if( sel.rangeCount ) ['Start', 'End'].forEach(pos => sel.getRangeAt(0)["set" + pos](node, node.length) ) } // Replace all 'foo' with 'bar' textNodes var addedNodes = replaceTextWithNode(elm, 'foo', document.createTextNode('bar')) // Place caret at last occurrence of 'bar' elm.focus() setRangeAtEnd(addedNodes[addedNodes.length-1])
 <div contenteditable>Lorem ipsum foo dolor foo amet foo, consectetur adipiscing elit.</div>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM