Crea un editor WYSIWYG con el atributo contentEditable
() translation by (you can also view the original English article)
Los editores WYSIWYG son bastante populares. Es posible que tú también hayas utilizado uno en algún momento. Hay muchas bibliotecas disponibles para ayudarte a configurar tu propio editor. Aunque son rápidas de configurar, también hay desventajas en el uso de estas. Para empezar, son excesivas. La mayoría de ellas tienen funciones extravagantes que tal vez no utilices. Además, personalizar la apariencia de esos editores puede ser un dolor de cabeza.
En este tutorial, construiremos nuestro propio editor WYSIWYG ligero. Al final de este tutorial, tendrás un editor con capacidades básicas de formato que es el estilo de acuerdo a tus preferencias.
Comencemos con la introducción de execCommand
. Utilizaremos este comando para implementar ampliamente nuestro editor.
Document.execCommand()
execCommand
es un método del objeto del documento. Nos permite manipular el contenido de una región editable. Cuando se utiliza junto a contentEditable
, puede ayudarnos a crear un editor de texto enriquecido. Hay muchos comandos disponibles, como añadir un enlace, poner una selección en negrita o cursiva y cambiar el tamaño o el color de la fuente. Este método sigue la sintaxis:
1 |
document.execCommand(CommandName, ShowDefaultUI, ValueArgument); |
CommandName
es una cadena que especifica el nombre del comando a ejecutar. ShowDefaultUI
es un booleano que indica si la interfaz de apoyo debe mostrarse o no. Esta opción no está completamente implementada, y es mejor establecerla en falso. ValueArgument
es una cadena para proporcionar información como la URL de la imagen o el color frontal
. Este argumento se establece en null
cuando un comando no requiere un valor para tener efecto.
Necesitaremos utilizar diferentes versiones de este método para implementar varias características. En los próximos párrafos, repasaré todas y cada una de ellas.
Comandos sin argumento de valor
Comandos como negrita, justificar, deshacer y rehacer no necesitan un ValueArgument
. En estos casos utilizamos la siguiente sintaxis:
1 |
document.execCommand(commandName, false, null); |
CommandName
es simplemente el nombre de los comandos como: justificaciónalcentro
, justificaciónaladerecha
, negrita
, etc.
Comandos con un argumento de valor
Comandos como insertarimagen
, crearlink
y colorfrente
necesitan un argumento para funcionar correctamente. Para estos comandos, se necesita la siguiente sintaxis:
1 |
document.execCommand(commandName, false, value); |
En el caso de insertarmagen
, el valor sería la URL de la imagen a insertar. En el caso de colorfrente
, sería un valor de color como #FF9966
o un nombre como azul
.
Comandos que añaden etiquetas de estilo de bloque
Para añadir etiquetas de estilo de bloque HTML es necesario utilizar formatBlock
como commandName
y el nombre de la etiqueta como valueArgument
. La sintaxis sería similar a:
1 |
document.execCommand('formatBlock', false, tagName); |
Este método añadirá una etiqueta de estilo de bloque HTML alrededor de la línea que contiene la selección actual. También reemplazará cualquier etiqueta que ya existiera allí. tagName
puede ser cualquiera de las etiquetas de encabezado (h1
-h6
), p
o blockquote
.
Aquí he puesto los comandos más comunes. Puedes visitar Mozilla para ver una lista de todos los comandos disponibles.
Crea una barra de herramientas
Con lo básico fuera de nuestro camino, es el momento de crear la barra de herramientas. Voy a utilizar los iconos de Font Awesome para los botones. Habrás notado que, dejando de lado algunas diferencias, todos los execCommands
tienen una estructura similar. Podemos usar esto a nuestro favor utilizando el siguiente marcado para los botones de la barra de herramientas:
1 |
<a href="#" data-command='commandName'><i class='fa fa-icon'></i></a> |
De esta manera, cada vez que los usuarios hagan clic en un botón, podremos saber qué versión de execCommand
utilizar en función del valor del atributo data-command
. Aquí hay unos cuantos botones como referencia:
1 |
<a href="#" data-command='h2'>H2</a> |
2 |
<a href="#" data-command='undo'><i class='fa fa-undo'></i></a> |
3 |
<a href="#" data-command='createlink'><i class='fa fa-link'></i></a> |
4 |
<a href="#" data-command='justifyLeft'><i class='fa fa-align-left'></i></a> |
5 |
<a href="#" data-command='superscript'><i class='fa fa-superscript'></i></a> |
El valor del atributo data-command
del primer botón es h2
. Después de comprobar este valor en JavaScript, utilizaremos la versión formatBlock
del método execCommand
. Del mismo modo, para el último botón, el superíndice
sugiere que debemos utilizar la versión sin valueArgument
de execCommand
.
La creación de los botones foreColor
y backColor
es una historia diferente. Plantean dos problemas. Dependiendo de cuántos colores estemos proporcionando a los usuarios para que elijan, escribir tanto código puede ser tedioso y propenso a errores. Para solucionar este problema podemos utilizar el siguiente código JavaScript:
1 |
var colorPalette = ['000000', 'FF9966', '6699FF', '99FF66','CC0000', '00CC00', '0000CC', '333333', '0066FF', 'FFFFFF']; |
2 |
|
3 |
var forePalette = $('.fore-palette'); |
4 |
|
5 |
for (var i = 0; i < colorPalette.length; i++) { |
6 |
forePalette.append('<a href="#" data-command="forecolor" data-value="' + '#' + colorPalette[i] + '" style="background-color:' + '#' + colorPalette[i] + ';" class="palette-item"></a>'); |
7 |
}
|
Observa que también estoy estableciendo un atributo para el data-value
para cada color. Esto se utilizará más tarde como valueArgument
en el método execCommand
.
El segundo problema es que no podemos mostrar tantos colores todo el tiempo, porque ocuparía mucho espacio y sería una experiencia terrible. Utilizando un poco de CSS, podemos asegurarnos de que la paleta de colores aparezca sólo cuando el usuario pase el mouse por encima de los botones respectivos. También hay que cambiar el marcado de estos botones por el siguiente:
1 |
<div class="fore-wrapper"><i class='fa fa-font'></i> |
2 |
<div class="fore-palette"> |
3 |
</div>
|
4 |
</div>
|
Para mostrar las paletas sólo al pasar por encima, necesitamos el siguiente CSS:
1 |
.fore-palette, |
2 |
.back-palette { |
3 |
display: none; |
4 |
}
|
5 |
|
6 |
.fore-wrapper:hover .fore-palette, |
7 |
.back-wrapper:hover .back-palette { |
8 |
display: block; |
9 |
float: left; |
10 |
position: absolute; |
11 |
}
|
Hay muchas otras reglas CSS en la demo de CodePen para hacer la barra de herramientas más bonita, pero esto es todo lo que se necesitas para la funcionalidad principal.
Añada funcionalidad al editor
Ahora, es el momento de hacer que nuestro editor sea funcional. El código necesario para hacerlo es sorprendentemente pequeño.
1 |
$('.toolbar a').click(function(e) { |
2 |
|
3 |
var command = $(this).data('command'); |
4 |
|
5 |
if (command == 'h1' || command == 'h2' || command == 'p') { |
6 |
document.execCommand('formatBlock', false, command); |
7 |
}
|
8 |
|
9 |
if (command == 'forecolor' || command == 'backcolor') { |
10 |
document.execCommand($(this).data('command'), false, $(this).data('value')); |
11 |
}
|
12 |
|
13 |
if (command == 'createlink' || command == 'insertimage') { |
14 |
url = prompt('Enter the link here: ','http:\/\/'); |
15 |
document.execCommand($(this).data('command'), false, url); |
16 |
}
|
17 |
|
18 |
else document.execCommand($(this).data('command'), false, null); |
19 |
|
20 |
});
|
Comenzamos adjuntando un evento de clic a todos los botones de la barra de herramientas. Cada vez que se hace clic en un botón de la barra de herramientas, almacenamos el valor del atributo data-command
del respectivo botón en la variable, command
. Esto se utiliza posteriormente para llamar a la versión apropiada del método execCommand
. Nos ayuda el escribir un código conciso y evitar las repeticiones.
Al establecer colorfrente
y coloradetrás
, estoy usando el atributo data-value
como tercer argumento. crearlink
e insertarimagen
no tienen un valor url
constante, así que usamos un prompt para obtener los valores del usuario. También puedes realizar comprobaciones adicionales para asegurarte de que la url
es válido. Si la variable comando
no satisface ninguno de los bloques If
, ejecutamos la primera versión de execCommand
.
Este es el aspecto de nuestro editor WYSIWYG.



