在contenteditable中替换特定单词

我有一个满足的div

我需要从插入位置得到最后一个字,在某些情况下我必须测试并删除这个特定的字。 以下是我的表现

 $('#divTest').on('keyup focus', function (e) { if (e.keyCode == 32) { var lastWord = getWordPrecedingCaret(this), spanLastWord = $('#lastWord'); } }); function getWordPrecedingCaret(containerEl) { var preceding = "", sel, range, precedingRange; if (window.getSelection) { sel = window.getSelection(); if (sel.rangeCount > 0) { range = sel.getRangeAt(0).cloneRange(); range.collapse(true); range.setStart(containerEl, 0); preceding = range.toString(); } } else if ((sel = document.selection) && sel.type != "Control") { range = sel.createRange(); precedingRange = range.duplicate(); precedingRange.moveToElementText(containerEl); precedingRange.setEndPoint("EndToStart", range); preceding = precedingRange.text; } var words = range.toString().trim().split(' '), lastWord = words[words.length - 1]; if (lastWord) { var resultValue = 'some'; // this value is coming from some other function if (resultValue == lastWord) { alert('do nothing'); // do nothing } else { alert('replace word'); // delete That specific word and replace if with resultValue } return lastWord; } } 

演示: http : //codepen.io/anon/pen/ogzpXV

我尝试过range.deleteContents(); 但这将删除div中的所有内容。 我怎样才能替换特定单词?

要使用Ranges我们需要记住,我们正在使用节点,而不仅仅是渲染的文本。 您想要操纵的结构是:

 
<-- Element Node "some text" <-- TextNode

但它也可能是:

 
<-- Element Node "some text" <-- TextNode "more text" <-- TextNode "" <-- TextNode

为了解决你的问题,只处理一个TextNode更简单,我建议使用normalize()函数将它们全部连接成一个。

然后你只需要在deleteContents()之前将Range设置为单词的边界。 删除后,您可以使用insertNode()插入带有替换的新TextNode

 var wordStart = range.toString().lastIndexOf(lastWord); var wordEnd = wordStart + lastWord.length; /* containerEl.firstChild refers to the div's TextNode */ range.setStart(containerEl.firstChild, wordStart); range.setEnd(containerEl.firstChild, wordEnd); range.deleteContents(); range.insertNode(document.createTextNode(resultValue)); 

为此,您需要将文本放在单个TextNode 。 但是在“ ìnsertNode节点”之后, div将包含多个文本节点。 要解决这个问题,只需调用normalize()即可加入所有TextNode元素。

 containerEl.normalize(); 

编辑:

正如Basj指出的那样,原始解决方案失败了多线。 这是因为当按下ENTER时,结构会改变:

 
<-- Element Node "some text" <-- TextNode

类似于:

 
<-- Element Node
"some text"
"more text"

我已经更新了这个答案,但是也值得在这个问题上阅读Basj的答案 : 在光标前替换单词,当多条线在contenteditable时

JSFiddle演示或可运行的代码片段:

 document.getElementById('divTest').onkeyup = function (e) { if (e.keyCode == 32) { getWordPrecedingCaret(this); } }; function getWordPrecedingCaret(containerEl) { var preceding = "", sel, range, precedingRange; if (window.getSelection) { sel = window.getSelection(); if (sel.rangeCount > 0) { range = sel.getRangeAt(0).cloneRange(); range.collapse(true); range.setStart(containerEl, 0); preceding = range.toString(); } } else if ((sel = document.selection) && sel.type != "Control") { range = sel.createRange(); precedingRange = range.duplicate(); precedingRange.moveToElementText(containerEl); precedingRange.setEndPoint("EndToStart", range); preceding = precedingRange.text; } var words = range.toString().trim().split(' '), lastWord = words[words.length - 1]; if (lastWord) { var resultValue = 'some'; // this value is coming from some other function if (resultValue == lastWord) { console.log('do nothing: ' + lastWord); // do nothing } else { console.log('replace word ' + lastWord); /* Find word start and end */ var wordStart = range.endContainer.data.lastIndexOf(lastWord); var wordEnd = wordStart + lastWord.length; console.log("pos: (" + wordStart + ", " + wordEnd + ")"); range.setStart(range.endContainer, wordStart); range.setEnd(range.endContainer, wordEnd); range.deleteContents(); range.insertNode(document.createTextNode(resultValue)); // delete That specific word and replace if with resultValue /* Merge multiple text nodes */ containerEl.normalize(); } return lastWord; } } 
 
Write words here and hit SPACE BAR
  words = ['oele', 'geel', 'politie', 'foo bar']; function markWords() { var html = div.html().replace(/<\/?strong>/gi, ''), text = html.replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' '), exp; $.each(words, function(i, word) { exp = new RegExp('\\b(' + word + ')\\b', 'gi'); html = html.replace(exp, function(m) { console.log('WORD MATCH:', m); return '' + m + ''; }); }); //html = html.replace(' ', ' ').replace(/\s+/g, ' '); console.log('HTML:', html); console.log('----'); div.html(html); } 

setinterval上调用此函数

小提琴

Tobías的解决方案适用于单行contenteditable div。 但是如果添加多行,它就不再起作用了。

这是一个适用于单行或多行可信任div的通用解决方案。