我有一个满足的div
<div id="divTest" contenteditable="true">
我需要从插入位置得到最后一个字,在某些情况下我必须测试并删除这个特定字。以下是我的表现
$('#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中的所有内容。 我怎样才能替换特定的单词?
答案 0 :(得分:7)
要使用Ranges
,我们需要记住,我们正在使用节点,而不仅仅是呈现的文本。您想要操纵的结构是:
<div id="divTest" contenteditable="true"> <-- Element Node
"some text" <-- TextNode
</div>
但它也可能是:
<div id="divTest" contenteditable="true"> <-- Element Node
"some text" <-- TextNode
"more text" <-- TextNode
"" <-- TextNode
</div>
要解决您的问题更简单,只能处理一个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时,结构会改变:
<div id="divTest" contenteditable="true"> <-- Element Node
"some text" <-- TextNode
</div>
类似于:
<div id="divTest" contenteditable="true"> <-- Element Node
<div>"some text"</div>
<div>"more text"</div>
</div>
我已经更新了这个答案,但在这个问题上也值得一读Basj's answer:Replace word before cursor, when multiple lines in contenteditable
JSFiddle demo或可运行的代码段:
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;
}
}
<div id="divTest" contenteditable="true">Write words here and hit SPACE BAR</div>
答案 1 :(得分:4)
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 '<strong>' + m + '</strong>';
});
});
//html = html.replace(' ', ' ').replace(/\s+/g, ' ');
console.log('HTML:', html);
console.log('----');
div.html(html);
}
在setinterval
<强> Fiddle 强>
答案 2 :(得分:1)
Tobías的解决方案适用于单行contenteditable
div。但是如果添加多行,它就不再起作用了。
Here是适用于单行或多行可信div的通用解决方案。