También puedes implementar la funcionalidad de auto-guardado usando localStorage
de eso hablé en mi último tutorial.
Diferencias entre navegadores
Los distintos navegadores tienen pequeñas diferencias de implementación. Por ejemplo, ten en cuenta que al utilizar formatBlock
, Internet Explorer sólo admite las etiquetas de encabezamiento h1 - h6
, dirección
y pre
. También es necesario incluir los delimitadores de la etiqueta al especificar el comandoName
como h3
.
No todos los comandos son compatibles con todos los navegadores. Internet Explorer no soporta comandos como insertHTML
y hiliteColor
. Del mismo modo, insertBrOnReturn
sólo es compatible con Firefox. Puedes leer más sobre las inconsistencias de los navegadores en esta página de GitHub.
Reflexiones finales
Crear tu propio editor WYSIWYG puede ser una gran experiencia de aprendizaje. En este tutorial he cubierto un montón de comandos y he utilizado algo de CSS para el estilo básico. Como ejercicio, te sugiero que intentes implementar un botón de la barra de herramientas para establecer la fuente
de una selección de texto. La implementación será similar a la del botón colorfrente
.
Espero que te haya gustado este tutorial y hayas aprendido algo nuevo. Si has creado tu propio editor WYSIWYG desde cero, no dudes en poner el link en la sección de comentarios.