在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的通用解决方案。