Add tag in all repetitions of a given word

3

See the code below:

<style type="text/css">
    .format{
        color: red;
        font-style: italic;
    }
</style>

<div>
     <p>texto texto texto 'palavra-chave'. texto 'palavra-chave', texto texto.<p>
</div>

Within div , ignoring tags, I want whenever a 'keyword' is found to put it within <span class="format">

What would be the best solution for this using Pure JavaScript ?

    
asked by anonymous 23.12.2014 / 19:34

1 answer

2

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.

    
24.12.2014 / 20:21