Idioma
Categoria
Pesquisar

TinyMCE: envolva o texto selecionado no editor com um elemento span

Crie um botão para envolver o texto selecionado no editor em um elemento span contendo uma classe específica com as estilizações desejadas

Em JavaScript Por Rudi Drusian Lange
Publicado em
Última atualização

A ideia é configurar um botão para alternar entre adicionar e remover um span com um estilo css ao texto selecionado.

Este artigo utilizou a última versão da série 5, TinyMCE 5.10.9 e também a última versão disponível nesta data da série 6 , TinyMCE 6.8.2.

Demo e Download

O conteúdo abordado neste artigo pode ser testado online em nosso demo e também pode ser baixado para uso local.

Demo Download

Configuração básica

Crie o <textarea> e carregue o TinyMCE:

HTML

<textarea id='default'></textarea>
<script src='tinymce.5.10.9/js/tinymce/tinymce.min.js'></script>

Altere o caminho para o arquivo tinymce.min.js conforme a sua necessidade.

Inicializando

Iniciaremos com um editor básico usando o elemento textarea com o id='default' criado acima. A altura será definida como 500px e o plugin code será carregado para disponibilizar a visualização do código fonte em HTML. A barra de ferramentas será customizada contendo os botões negrito, itálico e code.

JavaScript

tinymce.init({
  selector: 'textarea#default',
  height: 500,
  plugins: 'code',
  toolbar: 'bold italic code',
  content_css: 'css/style.css?1',
  formats: {
     bold: {
        inline: 'span',
        attributes: { class: 'text-bold' },
	   },
	},
});

A opção content_css define o caminho para o arquivo css contendo as regras de estilo que serão utilizadas dentro do editor, para por exemplo personalizar fontes, parágrafos e cabeçalhos. Os caracteres ?1 em frente ao nome do arquivo css são usados para limpar o cache depois de alterações, mudar o número em frente do ponto de interrogação força o recarregamento do arquivo, isto ocorre pois entende-se que o nome é diferente porém o arquivo sendo carregado é o mesmo.

Por fim, alteramos o comportamento padrão do negrito que é envolver o texto selecionado em um elemento <b>. Com a configuração acima, ao aplicar o negrito o texto selecionado será envolvido por um elemento <span class='text-bold'>. Neste caso é necessário fazer a estilização no arquivo css para que o negrito seja aplicado.

CSS

.text-bold {
   font-weight: bold;
}

Teste o botão negrito inspecionando o código html do editor usando o botão < >.

Configurando os botões

Começando pelo css, prepare as classes que serão usadas no editor.

CSS

.text-bold { font-weight: bold; }
.text-red { color: red; }
.text-blue { color: blue; }

Lembre-se de mudar o número no final do style.css?1 se fizer alguma alteração no css depois de ter carregado o editor. O TinyMCE guarda cache do arquivo e somente simulando a mudança no nome as alterações surtirão efeito.

Abaixo o código completo com dois botões adicionais, Red e Blue, que envolvem o texto selecionado em um span contendo suas respectivas classes css.

JavaScript

// Função para converter caracteres html em suas entidades
function encodeHTML(s) {
   return s.replace(/&/g, '&amp;')
           .replace(/</g, '&lt;')
           .replace(/>/g, '&gt;')
           .replace(/"/g, '&quot;');
} 

tinymce.init({
  selector: 'textarea#default',
  height: 500,
  plugins: 'code',
  // Adiciona os botões Red e Blue na barra de ferramentas 
  toolbar: 'bold italic code | textred textblue',
  content_css: 'css/style.css?1',
  formats: {
    bold: {
       inline: 'span',
       attributes: { class: 'text-bold' },
		},
    // Cria o formato para os botões
   'textred': {   inline: 'span',   classes: 'text-red',   wrapper: true   },   'textblue': {   inline: 'span',   classes: 'text-blue',     wrapper: true   }, }, setup: function(editor) {   // Função para envolver o texto selecionado com o span // Recebe como parâmetro a classe a ser utilizada no span
  function wrapText(cls) {   const node = editor.selection.getNode();     const selectedText = encodeHTML(editor.selection.getContent({ format: 'text' }));   if (node.nodeName == 'SPAN') {     const l = node.classList.length;     if (l == 0) { node.classList.add(cls); } else if (l == 1) {     if (node.classList.contains(cls)) { editor.execCommand('mceReplaceContent', false, selectedText); } else { node.classList.add(cls); } } else { node.classList.remove(cls); } } else { editor.execCommand( 'mceReplaceContent', false, '<span class="' + cls + '">' + selectedText + '</span>'
);         } }   // Configura os botões  editor.ui.registry.addButton('textred', {   text: 'Red',     tooltip: 'Span Vermelho',     onAction: function() {     wrapText('text-red'); } }); editor.ui.registry.addButton('textblue', {   text: 'Blue',   onAction: function() {     wrapText('text-blue'); } }); } });

Neste exemplo são configurados dois botões adicionais para estilizar o texto no editor usando um elemento span contendo uma classe css. Selecione o texto e clique nos novos botões para adicionar e remover a customização.

Para remover a customização a seleção deve conter exatamente o mesmo texto da seleção original, incluindo espaços em branco. Para alternar a customização é necessário mudar o cursor de posição no editor antes de selecionar novamente o texto, veja mais informações na seção bugs adiante.

Considerações finais

A função wrapText() envolve o texto selecionado em um span se já não estiver em um. Caso o texto já esteja envolvido por um span e este não possuir a classe vinculada ao botão, a classe será adicionada ao span ao em vez de adicionar um novo span.

Ao clicar no botão, se o span envolvendo o texto possuir mais de uma classe, uma delas sendo a classe vinculada ao botão, esta será removida e o span preservado com o restante de suas classes.

O span não será adicionado se o cursor estiver posicionado na palavra e o texto não estiver selecionado.

Este artigo serve de inspiração para outras situações, você pode envolver o texto em uma div, criar botões para adicionar templates, use a criatividade.

Este é somente um exemplo com uma funcionalidade simples, um ponto de partida para personalizações mais complexas. Assim sendo, a função wrapText() não possui toda a lógica necessária para não apresentar falhas. Em algumas situações os botões não funcionarão como o esperado.

Bugs

Um bug que parece estar relacionado ao editor ocorre da seguinte maneira: Ao selecionar um texto e clicar no botão negrito o padrão é aplicado, se clicar imediatamente após no botão Red o texto ficará vermelho e perderá o negrito. Para funcionar como deveria, após clicar no botão negrito, mude o cursor de posição, refaça a seleção do texto e somente depois clique no botão Red, assim o texto ficará em vermelho negrito.

Esse mesmo erro ocorre com o uso somente dos novos botões, se algo não funcionar tente mudar o cursor de posição e selecionar o texto novamente. Se for só uma palavra o clique duplo funciona. Mover o cursor do teclado de um lado e para outro e selecionar novamente também funciona.

Referências

O conteúdo deste artigo foi escrito através de consultas a documentação no site do TinyMCE, a tópicos no site stackoverflow, e a perguntas feitas ao ChatGPT.