我一直在搜索很多范围和选择相关的问题(大部分是由@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>
&#13;
注意:我只关心与Chrome的兼容性。
答案 0 :(得分:1)
解释代码注释和控制台日志。
请参阅selection
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>
答案 1 :(得分:0)
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>
答案 2 :(得分:0)
使用createNodeIterator
用NodeFilter.SHOW_TEXT
迭代所有textNode,很容易找出所有出现的字符串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>