This functionality is simple, you can use .split().join()
or regex to replace content with .replace()
.
In its simplest version it would be:
var editavel = document.querySelector('div[contenteditable="true"]');
var novoConteudo = '<span class="format">palavra-chave</span>';
editavel.innerHTML = editavel.innerHTML.split('palavra-chave').join(novoConteudo);
jsFiddle: link
If you want to do this on the fly substitution, with each new letter entered, then it gets more interesting. You can add an event listener to when a key is released ( keyup ) and check the content:
var editavel = document.querySelector('div[contenteditable="true"]');
editavel.addEventListener('keyup', function (e) {
var novoConteudo = '<span class="format">palavra-chave</span>';
this.innerHTML = this.innerHTML.split('palavra-chave').join(novoConteudo);
});
The problem now that needs to be resolved is the mask / cursor, which exits the position when the HTML is replaced. In SOen I found two questions about this where Tim Down responds on how to read and how to sect the cursor / mask. These answers did not solve the problem directly but led me to this solution:
Three steps!
# 1 - Replace all words when code first runs. This way we know that when we type we will only see one new one at a time.
# 2 - Use replace, with function! In this way we can insert a span and to solve the problem of the mask give this span a secret ID.
# 3 - So we can select this element and point the mask to the beginning of the next sibling.
Applied to this case the code would look like this:
var palavraChave = 'palavra-chave';
// fazer substituições antes de começar a escrever
var editavel = document.querySelector('div[contenteditable="true"]');
var novoConteudo = '<span class="format">palavra-chave</span>';
editavel.innerHTML = editavel.innerHTML.split('palavra-chave').join(novoConteudo);
var editavel = document.querySelector('div[contenteditable="true"]');
editavel.addEventListener('keyup', function (e) {
var regExp = new RegExp('(' + palavraChave + ')(?!<)', 'g');
// se não houver nada para substituir, sair
if (!this.innerHTML.match(regExp)) return;
var novoConteudo = this.innerHTML.replace(regExp, function () {
// trocar o match pelo novo conteudo
return '<span id="span_secreto">' + palavraChave + '</span>';
});
this.innerHTML = novoConteudo;
// ir buscar o elemento secreto criado para lhe retirar a ID e colocar a classe
var span = document.getElementById('span_secreto');
span.removeAttribute('id');
span.classList.add('format');
colocarCareto(span);
});
function colocarCareto(node) {
// defenir que elemento o careto deve seguir. Criar um novo placebo se o span fôr o ultimo
var target = node.nextSibling ? node.nextSibling : (function (el) {
var novoTextNode = document.createTextNode(' ');
el.appendChild(novoTextNode);
return novoTextNode
})(node);
// colocar o careto no inicio do proximo elemento
var range = document.createRange();
range.setStart(target, 1);
range.setEnd(target, 1);
var selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
}
.format {
color: red;
font-style: italic;
}
span.format {
color: #aad;
}
<div contenteditable="true">
texto texto texto palavra-chave. texto palavra-chav, texto texto.
</div>
The code still needs more tuning, and maybe afterwards to shorten it. But the main idea and functionality remains.