I have a contenteditable div
<div id="divTest" contenteditable="true">
I need to get the last word from caret position and on certain condition I have to test and remove this specific word only. Below is how am I doing
$('#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;
}
}
Demo: http://codepen.io/anon/pen/ogzpXV
I have tried range.deleteContents(); but that will delete all the content in the div. How can I replace specific word only?
Call this function on
setinterval
Fiddle
Tobías' solution works well for single-line
contenteditable
div. But if you add multiple lines, it doesn't work anymore.Here is a general solution that works for both single-line or multiline contenteditable div.
To work with
Ranges
we need to keep in mind that we are working with Nodes, not only the text that is rendered. The structure you want to manipulate is:But it also could be:
To solve your problem is simplier to handle only one
TextNode
, I propose to use thenormalize()
function to join all of them into a single one.Then you only need to set the
Range
to the word's bounds beforedeleteContents()
. Once deleted, you can insert a newTextNode
with the substitution usinginsertNode()
.For this to work, you need that the text is in a single
TextNode
. But afterìnsertNode
thediv
will contain multiple text nodes. To fix this simply callnormalize()
to join allTextNode
elements.Edit:
As Basj points out, the original solution fails for multiline. That's because when hitting ENTER the structure changes from:
to something like:
I've updated this answer, but it's also worth to read Basj's answer at this question: Replace word before cursor, when multiple lines in contenteditable
JSFiddle demo or runnable code snippet: