Barra de herramientas, vista previa en vivo y WYSIWYG en el área de texto que ya tiene
Última actualización:
Un editor de rebajas que mejora su área de texto en lugar de reemplazarla, por lo que el envío de formularios, la validación y los campos obligatorios siguen funcionando. Modos WYSIWYG y Simple Markdown, vista previa en vivo, buscar y reemplazar, soporte RTL, modo oscuro. Funciona de forma independiente con Django, Laravel, Rails, Node.js, PHP y cualquier pila.
No se requiere Frutjam ni viento de cola. Los estilos están agrupados, por lo que el editor se inserta en cualquier proyecto.
Instalación
NPM (bundlers: Vite, webpack, Rollup, etc.)
npm install markdown-text-editor |
1 2 | import MarkdownEditor from 'markdown-text-editor'; new MarkdownEditor('#markdown-editor'); |
Las definiciones de TypeScript se envían con el paquete, por lo que las opciones, las entradas de la barra de herramientas y las formas variables se comprueban y se completan automáticamente sin instalación adicional.
CDN: módulo ES
1 2 3 4 | <script type="module"> import MarkdownEditor from 'https://cdn.jsdelivr.net/npm/markdown-text-editor/+esm'; new MarkdownEditor('#markdown-editor'); </script> |
CDN: etiqueta de script global (IIFE)
No se necesita importar: MarkdownEditor está disponible como variable global automáticamente.
1 2 3 4 5 6 7 8 9 | <form action="/api/save" method="POST"> <textarea id="markdown-editor" name="content"># Hello World</textarea> <button type="submit">Save Content</button> </form> <script src="https://cdn.jsdelivr.net/npm/markdown-text-editor"></script> <script> new MarkdownEditor('#markdown-editor'); </script> |
Markdown Editor
Inicio rápido
Pasa un objeto de opciones para personalizar el editor. Todas las opciones son opcionales: omita cualquiera para usar el valor predeterminado.
1 2 3 4 | const editor = new MarkdownEditor('#markdown-editor', { placeholder: 'Write your markdown...', toolbar: ['heading', 'bold', 'italic', 'strikethrough', 'ul', 'ol', 'checklist', 'blockquote', 'link', 'preview'], }); |
La filosofía: "Native-First"
La mayoría de los editores rompen el flujo de trabajo web estándar. MarkdownEditor lo acepta. Porque se encuentra directamente encima de un <textarea>, no necesitas aprender una nueva forma de manejar los datos.
- No se necesita vinculación de datos: Funciona con
<form method="POST">Al abrir la caja - ** Acceso Estándar:** Uso
document.getElementById('editor').valuecomo una entrada normal. - Backend Agnostic: Funciona con cualquier backend (Python, Node.js, PHP, etc.) al igual que un campo de formulario normal
MarkdownEditor vs EasyMDE / SimpleMDE
La mayoría de los editores de rebajas de JavaScript (EasyMDE, SimpleMDE, editores basados en CodeMirror) * ocultan sus <textarea>* y editar una copia, escribiendo el contenido cuando se envíe el formulario. Eso funciona hasta que algo más necesita el valor: a required campo en el que el navegador no puede enfocar bloquea el envío por completo, y .value o FormData leer antes de enviar devuelve una cadena vacía, que rompe las protecciones htmx, Turbo, autoguardado y cambios no guardados. MarkdownEditor es diferente. Estiliza el área de texto que ya tienes y la deja como el campo en el que escribes, por lo que el valor es correcto en cada momento.
| Resaltar | MarkdownEditor | EasyMDE / SimpleMDE |
|---|---|---|
| Texto nativoárea conservada | ✅ | ❌ Oculto, editado como copia |
Tiene compatibilidad con required campos | ✅ | ❌ El navegador bloquea el envío |
.value corregir antes de enviar | ✅ | ❌ Vacío hasta que se envíe el formulario |
Serializa con FormData, htmx, Turbo | ✅ | ❌ Necesita la API propia del editor |
| Modo híbrido WYSIWYG | ✅ | ❌ |
| Búsqueda y sustitución integradas | ✅ | ❌ |
| Implementación RTL | Fuera del estandard | A través de CodeMirror's direction opción |
| Controladores de eventos en línea (CSP) | Ninguno | Algunas |
| Modo oscuro/ tematización | ✅ | Edición limitada |
| Tamaño del paquete, gzipped | 53 KB | 107 KB (JS + CSS) |
Integración del marco
Porque MarkdownEditor preserva el nativo <textarea>, se integra con todos los marcos de back-end sin ningún código adicional. Su servidor recibe el contenido rebajado exactamente como lo haría desde cualquier campo de formulario estándar. Para React y Vue se necesitan algunas líneas, cubiertas al final de esta sección.
Django
Añada un class a su widget de área de texto e inicializar el editor: request.POST['content'] funciona sin pasos adicionales.
1 2 3 4 5 6 7 8 | # forms.py class PostForm(forms.ModelForm): class Meta: model = Post fields = ['content'] widgets = { 'content': forms.Textarea(attrs={'class': 'markdown-editor'}), } |
1 2 | new MarkdownEditor('.markdown-editor'); // request.POST['content'] contains the markdown on submit |
Laravel
Usar f.text_area con una clase: $request->input('content') recibe la rebaja directamente.
1 2 | <textarea name="content" class="markdown-editor">{{ old('content') }}</textarea> <script>new MarkdownEditor('.markdown-editor');</script> |
Ruby on Rails
Tiene compatibilidad con form_with Al abrir la caja params[:content] contiene la rebaja. Para Turbo Drive, use turbo:load en vez de DOMContentLoaded.
1 2 3 4 5 6 | document.addEventListener('turbo:load', () => { document.querySelectorAll('.markdown-editor:not([data-mde-init])').forEach(el => { el.setAttribute('data-mde-init', 'true'); new MarkdownEditor(el); }); }); |
Node.js / Express
req.body.content recibe la rebaja al enviar el formulario: sin paso de sincronización, sin extracción personalizada.
1 2 3 | <textarea name="content" class="markdown-editor"></textarea> <script src="https://cdn.jsdelivr.net/npm/markdown-text-editor"></script> <script>new MarkdownEditor('.markdown-editor');</script> |
PHP
$_POST['content'] funciona exactamente como con cualquier área de texto estándar: colóquela y el manejo de su formulario existente no requiere cambios.
1 2 3 4 5 6 | <form method="POST" action="save.php"> <textarea name="content" class="markdown-editor"></textarea> <button type="submit">Save</button> </form> <script src="https://cdn.jsdelivr.net/npm/markdown-text-editor"></script> <script>new MarkdownEditor('.markdown-editor');</script> |
Reaccione
Crea el editor en un efecto y destrúyelo al desmontarlo. Usa defaultValue En lugar de dejar value: el editor escribe directamente en el área de texto, por lo que un enlace controlado sobrescribiría lo que el usuario está escribiendo.
1 2 3 4 5 6 7 8 9 10 11 12 13 | import { useEffect, useRef } from 'react'; import MarkdownEditor from 'markdown-text-editor'; function MarkdownField({ name, defaultValue = '', onChange }) { const ref = useRef(null); useEffect(() => { const editor = new MarkdownEditor(ref.current, { onChange }); return () => editor.destroy(); }, []); return <textarea ref={ref} name={name} defaultValue={defaultValue} />; } |
Volviendo editor.destroy() del efecto también cubre StrictMode, que ejecuta efectos dos veces en desarrollo y, de lo contrario, dejaría dos editores en un área de texto. La matriz de dependencias vacía es deliberada: las opciones se leen una vez cuando se crea el editor, por lo que volver a ejecutar el efecto lo derribaría y lo reconstruiría en cada cambio.
VUE
Misma idea. Establece el valor inicial una vez en onMounted y no obligan :value, o cada pulsación de tecla emitida de vuelta a través de v-model sobrescribiría el área de texto.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <script setup> import { ref, onMounted, onBeforeUnmount } from 'vue'; import MarkdownEditor from 'markdown-text-editor'; const el = ref(null); const props = defineProps({ modelValue: { type: String, default: '' } }); const emit = defineEmits(['update:modelValue']); let editor = null; onMounted(() => { el.value.value = props.modelValue; editor = new MarkdownEditor(el.value, { onChange: value => emit('update:modelValue', value) }); }); onBeforeUnmount(() => editor?.destroy()); </script> <template><textarea ref="el"></textarea></template> |
Utilizado como <MarkdownField v-model="content" />. En una forma simple se puede saltar v-model en su totalidad y leer el valor del área de texto en enviar, como con cualquier otro marco.
Configuración
Puede personalizar completamente el comportamiento y la interfaz del editor pasando un options objeto. Si omite una opción, se utiliza el valor ** predeterminado * *.
Las opciones se leen una vez, cuando se construye el editor. Cambiarlos después no tiene efecto: llamar destroy() y crear un nuevo editor en su lugar.
| Propiedad | Tipo | Predeterminado | Propósito |
|---|---|---|---|
mode | string | 'plain' | Establece la vista inicial. Utilice Hybrid para una experiencia WYSIWYG o Plain para la sintaxis RAW. |
placeholder | string | 'Write...' | Texto que se muestra cuando el editor está vacío. |
toolbar | array | [...] | Define qué herramientas aparecen y en qué orden. |
footer | Falso | objeto | Todo visible |
theme | string | inherited | Establece explícitamente el tema del editor (claro, oscuro, baya de nieve, baya oscura). Si se omite, el editor hereda data-theme del elemento ancestro más cercano o el <textarea> en sí misma. |
minHeight | number | 200 | Altura mínima en píxeles a la que el editor se reducirá cuando el contenido sea corto. Empareja con maxHeight para establecer el rango de crecimiento automático. |
maxHeight | number | 500 | Altura máxima en píxeles a la que el editor puede crecer en modo sin pantalla completa. Una vez que el contenido supera esta altura, aparece una barra de desplazamiento dentro del editor. El editor también tiene un controlador de arrastre para que los usuarios puedan redimensionarlo manualmente más allá de este límite. |
renderer | function | marked | Sustituye al analizador de rebajas utilizado para la vista previa. Recibe la cadena de descuento y debe devolver una cadena HTML. |
sanitizer | function | DOMPurify | Sustituye al desinfectante HTML. Recibe el HTML renderizado y debe devolver el HTML seguro para mostrar. |
onChange | function | undefined | La devolución de llamada se activa en cada cambio de contenido: escritura, acciones de la barra de herramientas, deshacer/rehacer y continuación de la lista. Recibe la cadena de descuento actual como su único argumento. |
Personalización 🛠 de la barra de herramientas
La barra de herramientas es modular. Puedes crear una experiencia mínima o un conjunto completo de funciones modificando la matriz.
Herramientas disponibles
| Categoria | Teclas de herramientas |
|---|---|
| Tipografía | heading, bold, italic, strikethrough, blockquote |
| Listas | ul (bullet), ol (numbered), checklist |
| Código | code En línea codeblock (bloque vallado) |
| Inserciones | hr Línea horizontal table (plantilla de tabla) |
| Medios de comunicación | link, image |
| Editar | undo, redo, indent, outdent |
| Visualización | preview |
| Plantillas | { variables: [...] }, configurado en línea |
Referencia de herramientas
| Herramienta | Explicación |
|---|---|
heading | Abre un menú desplegable para seleccionar el nivel de encabezado H1–H6 |
bold | Habilita el formato de texto en negrita. |
italic | Habilita el formato de texto en cursiva. |
strikethrough | Permite tachar el texto. |
ol | (Ordered List): convierte el texto en un formato de lista numerada. |
ul | (Lista desordenada): convierte el texto en una lista de viñetas. |
checklist | Añade casillas de verificación a tu texto, lo que lo hace ideal para tareas, listas de tareas pendientes o seguimiento del estado de finalización. |
blockquote | Resalte el texto citado o enfatizado. |
code | Ajusta el texto seleccionado en backticks individuales para el código en línea. Al hacer clic de nuevo se eliminan los backticks. |
codeblock | Ajusta el texto seleccionado en un bloque de código vallado de triple retroceso. Al hacer clic de nuevo, se eliminan las cercas. |
hr | Inserta un --- regla horizontal en la posición del cursor en su propia línea. |
table | Inserta una plantilla de tabla de rebajas 2x3 de arranque en la posición del cursor. |
image | Le permite insertar imágenes a través de la sintaxis de rebaja. |
link | Le permite añadir hipervínculos a su texto. |
undo | Para revertir los últimos cambios. |
redo | Para volver a aplicar los últimos cambios deshechos. |
indent | Para aumentar el nivel de sangría. |
outdent | Para disminuir el nivel de sangría. |
preview | Activa o desactiva una vista previa de pantalla completa lado a lado. Se puede hacer clic en las casillas de verificación del panel de vista previa y actualizar el origen de las rebajas al instante. Pulse Escape para salir de la pantalla completa. Si un encabezado fijo cubre el editor en pantalla completa, consulte --mte-fullscreen-z-index. |
Consejos de implementación
- Reordenar: Los botones aparecen en el orden exacto en que los enumeras en la matriz
- Eliminar: Simplemente omita cualquier clave (como
image) de la matriz para deshabilitar esa función por completo para el usuario - Native Fallback: Si no proporciona un
placeholderen JS, el plugin usará automáticamente elplaceholderatributo de tu HTML<textarea>
📊 Pie de página (barra de estado)
El pie de página se encuentra debajo del editor y muestra la **línea * * del cursor, la * * columna * *, el ** recuento de caracteres ** del documento y, opcionalmente, el ** recuento de palabras **, todo actualizado en tiempo real. Es visible de forma predeterminada y cada estadística se puede alternar de forma independiente.
| Llave | Tipo | Predeterminado | Explicación |
|---|---|---|---|
line | boolean | true | Mostrar el número de línea actual. |
col | boolean | true | Mostrar el número de columna actual. |
chars | boolean | true | Mostrar el recuento total de caracteres. |
words | boolean | false | Mostrar el recuento total de palabras. Desactivado de forma predeterminada: establecido en true activar |
Ejemplos de uso
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | // Default — line, col, and chars visible new MarkdownEditor('#editor'); // Disable the footer entirely new MarkdownEditor('#editor', { footer: false }); // Hide only character count new MarkdownEditor('#editor', { footer: { chars: false } }); // Hide line and column, keep character count new MarkdownEditor('#editor', { footer: { line: false, col: false } }); // Show only line number new MarkdownEditor('#editor', { footer: { col: false, chars: false } }); // Enable word count alongside the defaults new MarkdownEditor('#editor', { footer: { words: true } }); // Show word count only new MarkdownEditor('#editor', { footer: { line: false, col: false, chars: false, words: true } }); |
Modos 🔀 de edición
MarkdownEditor ofrece dos formas distintas de escribir y formatear tu contenido. Puedes alternar entre una vista tradicional centrada en la sintaxis o una experiencia moderna y visual.
plain(Predeterminado): Un entorno de Markdown limpio y de alto rendimiento donde la sintaxis (como**bold**o# heading) es visible. Ideal para desarrolladores y puristas de Markdownhybrid: una experiencia inspirada en WYSIWYG que renderiza el formato (negrita, cursiva, encabezados) en tiempo real a medida que escribe, sin dejar de mantener la estructura de Markdown subyacente.
1 2 3 4 5 6 7 8 9 10 11 12 | // Default initialization (Plain Mode) new MarkdownEditor('#markdown-editor'); // Explicit Plain Mode new MarkdownEditor('#markdown-editor', { mode: 'plain' }); // Hybrid (Visual) Mode new MarkdownEditor('#markdown-editor', { mode: 'hybrid' }); |
Vista previa de modo híbrido y simple:
Modo híbrido
El formato visual se representa en tiempo real mientras escribe.
Modo normal (predeterminado)
Se centra en la sintaxis cruda de Markdown para una experiencia ligera.
&Temas
MarkdownEditor hereda automáticamente su tema de la página circundante: no se requiere configuración. El editor lee data-theme desde el antepasado más cercano en la inicialización, por lo que se mantiene sincronizado con el tema de su sitio fuera de la caja.
Cómo se resuelve el tema (orden de prioridad)
themeoption: anulación explícita pasada en el objeto optionsdata-themeen el<textarea>: se establece directamente en el elementodata-themeen cualquier antepasado: por ejemplo,<html>,<body>, o un envoltorio<div>
Temas disponibles
'light' (predeterminado) 'dark', 'snowberry', 'darkberry'
Opción 1, heredar de <html> o cualquier antepasado (configuración cero)
1 2 3 4 5 6 | <html data-theme="dark"> ... <textarea id="markdown-editor"></textarea> <script> new MarkdownEditor('#markdown-editor'); // picks up dark automatically </script> |
Opción 2: data-theme Directamente sobre la cubeta. <textarea>
1 2 3 4 | <textarea id="markdown-editor" data-theme="dark"></textarea> <script> new MarkdownEditor('#markdown-editor'); </script> |
Opción 3: explícita theme option (anula todo)
1 2 3 | new MarkdownEditor('#markdown-editor', { theme: 'dark' }); |
Tema 🎨 personalizado a través de variables CSS
Puede personalizar completamente el aspecto del editor anulando sus variables CSS en el .markdown-editor-wrapper elemento o cualquier [data-theme] selector. Todos los colores utilizan el Espacio de color OKLCH para obtener resultados perceptualmente uniformes.
| Variable | Propósito | Light / Default | Dark default |
|---|---|---|---|
--color-base | Fondo del editor | oklch(100% 0 0) | oklch(10.9% 0 0) |
--color-on-base | Color de texto primario | oklch(22% 0 0) | oklch(98% 0 0) |
--color-primary | Acento principal (barra de herramientas activa, enlaces) | oklch(51.1% .262 277) | oklch(66.4% .184 286) |
--color-on-primary | Texto en superficies de color primario | oklch(96.2% .018 272) | oklch(10% .01 270) |
--color-secondary | Acento secundario | oklch(59.1% .293 323) | oklch(65% .18 220) |
--color-accent | Destacar acento (código en línea, cursiva) | oklch(54.1% .281 293) | oklch(75% .18 50) |
--color-neutral | Superficies neutras (bordes, divisores) | oklch(15% 0 0) | oklch(85% 0 0) |
--color-error | Color de estado de error | oklch(57.7% .245 27) | oklch(60% .22 30) |
--border-radius | Redondeo de esquinas del marco del editor | 0.25rem |
Ejemplo de tema personalizado
Anular cualquier variable en .markdown-editor-wrapper después de que el editor inicializa, o define una [data-theme] en tu hoja de estilo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | /* Override individual variables */ .markdown-editor-wrapper { --color-primary: oklch(60% 0.2 30); /* orange accent */ --border-radius: 0.5rem; } /* Or define a full custom theme */ [data-theme="brand"] .markdown-editor-wrapper, .markdown-editor-wrapper[data-theme="brand"] { --color-base: oklch(15% 0.01 250); --color-on-base: oklch(95% 0 0); --color-primary: oklch(65% 0.22 145); /* green */ --color-on-primary: oklch(10% 0 0); --color-accent: oklch(75% 0.18 60); --color-neutral: oklch(80% 0 0); --border-radius: 0.75rem; } |
new MarkdownEditor('#markdown-editor', { theme: 'brand' }); |
Variables
Añade un menú desplegable de la barra de herramientas para insertar marcadores de posición. Útil cuando la persona que escribe un documento no es el desarrollador que definió la sintaxis del marcador de posición, como con las plantillas de correo electrónico, factura o contrato: eligen un nombre legible y se inserta la sintaxis correcta para ellos.
1 2 3 4 5 6 7 8 9 | new MarkdownEditor('#markdown-editor', { toolbar: ['bold', 'italic', 'link', { variables: [ { label: 'Customer Name', value: '{{customer.name}}' }, { label: 'Invoice No', value: '{{invoice.number}}' } ]}, 'preview' ] }); |
La herramienta se configura en línea en el toolbar matriz, por lo que su posición entre los otros botones depende de usted. El botón enumera las etiquetas y, al hacer clic en una, inserta su value en el cursor, reemplazando cualquier selección. No se representa nada si la lista está vacía.
Valores de muestra en la vista previa
Una entrada dada un sample muestra esa muestra en la vista previa, mientras que el área de texto mantiene el marcador de posición real. Las entradas sin uno aparecen tal como están escritas, por lo que puedes mezclar ambas.
1 2 3 4 5 6 7 | { variables: [ { label: 'Customer Name', value: '{{customer.name}}', sample: 'Hannes' }, { label: 'Invoice No', value: '{{invoice.number}}' } ]} // textarea : Hi {{customer.name}}, invoice {{invoice.number}} // preview : Hi Hannes, invoice {{invoice.number}} |
Agrupamiento
dar una entrada items en lugar de un value para representar una sección con encabezado. Las entradas agrupadas y planas se pueden combinar en una lista.
1 2 3 4 5 6 7 8 9 10 | { variables: [ { label: 'Today', value: '{{today}}', sample: '10 September 2026' }, { label: 'Customer', items: [ { label: 'Name', value: '{{customer.name}}', sample: 'Hannes' }, { label: 'Email', value: '{{customer.email}}', sample: 'hannes@example.com' } ]}, { label: 'Invoice', items: [ { label: 'Number', value: '{{invoice.number}}' } ]} ]} |
Cosas que debes saber
- El editor nunca resuelve variables. Inserta texto y su aplicación sustituye valores reales más adelante, normalmente en el lado del servidor.
samplesolo afecta lo que muestra la vista previa. - Las etiquetas se muestran como texto sin formato, por lo que el marcado en una etiqueta nunca se representa.
- Las entradas con formato incorrecto se omiten en lugar de descartarse, y el botón no se representa en absoluto cuando no se configura nada utilizable.
🖋 Representador personalizado
La vista previa se representa con marcado y desinfectado con DOMPurificar. Ambos pueden ser reemplazados. Úselo cuando su aplicación ya muestre rebajas con otra biblioteca y desee que la vista previa coincida exactamente con la producción.
Ambas opciones son funciones simples que toman una cadena y devuelven una cadena, por lo que cualquier analizador y desinfectante funciona.
1 2 3 4 5 6 7 8 9 | import MarkdownIt from 'markdown-it'; import taskLists from 'markdown-it-task-lists'; import MarkdownEditor from 'markdown-text-editor'; const md = new MarkdownIt({ linkify: true, breaks: true }).use(taskLists); new MarkdownEditor('#markdown-editor', { renderer: markdown => md.render(markdown) }); |
DOMPurify aún se ejecuta en la salida, por lo que mantiene la protección XSS sin configurar nada.
desinfectante personalizado
Solo es necesario cuando el valor predeterminado elimina algo que emite su renderizador. DOMPurify elimina <iframe> de forma predeterminada, por lo que las incrustaciones de vídeo deben permitirse explícitamente. DOMPurify debe importarse en su propio código, ya que la copia incluida con el editor es interna.
1 2 3 4 5 6 7 8 9 | import DOMPurify from 'dompurify'; new MarkdownEditor('#markdown-editor', { renderer: markdown => md.render(markdown), sanitizer: html => DOMPurify.sanitize(html, { ADD_TAGS: ['iframe'], ADD_ATTR: ['allow', 'allowfullscreen', 'frameborder'] }) }); |
Usando una etiqueta de script en lugar de un paquete, cargue DOMPurify junto con el editor:
1 2 | <script src="https://cdn.jsdelivr.net/npm/dompurify"></script> <script src="https://cdn.jsdelivr.net/npm/markdown-text-editor"></script> |
Cosas que debes saber
- Solo vista previa. El formato en vivo del modo híbrido utiliza un procesador interno separado y no se ve afectado.
- Las listas de tareas necesitan compatibilidad con complementos. Las casillas de verificación en las que se puede hacer clic se encuentran buscando
input[type="checkbox"]en la salida, por lo que se necesita rebajasmarkdown-it-task-lists. El editor registra una advertencia si detecta la sintaxis de la lista de tareas y no hay casillas de verificación. - Ambos deben ser sincrónicos y devolver una cadena. Un
asyncfunción escribe[object Promise]en la vista previa. - Reemplazar el desinfectante reemplaza su protección. Un paso como
html => htmldesactiva la desinfección por completo y solo es seguro para contenido totalmente confiable.
🎨 Diseño de elementos internos
Las variables CSS cubren la mayoría de los temas porque heredan, por lo que establecer una en .markdown-editor-wrapper Llega a la barra de herramientas, botones, vista previa y pie de página. Busque un nombre de clase sólo cuando ninguna variable exponga lo que necesita.
1 2 3 4 5 | /* Variables set on the wrapper inherit down to every child */ .markdown-editor-wrapper { --border-radius: 12px; /* rounds the editor and its toolbar buttons */ --color-primary: oklch(60% 0.2 30); } |
referencia de clase
Estos nombres de clases son estables y seguros para diseñarlos.
| Clase | Elemento |
|---|---|
.markdown-editor-wrapper | Contenedor exterior que envuelve todo el editor. |
.toolbar | Barra de herramientas encima del área de edición |
.markdown-btn | Botón de barra de herramientas individual |
.preview-btn | El botón de alternancia de vista previa/pantalla completa |
.editor-layout | Cuadrícula que contiene el área de edición y vista previa una al lado de la otra |
.textarea-wrapper | Envoltura alrededor del área de edición |
.editor-textarea | El elemento de área de texto subyacente |
.display-layer | Capa de formato renderizada, solo modo híbrido |
.preview-wrapper | Columna de vista previa |
.preview-content | Rebaja renderizada dentro de la vista previa |
.editor-footer | Barra de estado debajo del editor |
.find-replace-panel | Buscar y reemplazar panel |
1 2 3 4 5 6 7 8 9 10 | /* Some variables are set by the component on itself, which beats an inherited value. Target the element directly for those. */ .markdown-editor-wrapper .markdown-btn { --btn--font-size: 0.875rem; } /* And use classes for anything no variable exposes */ .markdown-editor-wrapper .toolbar { border-bottom: 2px solid oklch(60% 0.2 30); } |
Dirigirse a un editor
Con varios editores en una página, apunte a uno de ellos con data-editor. El contenedor refleja la identificación de su <textarea>, entonces <textarea id="notes"> te da [data-editor="notes"]. La identificación en sí permanece en el área de texto, por lo que getElementById sigue funcionando.
1 2 3 4 | /* one editor only */ [data-editor="notes"] { --border-radius: 0; } |
🪟 Pantalla completa y capas (índice z)
En pantalla completa el editor usa z-index: 10000, que borra las capas que la mayoría de los marcos de UI reservan para encabezados fijos y superposiciones. Si un encabezado fijo o una barra lateral aún cubre el editor, aumente este valor.
Anularlo con --mte-fullscreen-z-index. El valor se aplica solo en pantalla completa y se hereda, por lo que establecerlo en cualquier antecesor cubre todos los editores que se encuentran debajo de él.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /* every editor on the page */ .markdown-editor-wrapper { --mte-fullscreen-z-index: 20000; } /* one section only - the value inherits down */ #admin-panel { --mte-fullscreen-z-index: 20000; } /* a single editor, by its textarea id */ [data-editor="notes"] { --mte-fullscreen-z-index: 20000; } |
API de contenido
Una de las principales fortalezas de MarkdownEditor es que mantiene la base <textarea> perfectamente sincronizado. Ya sea que esté utilizando un marco de JavaScript moderno o un backend tradicional como Django, PHP o Laravel, el flujo de trabajo sigue siendo simple y nativo.
Contenido de lectura y escritura
1. El estilo nativo (recomendado)
Debido a que el editor mejora un área de texto estándar, puede utilizar métodos DOM familiares. Esta es la forma más rápida de interactuar con sus datos sin necesidad de aprender una nueva API.
1 2 3 4 5 | // Retrieve content via ID const markdown = document.getElementById('markdown-editor').value; // Set content via ID (The editor UI updates automatically) document.getElementById('markdown-editor').value = "# New Heading Content"; |
2. Usando una referencia variable
Si tiene una referencia al elemento de área de texto, puede usarla directamente: no se necesita una API específica de la biblioteca.
1 2 3 4 5 6 7 | const textarea = document.getElementById('markdown-editor'); // Retrieve content const markdown = textarea.value; // Set content (the editor UI reflects this immediately) textarea.value = "## Updated via JS"; |
3. Configuración del contenido inicial del lado del servidor
La forma recomendada de configurar el contenido inicial es directamente en el <textarea> HTML: esto funciona de forma natural con todos los frameworks backend (Django, Laravel, Rails, PHP, etc.) y el editor lo renderiza automáticamente al iniciar.
1 2 | <!-- Recommended: set content server-side --> <textarea id="markdown-editor"># Hello World</textarea> |
Para leer o actualizar contenido en tiempo de ejecución, use el nativo textarea valor. Llamar editor.render() después de una actualización para actualizar la vista previa y la capa híbrida.
1 2 3 4 5 6 7 8 | const textarea = document.getElementById('markdown-editor'); // Read const markdown = textarea.value; // Update at runtime textarea.value = '# New content'; editor.render(); |
4. Derribando al editor: destroy()
Llamar editor.destroy() para eliminar el contenedor DOM del editor y restaurar el original <textarea> a su posición en el documento. Útil en aplicaciones de una sola página al desmontar una vista.
1 2 3 4 | const editor = new MarkdownEditor('#markdown-editor'); // Remove the editor and restore the plain textarea editor.destroy(); |
Reaccionar a los cambios con onChange
pasar un onChange devolución de llamada para recibir notificaciones sobre cada cambio de contenido. Recibe la cadena de rebajas actual.
1 2 3 4 5 | const editor = new MarkdownEditor('#markdown-editor', { onChange(value) { console.log('Content changed:', value.length, 'characters'); } }); |
Borrador de guardado automático con localStorage
Usar onChange para guardar un borrador con cada pulsación de tecla. Restáurelo completando previamente el área de texto antes de inicializar el editor.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | const DRAFT_KEY = 'my-page-draft'; // Restore saved draft before init (only if textarea starts empty) const textarea = document.getElementById('markdown-editor'); const saved = localStorage.getItem(DRAFT_KEY); if (saved && !textarea.value) textarea.value = saved; // Save on every change const editor = new MarkdownEditor('#markdown-editor', { onChange(value) { localStorage.setItem(DRAFT_KEY, value); } }); // Clear draft after successful form submission document.querySelector('form').addEventListener('submit', () => { localStorage.removeItem(DRAFT_KEY); }); |
Envío de formulario
Porque MarkdownEditor está construido directamente en el nativo <textarea>, es compatible con todos los marcos de backend (Django, Laravel, PHP, Ruby on Rails, etc.) desde el primer momento.
Aquí es donde brilla la filosofía de "los nativos primero". No es necesario sincronizar datos manualmente antes de enviar un formulario. El navegador trata al editor exactamente como un campo de entrada estándar.
1 2 3 4 5 6 7 8 9 10 11 12 | <form method="POST" action="/api/submit"> <textarea id="markdown-editor" name="content" class="h-48" rows="5"> # Initial Content </textarea> <button type="submit">Submit to Server</button> </form> <script> // Just initialize it. That's it. new MarkdownEditor('#markdown-editor'); </script> |
Nota: Inicialización obligatoria del complemento MarkdownEditor
Simplemente use un HTML estándar <form>. El name El atributo en el área de texto es lo que su servidor utilizará para identificar el contenido.
🚀 Por qué cambia las reglas del juego para los backends
Dado que el editor conserva el nativo. <textarea> comportamiento, su servidor maneja los datos como una cadena estándar. No se requiere lógica adicional: no preventDefault() y sin manual FormData construcción.
💡 Por qué esta es una "función excelente":
La mayoría de los editores (como Quill, Editor.js, simpleMDE, easyMDE) guardan datos en estructuras JSON complejas. Si un desarrollador los usa, debe reescribir el esquema de su base de datos y su lógica de representación.
Con MarkdownEditor, un desarrollador puede tomar un sitio web antiguo y reemplazarlo por uno simple. <textarea> con su editor, y el backend ni siquiera sabe que cambió. Simplemente recibe el mismo texto sin formato de siempre, pero el usuario obtiene una experiencia 10 veces mejor.
| Marco / Idioma | Cómo acceder al contenido de Markdown |
|---|---|
| PHP | $_POST['content'] |
| Django | request.POST.get('content') |
| Node.js (Rápido) | req.body.content |
| Laravel | $request->input('content') |
| Ruby on Rails | params[:content] |
🖼️ Carga de imágenes avanzada
Manejar la carga de imágenes de forma nativa: en lugar de depender de cadenas Base64 lentas y con mucha memoria, es una ganancia significativa tanto para el rendimiento como para el SEO.
Opciones de configuración
La herramienta de imagen admite una fileInput configuración para manejar cargas directas al servidor.
accept: Defina una variedad de formatos de imagen permitidos (por ejemplo, 'webp', 'avif')uploadUrl: especifique el punto final de backend dondeFileEl objeto será enviado a través dePOSTparams: Objeto opcional para enviar datos adicionales (como tokens CSRF, ID de usuario o nombres de carpetas) junto con el archivo de imagen.
Ejemplo de uso (configuración completa)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | const options = { placeholder: 'Start writing...', toolbar: [ 'link', { image: { fileInput: { accept: ['webp', 'avif'], // restrict the image upload format uploadUrl: '/api/upload', // Your upload endpoint params: { _token: 'your_csrf_token_here', // Essential for Laravel/Django folder: 'blog_posts' } }, // Supports boolean: true/false OR object: { required: true } altInput: { required: true } } }, 'preview' ], } const editor = new MarkdownEditor('#markdown-editor', options); |
📡 Integración del servidor
La Solicitud
El editor envía un POST solicitar como multipart/form-data. Por defecto, incluye:
image_file: El objeto de archivo realimage_alt: El texto alternativo ingresado por el usuario.- ...más cualquier dato personalizado definido en el
paramsobjeto
La respuesta requerida
Para confirmar una carga exitosa e insertar la imagen en el editor, su servidor debe devolver la siguiente estructura JSON:
1 2 3 4 | { "success": true, "image_path": "https://cdn.yourdomain.com/uploads/image.webp" } |
Nota: asegúrese de utilizar la clave image_path para la URL de la imagen cargada.
Validación de texto alternativo de imagen (altInput)
Para garantizar que su contenido siga siendo accesible y compatible con SEO, MarkdownEditor aplica la validación de texto alternativo de forma predeterminada.
- Comportamiento predeterminado: si
altInputno está definido, por defecto es{ required: true } - Hacer cumplir la accesibilidad: se impedirá que los usuarios inserten una imagen hasta que se proporcione una descripción alternativa.
1. Predeterminado (no se necesita configuración)
1 2 3 4 | // Alt text is REQUIRED by default image: { fileInput: { uploadUrl: '/api/upload' } } |
2. Taquigrafía (desactivar validación)
Si desea permitir imágenes sin descripciones, simplemente configure el valor booleano en false.
1 2 3 | image: { altInput: false // Users can now skip the alt text field } |
3. Basado en objetos (explícito)
1 2 3 4 5 | image: { altInput: { required: false // Disables alt text validation — users can skip the alt field } } |
Uso de imagen estándar (No fileInput)
Si fileInput no está configurado, el editor utiliza de forma predeterminada un modal simple basado en URL. Esto es ideal si sus usuarios se vinculan principalmente a servidores de imágenes externos.
1 2 3 4 5 6 7 8 | const options = { toolbar: [ 'link', 'image', 'preview' ], } const editor = new MarkdownEditor('#markdown-editor', options); |
💡 ¿Por qué utilizar parámetros?
En frameworks como Laravel o Django, no puedes cargar archivos sin un token CSRF. Al agregar _token hacia params objeto, su solicitud pasará a través del middleware de seguridad del backend sin problemas, manteniendo la filosofía de "Lógica Cero" para sus controladores del lado del servidor.
⌨️ Atajos de teclado
Las acciones de formato comunes se pueden activar directamente desde el teclado sin tocar la barra de herramientas. Cada acceso directo también se muestra en la información sobre herramientas del botón de la barra de herramientas correspondiente.
| Atajo | Acción |
|---|---|
Ctrl + B / ⌘ B | Alternar Negrita |
Ctrl + I / ⌘ I | Alternar Cursiva |
Ctrl + K / ⌘ K | Insertar enlace |
Ctrl + ` / ⌘ ` | Alternar en línea Code |
Ctrl + Shift + S / ⌘ ⇧ S | Alternar |
Ctrl + 1 / ⌘ 1 | Título 1 |
Ctrl + 2 / ⌘ 2 | Título 2 |
Ctrl + 3 / ⌘ 3 | Título 3 |
Ctrl + L / ⌘ L | Alternar lista de viñetas |
Ctrl + Z / ⌘ Z | Deshacer |
Ctrl + Shift + Z / ⌘ ⇧ Z | Rehacer |
Tab | Sangrar líneas seleccionadas |
Shift + Tab | Eliminar sangría de líneas seleccionadas |
Ctrl + F / ⌘ F | Abrir panel de búsqueda |
Ctrl + H / ⌘ H | Abrir el panel Buscar y reemplazar |
Ctrl + Shift + F / ⌘ ⇧ F | Alternar vista previa a pantalla completa |
F11 | Alternar vista previa a pantalla completa |
Escape | Cerrar el panel Buscar/Salir de la vista previa en pantalla completa |
🔍 Buscar y reemplazar
Un panel integrado de búsqueda y reemplazo está disponible dentro del editor: no se necesita extensión del navegador ni herramienta separada.
- Prensa
Ctrl + F(o⌘ F) para abrir el panel Buscar - Prensa
Ctrl + H(o⌘ H) para abrir el panel Buscar y reemplazar - La búsqueda no distingue entre mayúsculas y minúsculas y muestra un contador de partidos en vivo (por ejemplo, 3 de 12)
- Navegar partidos con los botones ▲ / ▼ o
Enter/Shift + Enter - Reemplazar reemplaza la coincidencia resaltada actual; Reemplazar todo reemplaza cada ocurrencia a la vez
- Prensa
Escapepara cerrar el panel y devolver el foco al editor
El panel flota en la esquina superior derecha del área de contenido del editor y no interrumpe la escritura.
Características
🔌 Integración de formularios nativos
Funciona exactamente como un estándar <textarea>. Sin API complejas: solo use el value o name atributo. "Simplemente funciona" con envíos de formularios HTML estándar en PHP, Django o Node.js.
🖼️ Carga de imágenes avanzada
Configure las cargas del servidor nativo a través de API. Evite cadenas Base64 pesadas para garantizar cargas de página más rápidas y un SEO superior al alojar imágenes en su propia CDN.
🔀 Modos híbridos y simples
Cambiar entre un Híbrido (WYSIWYG) experiencia en edición visual o Rebaja simple Modo para una sensación de codificación tradicional.
🚀 Alto rendimiento
A 53 KB comprimidos paquete (252 KB minimizado, CSS incluido) optimizado para "contenido pesado". Maneja documentos y archivos de gran tamaño sin ningún retraso en la entrada ni caída del rendimiento. Actualizaciones de vista previa sin rebote, cálculos de estilo en caché y manejo del teclado sin conflictos: por lo que Tab y Enter siempre hacen exactamente una cosa.
🌍 Soporte RTL incorporado
Soporte nativo para idiomas de derecha a izquierda como árabe, urdu y farsi. Perfecto para crear aplicaciones accesibles globalmente.
✨ Resaltado de sintaxis
Legibilidad mejorada con código claro y formato de rebajas.
🌙 Temas adaptativos
Incluye soporte automático para el modo oscuro. Se sincroniza con la configuración de su sistema o con el UI de mermelada de frutas Biblioteca para una experiencia visual perfecta.
📝 Edición inteligente
Continuación automática de listas estilo GitHub para listas ordenadas, listas desordenadas y listas de verificación: presione Enter y el editor continúa el patrón. Se puede hacer clic en las casillas de verificación en el panel de vista previa y se sincronizan con la fuente de rebajas al instante.
📱 Totalmente receptivo
Una interfaz de usuario fluida y pensada para dispositivos móviles que se adapta perfectamente a computadoras de escritorio, tabletas y teléfonos inteligentes para editar sobre la marcha.
📦 Soporte universal
compatible con ESM, UMD, CommonJS y IIFE. Funciona de inmediato a través de CDN (<script src>), npm o cualquier paquete (Vite, webpack, Rollup): no se necesita configuración adicional.
♿ Accesible por defecto
Compatibilidad total con ARIA integrada: punto de referencia de la barra de herramientas, región de vista previa etiquetada, botones fáciles de leer en pantalla, aria-pressed en la palanca de vista previa, disabled y aria-disabled en herramientas inactivas y restauración correcta del enfoque cuando se cierran los modales.
🛡️ Cero conflictos de CSS
Los estilos del editor tienen un alcance completo para .markdown-editor-wrapper. La verificación previa global de Tailwind está excluida, por lo que el editor convive de forma segura junto con Bootstrap, Tailwind o cualquier otro marco sin alterar sus estilos.
⌨️ Atajos de teclado
Ctrl+B, Ctrl+I, Ctrl+K, Ctrl+`, Ctrl+Shift+S: acciones de formato comunes sin tocar el mouse. Cada acceso directo se muestra en la información sobre herramientas del botón de la barra de herramientas.
🔍 Buscar y reemplazar
Prensa Ctrl+F encontrar o Ctrl+H para abrir buscar y reemplazar. Búsqueda que no distingue entre mayúsculas y minúsculas con contador de partidos en vivo, navegación siguiente/anterior, reemplazo único y reemplazar todo: sin salir del editor.
🔒 Vista previa segura XSS
La vista previa renderizada se desinfecta mediante DOMPurificar antes de escribirse en el DOM. Las etiquetas de script, los controladores de eventos en línea y las URL maliciosas en las entradas de rebajas diseñadas se eliminan automáticamente: no se requiere configuración.
▶️ Vista previa en tiempo real
Vea su descuento renderizado instantáneamente a medida que escribe.
🔗 Fácil integración
Integre perfectamente en cualquier proyecto web con una configuración mínima.
🛠️ Barra de herramientas personalizable
Configure y reordene dinámicamente las opciones de la barra de herramientas como negrita, cursiva y más.
Ejemplo de configuración completa
Utilice este ejemplo completo para inicializar MarkdownEditor con todas las funciones principales, incluido el orden personalizado de la barra de herramientas y el manejo avanzado de carga de imágenes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | const editor = new MarkdownEditor('#markdown-editor', { mode: 'hybrid', placeholder: 'Start writing...', footer: { line: true, col: true, chars: true, words: true, }, onChange(value) { console.log('Content updated:', value.length, 'characters'); }, toolbar: [ 'heading', 'bold', 'italic', 'strikethrough', 'blockquote', 'ul', 'ol', 'checklist', 'code', 'codeblock', 'hr', 'table', { image: { fileInput: { accept: ['webp', 'avif', 'png'], uploadUrl: '/api/upload' } } }, 'link', 'undo', 'redo', 'indent', 'outdent', 'preview' ], }); // Read content natively const markdown = document.getElementById('markdown-editor').value; // destroy() when the view unmounts (SPAs) // editor.destroy(); |
¿Encontró esto útil?
Una estrella de GitHub ayuda a otros desarrolladores a descubrir el editor. es parte de mermelada de frutas. Una estrella allí también ayuda.