Barre d'outils, aperçu en direct et WYSIWYG sur la zone de texte que vous possédez déjà
Dernière mise à jour:
Un éditeur de démarques qui améliore votre zone de texte au lieu de la remplacer, afin que la soumission, la validation et les champs obligatoires du formulaire continuent de fonctionner. Modes WYSIWYG et Markdown simple, aperçu en direct, recherche et remplacement, prise en charge RTL, mode sombre. Fonctionne de manière autonome avec Django, Laravel, Rails, Node.js, PHP et n'importe quelle pile.
Aucun Frutjam et aucun Tailwind requis. Les styles sont regroupés, de sorte que l'éditeur s'intègre dans n'importe quel projet.
Installation
NPM (bundles : Vite, webpack, Rollup, etc.)
npm install markdown-text-editor |
1 2 | import MarkdownEditor from 'markdown-text-editor'; new MarkdownEditor('#markdown-editor'); |
Les définitions TypeScript sont livrées avec le package, de sorte que les options, les entrées de la barre d'outils et les formes de variables sont vérifiées et complétées automatiquement sans installation supplémentaire.
CDN : module 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 : balise de script global (IIFE)
Aucune importation nécessaire : MarkdownEditor est automatiquement disponible en tant que variable globale.
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> |
Démo de l'éditeur Markdown
Démarrage rapide
Passez un objet d'options pour personnaliser l'éditeur. Toutes les options sont facultatives : omettez-en toutes pour utiliser la valeur par défaut.
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 philosophie : « Les autochtones d'abord »
La plupart des éditeurs rompent avec le flux de travail Web standard. MarkdownEditor l'accepte. Parce qu'il se trouve directement au-dessus d'un <textarea>, vous n'avez pas besoin d'apprendre une nouvelle façon de gérer les données.
- Aucune liaison de données nécessaire : Fonctionne avec
<form method="POST">hors de la boîte - Accès standard : Utilisation
document.getElementById('editor').valuetout comme une entrée normale. - Backend Agnostic : Fonctionne avec n'importe quel backend (Python, Node.js, PHP, etc.), tout comme un champ de formulaire normal
MarkdownEditor et EasyMDE / SimpleMDE
La plupart des éditeurs de démarques JavaScript (EasyMDE, SimpleMDE, éditeurs basés sur CodeMirror) ** cachent votre <textarea>** et modifiez une copie, en écrivant le contenu lorsque le formulaire est soumis. Cela fonctionne jusqu'à ce que quelque chose d'autre ait besoin de la valeur : a required le champ sur lequel le navigateur ne peut pas se concentrer bloque entièrement la soumission, et .value ou FormData lire avant de soumettre renvoie une chaîne vide, ce qui brise les protections htmx, Turbo, la sauvegarde automatique et les modifications non enregistrées. MarkdownEditor est différent. Il stylise la zone de texte que vous avez déjà et la laisse comme champ dans lequel vous tapez, de sorte que la valeur est correcte à tout moment.
| Fonctionnalité | Éditeur de démarques | EasyMDE / SimpleMDE |
|---|---|---|
| Zone de texte native préservée | ✅ | ❌ Caché, édité en copie |
Fonctionne avec required champs | ✅ | ❌ Le navigateur bloque la soumission |
.value corriger avant de soumettre | ✅ | ❌ Vide jusqu'à ce que le formulaire soit soumis |
Sérialise avec FormData, htmx, Turbo | ✅ | ❌ Nécessite la propre API de l'éditeur |
| Mode hybride WYSIWYG | ✅ | ❌ |
| Rechercher et remplacer intégrés | ✅ | ❌ |
| Prise en charge RTL | ✅ Intégré | Via CodeMirror direction option |
| Gestionnaires d'événements en ligne (CSP) | ✅ Aucun | Quelques |
| Mode sombre / thème | ✅ | Limité |
| Taille du paquet, compressé | 53 Ko | 107 Ko (JS + CSS) |
Intégration du cadre
Parce que MarkdownEditor préserve le natif <textarea>, il s'intègre à tous les frameworks backend sans aucun code supplémentaire. Votre serveur reçoit le contenu de démarque exactement comme il le ferait pour n'importe quel champ de formulaire standard. Pour React et Vue, cela prend quelques lignes, couvertes à la fin de cette section.
Django
Ajouter un class à votre widget textarea et initialisez l'éditeur : request.POST['content'] fonctionne sans étapes supplémentaires.
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
Utiliser f.text_area avec une classe : $request->input('content') reçoit directement la démarque.
1 2 | <textarea name="content" class="markdown-editor">{{ old('content') }}</textarea> <script>new MarkdownEditor('.markdown-editor');</script> |
Rubis sur Rails
Fonctionne avec form_with hors de la boîte : params[:content] contient la démarque. Pour Turbo Drive, utilisez turbo:load au lieu 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 reçoit la démarque lors de la soumission du formulaire : pas d'étape de synchronisation, pas d'extraction personnalisée.
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'] fonctionne exactement comme avec n'importe quelle zone de texte standard : déposez-la et la gestion de votre formulaire existant ne nécessite aucune modification.
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> |
Réagir
Créez l'éditeur dans un effet et détruisez-le au démontage. Utiliser defaultValue plutôt que value: l'éditeur écrit directement dans la zone de texte, donc une liaison contrôlée écraserait ce que l'utilisateur tape.
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} />; } |
De retour editor.destroy() de l'effet couvre également StrictMode, qui exécute les effets deux fois en cours de développement et laisserait autrement deux éditeurs sur une seule zone de texte. Le tableau de dépendances vide est délibéré : les options sont lues une fois lorsque l'éditeur est créé, donc réexécuter l'effet le démolirait et le reconstruirait à chaque modification.
Vue
Même idée. Réglez la valeur initiale une fois dans onMounted et ne lie pas :value, ou chaque frappe émise via v-model écraserait la zone de texte.
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> |
Utilisé comme <MarkdownField v-model="content" />. Sous une forme simple, vous pouvez ignorer v-model entièrement et lisez la valeur de la zone de texte lors de la soumission, comme avec tout autre framework.
Configuration
Vous pouvez entièrement personnaliser le comportement et l'interface de l'éditeur en passant un options objet. Si vous omettez une option, la valeur par défaut est utilisée.
Les options sont lues une fois, lorsque l'éditeur est construit. Les modifier ensuite n'a aucun effet : appelez destroy() et créez un nouvel éditeur à la place.
| Propriété | Taper | Défaut | But |
|---|---|---|---|
mode | string | 'plain' | Définit la vue initiale. Utilisez hybride pour une expérience WYSIWYG ou simple pour une syntaxe brute. |
placeholder | string | 'Write...' | Texte affiché lorsque l'éditeur est vide. |
toolbar | array | [...] | Définit quels outils apparaissent et dans quel ordre. |
footer | faux | objet | tout est visible | Contrôle la barre d'état affichée sous l'éditeur. Régler sur false pour le masquer entièrement, ou passez un objet pour basculer les statistiques individuelles. |
theme | string | inherited | Définit explicitement le thème de l'éditeur (clair, foncé, symphorine, mûre). En cas d'omission, l'éditeur hérite data-theme de l'élément ancêtre le plus proche ou du <textarea> lui-même. |
minHeight | number | 200 | Hauteur minimale en pixels à laquelle l'éditeur se réduira lorsque le contenu est court. S'associe avec maxHeight pour définir la plage de croissance automatique. |
maxHeight | number | 500 | Hauteur maximale en pixels que l'éditeur peut atteindre en mode non plein écran. Une fois que le contenu dépasse cette hauteur, une barre de défilement apparaît dans l'éditeur. L'éditeur dispose également d'une poignée de déplacement permettant aux utilisateurs de le redimensionner manuellement au-delà de cette limite. |
renderer | function | marked | Remplace l'analyseur de démarque utilisé pour l'aperçu. Reçoit la chaîne de démarque et doit renvoyer une chaîne HTML. |
sanitizer | function | DOMPurify | Remplace le désinfectant HTML. Reçoit le code HTML rendu et doit renvoyer le code HTML sécurisé à afficher. |
onChange | function | undefined | Rappel déclenché à chaque modification de contenu : saisie, actions de la barre d'outils, annulation/rétablissement et continuation de la liste. Reçoit la chaîne de démarque actuelle comme seul argument. |
labels | object | undefined | Traductions pour la propre interface de l'éditeur, saisies par la chaîne anglaise. Tout ce que vous omettez reste en anglais. |
🛠 Personnalisation de la barre d'outils
La barre d'outils est modulaire. Vous pouvez créer une expérience minimale ou une suite de puissance complète en modifiant la baie.
Outils disponibles
| Catégorie | Clés d'outils |
|---|---|
| Typographie | heading, bold, italic, strikethrough, blockquote |
| Listes | ul (bullet), ol (numbered), checklist |
| Code | code (en ligne), codeblock (bloc clôturé) |
| Insertions | hr (règle horizontale), table (modèle de tableau) |
| Médias | link, image |
| Édition | undo, redo, indent, outdent |
| Voir | preview |
| Modèles | { variables: [...] }, configuré en ligne |
Référence des outils
| Outil | Description |
|---|---|
heading | Ouvre une liste déroulante pour sélectionner les niveaux de titre H1 à H6 |
bold | Active le formatage du texte en gras. |
italic | Active le formatage du texte en italique. |
strikethrough | Permet le texte barré. |
ol | (Liste ordonnée) : convertit le texte en un format de liste numérotée. |
ul | (Liste non ordonnée) : convertit le texte en liste à puces. |
checklist | Ajoute des cases à cocher à votre texte, ce qui le rend idéal pour les tâches, les listes de tâches ou le suivi de l'état d'avancement. |
blockquote | Mettez en surbrillance le texte cité ou souligné. |
code | Encapsule le texte sélectionné dans des backticks simples pour le code en ligne. Cliquer à nouveau supprime les backticks. |
codeblock | Encapsule le texte sélectionné dans un bloc de code clôturé à triple backtick. Un nouveau clic supprime les clôtures. |
hr | Insère un --- règle horizontale à la position du curseur sur sa propre ligne. |
table | Insère un modèle de tableau de démarques de départ 2x3 à la position du curseur. |
image | Vous permet d'insérer des images via la syntaxe markdown. |
link | Vous permet d'ajouter des hyperliens à votre texte. |
undo | Pour annuler les dernières modifications. |
redo | Pour réappliquer les dernières modifications annulées. |
indent | Pour augmenter le niveau d'indentation. |
outdent | Pour diminuer le niveau d'indentation. |
preview | Active/désactive un aperçu côte à côte en plein écran. Les cases à cocher dans le volet d'aperçu sont cliquables et mettent à jour instantanément la source de démarque. Appuyez sur Échap pour quitter le plein écran. Si un en-tête fixe couvre l'éditeur en plein écran, voir --mte-fullscreen-z-index. |
💡 Conseils de mise en œuvre :
- Réorganisation : les boutons apparaissent dans l'ordre exact dans lequel vous les répertoriez dans le tableau
- Suppression : omettez simplement n'importe quelle clé (comme
image) du tableau pour désactiver entièrement cette fonctionnalité pour l'utilisateur - Native Fallback : si vous ne fournissez pas de
placeholderen JS, le plugin utilisera automatiquement leplaceholderattribut de votre code HTML<textarea>
📊 Pied de page (barre d'état)
Le pied de page se trouve sous l'éditeur et affiche la ligne, la colonne du curseur, le nombre de caractères du document et éventuellement le nombre de mots, le tout mis à jour en temps réel. Il est visible par défaut et chaque statistique peut être basculée indépendamment.
| Clé | Taper | Défaut | Description |
|---|---|---|---|
line | boolean | true | Afficher le numéro de ligne actuel. |
col | boolean | true | Afficher le numéro de colonne actuel. |
chars | boolean | true | Afficher le nombre total de caractères. |
words | boolean | false | Afficher le nombre total de mots. Désactivé par défaut : réglé sur true pour activer. |
Exemples d'utilisation
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 } }); |
🔀 Modes d'édition
MarkdownEditor propose deux manières distinctes d'écrire et de formater votre contenu. Vous pouvez basculer entre une vue traditionnelle axée sur la syntaxe ou une expérience moderne axée avant tout sur le visuel.
plain(Par défaut) : Un environnement Markdown propre et hautes performances où la syntaxe (comme**bold**ou# heading) est visible. Idéal pour les développeurs et les puristes de Markdownhybrid: Une expérience inspirée de WYSIWYG qui restitue le formatage (gras, italique, titres) en temps réel au fur et à mesure que vous tapez, tout en conservant la structure Markdown sous-jacente.
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' }); |
Aperçu des modes hybride et simple :
Mode hybride
Le formatage visuel est rendu en temps réel pendant que vous tapez.
Mode simple (par défaut)
Se concentre sur la syntaxe Markdown brute pour une expérience légère.
🌙 Thématisation
MarkdownEditor hérite automatiquement de son thème de la page environnante : aucune configuration requise. L'éditeur lit data-theme de l'ancêtre le plus proche lors de l'initialisation, il reste donc synchronisé avec le thème de votre site dès la sortie de la boîte.
Comment le thème est résolu (ordre de priorité)
themeoption : remplacement explicite passé dans l'objet optionsdata-themesur le<textarea>: défini directement sur l'élémentdata-themesur n'importe quel ancêtre : par ex.<html>,<body>, ou un emballage<div>
Thèmes disponibles
'light' (défaut), 'dark', 'snowberry', 'darkberry'
Option 1, hériter de <html> ou n'importe quel ancêtre (zéro config)
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> |
Option 2 : définir data-theme directement sur le <textarea>
1 2 3 4 | <textarea id="markdown-editor" data-theme="dark"></textarea> <script> new MarkdownEditor('#markdown-editor'); </script> |
Option 3 : explicite theme option (remplace tout)
1 2 3 | new MarkdownEditor('#markdown-editor', { theme: 'dark' }); |
🎨 Thème personnalisé via des variables CSS
Vous pouvez entièrement personnaliser l'apparence de l'éditeur en remplaçant ses variables CSS sur le .markdown-editor-wrapper élément ou tout autre [data-theme] sélecteur. Toutes les couleurs utilisent le Espace colorimétrique OKLCH pour des résultats perceptuellement uniformes.
| Variable | But | Lumière par défaut | Sombre par défaut |
|---|---|---|---|
--color-base | Contexte de l'éditeur | oklch(100% 0 0) | oklch(10.9% 0 0) |
--color-on-base | Couleur du texte principal | oklch(22% 0 0) | oklch(98% 0 0) |
--color-primary | Accent principal (barre d'outils active, liens) | oklch(51.1% .262 277) | oklch(66.4% .184 286) |
--color-on-primary | Texte sur des surfaces de couleurs primaires | oklch(96.2% .018 272) | oklch(10% .01 270) |
--color-secondary | Accent secondaire | oklch(59.1% .293 323) | oklch(65% .18 220) |
--color-accent | Mettre l'accent en surbrillance (code en ligne, italique) | oklch(54.1% .281 293) | oklch(75% .18 50) |
--color-neutral | Surfaces neutres (bordures, séparateurs) | oklch(15% 0 0) | oklch(85% 0 0) |
--color-error | Couleur de l'état d'erreur | oklch(57.7% .245 27) | oklch(60% .22 30) |
--border-radius | Arrondi des coins du cadre de l'éditeur | 0.25rem |
Exemple de thème personnalisé
Remplacer n'importe quelle variable sur .markdown-editor-wrapper après l'initialisation de l'éditeur, ou définir un paramètre personnalisé [data-theme] block dans votre feuille de style :
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' }); |
🌐 Langue de l'interface
Le texte, les info-bulles, les éléments de menu, les champs modaux et les boutons de l'éditeur sont en anglais par défaut. Passer un labels objet de le traduire. Les clés sont les chaînes anglaises, de la même manière que fonctionne gettext, vous répertoriez donc uniquement ce que vous souhaitez modifier et tout ce qui manque revient à l'anglais.
1 2 3 4 5 6 7 8 9 10 11 12 13 | new MarkdownEditor('#markdown-editor', { labels: { 'Bold': 'Gras', 'Italic': 'Italique', 'Heading': 'Titre', 'Link': 'Lien', 'Image link': 'Lien de l\'image', 'URL': 'URL', 'Alt text': 'Texte alternatif', 'Apply': 'Appliquer', 'Uploading...': 'Téléversement...', }, }); |
C’est ce qui compte le plus aux côtés du contenu de droite à gauche. L'arabe, l'ourdou et le farsi s'affichent correctement, mais une barre d'outils qui indique toujours "Gras" sur le texte de droite à gauche n'est qu'à moitié traduite.
🏷 Variables
Ajoute une liste déroulante de barre d'outils pour insérer des espaces réservés. Utile lorsque la personne qui rédige un document n'est pas le développeur qui a défini la syntaxe de l'espace réservé, comme pour les modèles d'e-mail, de facture ou de contrat : elle choisit un nom lisible et la syntaxe correcte est insérée pour elle.
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' ] }); |
L'outil est configuré en ligne dans le toolbar tableau, donc sa position parmi les autres boutons dépend de vous. Le bouton répertorie les étiquettes, et cliquer sur l'un insère son value au niveau du curseur, remplaçant toute sélection. Rien n'est rendu si la liste est vide.
Exemples de valeurs dans l'aperçu
Une entrée donnée un sample montre cet échantillon dans l'aperçu, tandis que la zone de texte conserve le véritable espace réservé. Les entrées sans une apparaissent telles qu'elles sont écrites, vous pouvez donc mélanger les deux.
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}} |
Regroupement
Donner une entrée items au lieu d'un value pour afficher une section intitulée. Les entrées groupées et plates peuvent être mélangées dans une seule liste.
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}}' } ]} ]} |
Choses à savoir
- L'éditeur ne résout jamais les variables. Il insère du texte et votre application remplace les valeurs réelles plus tard, généralement côté serveur.
samplen'affecte que ce que l'aperçu affiche. - Les étiquettes sont affichées sous forme de texte brut, donc le balisage d'une étiquette n'est jamais rendu.
- Les entrées mal formées sont ignorées plutôt que lancées, et le bouton n'est pas rendu du tout lorsque rien d'utile n'est configuré.
🖋 Rendu personnalisé
L'aperçu est rendu avec marqué et désinfecté avec DOMPurifier. Les deux peuvent être remplacés. Utilisez-le lorsque votre application restitue déjà le démarque avec une autre bibliothèque et que vous souhaitez que l'aperçu corresponde exactement à la production.
Les deux options sont des fonctions simples qui prennent une chaîne et renvoient une chaîne, donc n'importe quel analyseur et n'importe quel désinfectant fonctionne.
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 s'exécute toujours sur la sortie, vous conservez donc la protection XSS sans rien configurer.
Désinfectant personnalisé
Nécessaire uniquement lorsque la valeur par défaut supprime quelque chose que votre moteur de rendu émet. DOMPurify supprime <iframe> par défaut, les intégrations de vidéos doivent donc être autorisées explicitement. DOMPurify doit être importé dans votre propre code, puisque la copie fournie avec l'éditeur est interne.
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'] }) }); |
En utilisant une balise de script au lieu d'un bundler, chargez DOMPurify avec l'éditeur :
1 2 | <script src="https://cdn.jsdelivr.net/npm/dompurify"></script> <script src="https://cdn.jsdelivr.net/npm/markdown-text-editor"></script> |
Choses à savoir
- Aperçu uniquement. Le formatage en direct du mode hybride utilise un moteur de rendu interne distinct et n'est pas affecté.
- Les listes de tâches nécessitent la prise en charge du plugin. Les cases à cocher cliquables sont trouvées en recherchant
input[type="checkbox"]dans la sortie, donc une démarque est nécessairemarkdown-it-task-lists. L'éditeur enregistre un avertissement s'il détecte la syntaxe de la liste des tâches et aucune case à cocher. - Les deux doivent être synchrones et renvoyer une chaîne. Un
asyncla fonction écrit[object Promise]dans l'aperçu. - Le remplacement du désinfectant remplace votre protection. Un passage tel que
html => htmldésactive complètement la désinfection et n'est sûr que pour le contenu entièrement fiable.
🎨 Stylisme des éléments internes
Les variables CSS couvrent la plupart des thèmes car elles héritent, donc en définir une sur .markdown-editor-wrapper atteint la barre d'outils, les boutons, l'aperçu et le pied de page. Recherchez un nom de classe uniquement lorsqu'aucune variable n'expose ce dont vous avez besoin.
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); } |
Référence de classe
Ces noms de classe sont stables et peuvent être stylisés en toute sécurité.
| Classe | Élément |
|---|---|
.markdown-editor-wrapper | Conteneur externe enveloppant l'ensemble de l'éditeur |
.toolbar | Bande de barre d'outils au-dessus de la zone d'édition |
.markdown-btn | Bouton de barre d'outils individuel |
.preview-btn | Le bouton bascule aperçu/plein écran |
.editor-layout | Grille contenant la zone d'édition et l'aperçu côte à côte |
.textarea-wrapper | Wrapper autour de la zone d’édition |
.editor-textarea | L'élément textarea sous-jacent |
.display-layer | Couche de formatage rendue, mode hybride uniquement |
.preview-wrapper | Colonne Aperçu |
.preview-content | Démarquage rendu dans l'aperçu |
.editor-footer | Barre d'état sous l'éditeur |
.find-replace-panel | Rechercher et remplacer le panneau |
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); } |
Cibler un éditeur
Avec plusieurs éditeurs sur une même page, ciblez l'un d'entre eux avec data-editor. Le wrapper reflète l'identifiant de son <textarea>, donc <textarea id="notes"> te donne [data-editor="notes"]. L'identifiant lui-même reste dans la zone de texte, donc getElementById continue de fonctionner.
1 2 3 4 | /* one editor only */ [data-editor="notes"] { --border-radius: 0; } |
🪟 Plein écran et superposition (z-index)
En plein écran, l'éditeur utilise z-index: 10000, qui efface les couches que la plupart des frameworks d'interface utilisateur réservent aux en-têtes fixes et aux superpositions. Si un en-tête ou une barre latérale fixe couvre toujours l'éditeur, augmentez cette valeur.
Remplacez-le par --mte-fullscreen-z-index. La valeur s'applique uniquement en plein écran et hérite, donc sa définition sur n'importe quel ancêtre couvre tous les éditeurs situés en dessous.
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 contenu
L'un des principaux atouts de MarkdownEditor est qu'il conserve le contenu sous-jacent. <textarea> parfaitement synchronisé. Que vous utilisiez un framework JavaScript moderne ou un backend traditionnel comme Django, PHP ou Laravel, le workflow reste simple et natif.
Lire et écrire du contenu
1. La manière autochtone (recommandé)
Étant donné que l'éditeur améliore une zone de texte standard, vous pouvez utiliser les méthodes DOM familières. C'est le moyen le plus rapide d'interagir avec vos données sans apprendre une nouvelle 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. Utilisation d'une référence de variable
Si vous avez une référence à l'élément textarea, vous pouvez l'utiliser directement : aucune API spécifique à la bibliothèque n'est nécessaire.
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. Définition du contenu initial côté serveur
La méthode recommandée pour définir le contenu initial est directement dans le <textarea> HTML : cela fonctionne naturellement avec tous les frameworks backend (Django, Laravel, Rails, PHP, etc.) et l'éditeur le restitue automatiquement à l'initialisation.
1 2 | <!-- Recommended: set content server-side --> <textarea id="markdown-editor"># Hello World</textarea> |
Pour lire ou mettre à jour le contenu au moment de l'exécution, utilisez le natif textarea valeur. Appel editor.render() après une mise à jour pour actualiser l'aperçu et la couche hybride.
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. Démonter l'éditeur : destroy()
Appel editor.destroy() pour supprimer le wrapper DOM de l'éditeur et restaurer l'original <textarea> à sa position dans le document. Utile dans les applications monopage lors du démontage d'une vue.
1 2 3 4 | const editor = new MarkdownEditor('#markdown-editor'); // Remove the editor and restore the plain textarea editor.destroy(); |
Réagir aux changements avec onChange
Passer un onChange rappel pour être averti de chaque changement de contenu. Reçoit la chaîne de démarque actuelle.
1 2 3 4 5 | const editor = new MarkdownEditor('#markdown-editor', { onChange(value) { console.log('Content changed:', value.length, 'characters'); } }); |
Brouillon de sauvegarde automatique avec localStorage
Utiliser onChange pour enregistrer un brouillon à chaque frappe. Restaurez-le en pré-remplissant la zone de texte avant d'initialiser l'éditeur.
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); }); |
Soumission du formulaire
Parce que MarkdownEditor est construit directement sur le natif <textarea>, il est compatible avec tous les frameworks backend (Django, Laravel, PHP, Ruby on Rails, etc.) dès la sortie de la boîte.
C'est là que brille la philosophie « Native-First ». Vous n'avez pas besoin de synchroniser manuellement les données avant de soumettre un formulaire. Le navigateur traite l'éditeur exactement comme un champ de saisie standard.
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> |
Note: Initialisation du plugin MarkdownEditor obligatoire
Utilisez simplement un HTML standard <form>. Le name L'attribut sur la zone de texte est ce que votre serveur utilisera pour identifier le contenu.
🚀 Pourquoi cela change la donne pour les backends
Puisque l'éditeur préserve le natif <textarea> comportement, votre serveur gère les données comme une chaîne standard. Aucune logique supplémentaire n'est requise : non preventDefault() et pas de manuel FormData construction.
💡 Pourquoi s'agit-il d'une « fonctionnalité qui tue » :
La plupart des éditeurs (comme Quill, Editor.js, simpleMDE, easyMDE) enregistrent les données dans des structures JSON complexes. Si un développeur les utilise, il doit réécrire son schéma de base de données et sa logique de rendu.
Avec MarkdownEditor, un développeur peut prendre un ancien site Web, remplacer un simple <textarea> avec votre éditeur, et le backend ne sait même pas que cela a changé. Il reçoit simplement le même texte brut qu’il a toujours reçu, mais l’utilisateur bénéficie d’une expérience 10 fois meilleure.
| Cadre / Langage | Comment accéder au contenu Markdown |
|---|---|
| PHP | $_POST['content'] |
| Django | request.POST.get('content') |
| Node.js (Express) | req.body.content |
| Laravel | $request->input('content') |
| Rubis sur Rails | params[:content] |
🖼️ Téléchargement d'images avancé
Gérer les téléchargements d'images de manière native : plutôt que de s'appuyer sur des chaînes Base64 lentes et gourmandes en mémoire, c'est un gain significatif à la fois en termes de performances et de référencement.
Coller et déposer
Collez une capture d'écran ou faites glisser un fichier image dans l'éditeur et il sera téléchargé via le même point de terminaison que le bouton de la barre d'outils. Un espace réservé apparaît immédiatement afin que l'éditeur ne semble jamais figé, et il est remplacé par le chemin réel une fois le téléchargement effectué. Si le téléchargement échoue, l'espace réservé est remplacé par un marqueur visible plutôt que de disparaître silencieusement.
Cela nécessite uploadUrl configuré ci-dessous. Sans cela, coller une image ne fait rien et l'éditeur enregistre un avertissement expliquant ce qu'il faut ajouter, car l'alternative serait une chaîne Base64 dans votre base de données.
Plusieurs fichiers sont téléchargés simultanément, de sorte que les images arrivent dans l'ordre dans lequel elles sont arrivées.
Options de configuration
L'outil d'image prend en charge un fileInput configuration pour gérer les téléchargements directs sur le serveur.
accept: Définissez un tableau de formats d'image autorisés (par exemple, 'webp', 'avif')uploadUrl: Spécifiez le point de terminaison back-end où leFilel'objet sera envoyé viaPOSTparams: Objet facultatif pour envoyer des données supplémentaires (telles que des jetons CSRF, des identifiants d'utilisateur ou des noms de dossier) à côté du fichier image
Exemple d'utilisation (configuration complète)
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); |
📡 Intégration du serveur
La demande
L'éditeur envoie un POST demande comme multipart/form-data. Par défaut, il comprend :
image_file: L'objet fichier réelimage_alt: Le texte alternatif saisi par l'utilisateur- ...plus toutes les données personnalisées définies dans le
paramsobjet
La réponse requise
Pour confirmer un téléchargement réussi et insérer l'image dans l'éditeur, votre serveur doit renvoyer la structure JSON suivante :
1 2 3 4 | { "success": true, "image_path": "https://cdn.yourdomain.com/uploads/image.webp" } |
Remarque : Assurez-vous d'utiliser la clé image_path pour l'URL de l'image téléchargée.
Validation du texte alternatif de l'image (altInput)
Pour garantir que votre contenu reste accessible et optimisé pour le référencement, MarkdownEditor applique la validation du texte alternatif par défaut.
- Comportement par défaut : si
altInputn'est pas défini, sa valeur par défaut est{ required: true } - Appliquer l'accessibilité : les utilisateurs ne pourront pas insérer d'image tant qu'une description alternative n'est pas fournie.
1. Par défaut (aucune configuration requise)
1 2 3 4 | // Alt text is REQUIRED by default image: { fileInput: { uploadUrl: '/api/upload' } } |
2. Raccourci (désactiver la validation)
Si vous souhaitez autoriser les images sans description, définissez simplement le booléen sur false.
1 2 3 | image: { altInput: false // Users can now skip the alt text field } |
3. Basé sur des objets (explicite)
1 2 3 4 5 | image: { altInput: { required: false // Disables alt text validation — users can skip the alt field } } |
Utilisation d'images standard (non fileInput)
Si fileInput n'est pas configuré, l'éditeur utilise par défaut un simple modal basé sur une URL. C’est idéal si vos utilisateurs établissent principalement des liens vers des hôtes d’images externes.
1 2 3 4 5 6 7 8 | const options = { toolbar: [ 'link', 'image', 'preview' ], } const editor = new MarkdownEditor('#markdown-editor', options); |
💡 Pourquoi utiliser des paramètres ?
Dans des frameworks comme Laravel ou Django, vous ne pouvez pas télécharger de fichiers sans jeton CSRF. En ajoutant _token au params object, votre demande passera par le middleware de sécurité du backend de manière transparente, en conservant la philosophie « Zero Logic » pour vos contrôleurs côté serveur.
⌨️ Raccourcis clavier
Les actions de formatage courantes peuvent être déclenchées directement depuis le clavier sans toucher à la barre d'outils. Chaque raccourci est également affiché dans l'info-bulle du bouton de la barre d'outils correspondant.
| Raccourci | Action |
|---|---|
Ctrl + B / ⌘ B | Basculer Gras |
Ctrl + I / ⌘ I | Basculer Italique |
Ctrl + K / ⌘ K | Insérer un lien |
Ctrl + ` / ⌘ ` | Basculer en ligne Code |
Ctrl + Shift + S / ⌘ ⇧ S | Basculer |
Ctrl + 1 / ⌘ 1 | Titre 1 |
Ctrl + 2 / ⌘ 2 | Titre 2 |
Ctrl + 3 / ⌘ 3 | Titre 3 |
Ctrl + L / ⌘ L | Basculer la liste à puces |
Ctrl + Z / ⌘ Z | Défaire |
Ctrl + Shift + Z / ⌘ ⇧ Z | Refaire |
Tab | Indenter les lignes sélectionnées |
Shift + Tab | Supprimer les lignes sélectionnées |
Ctrl + F / ⌘ F | Ouvrir le panneau Rechercher |
Ctrl + H / ⌘ H | Ouvrir le panneau Rechercher et remplacer |
Ctrl + Shift + F / ⌘ ⇧ F | Activer l'aperçu plein écran |
F11 | Activer l'aperçu plein écran |
Escape | Fermer le panneau Rechercher/Quitter l’aperçu plein écran |
🔍 Rechercher et remplacer
Un panneau de recherche et de remplacement intégré est disponible dans l'éditeur : aucune extension de navigateur ni outil séparé n'est nécessaire.
- Presse
Ctrl + F(ou⌘ F) pour ouvrir le panneau Rechercher - Presse
Ctrl + H(ou⌘ H) pour ouvrir le panneau Rechercher et remplacer - La recherche est insensible à la casse et affiche un compteur de correspondances en direct (par exemple 3 sur 12)
- Parcourez les matchs avec les boutons ▲ / ▼ ou
Enter/Shift + Enter - Remplacer remplace la correspondance actuelle en surbrillance ; Remplacer tout remplace chaque occurrence à la fois
- Presse
Escapepour fermer le panneau et redonner le focus à l'éditeur
Le panneau flotte dans le coin supérieur droit de la zone de contenu de l'éditeur et n'interrompt pas l'écriture.
🔗 Liens à partir d'une URL collée
Sélectionnez du texte, collez une URL dessus et vous obtenez un lien de démarque au lieu de l'URL remplaçant ce que vous avez sélectionné.
1 2 | Read the docs <- select "the docs", paste https://example.com Read [the docs](https://example.com) <- what you get |
La règle est volontairement stricte : seul un strict http ou https L'URL sans espace est traitée de cette façon. Coller une phrase contenant une URL, ou coller sans rien sélectionner, se comporte comme un collage ordinaire.
Caractéristiques
🔌 Intégration de formulaire natif
Fonctionne exactement comme un standard <textarea>. Pas d'API complexes : utilisez simplement le value ou name attribut. Cela "fonctionne simplement" avec les soumissions de formulaires HTML standard en PHP, Django ou Node.js.
🖼️ Téléchargement d'images avancé
Configurez les téléchargements de serveur natifs via l'API. Évitez les chaînes Base64 lourdes pour garantir des chargements de pages plus rapides et un référencement supérieur en hébergeant des images sur votre propre CDN.
🔀 Modes hybride et simple
Basculer entre un Hybride (WYSIWYG) expérience en montage visuel ou Démarquage simple mode pour une sensation de codage traditionnelle.
🚀 Hautes performances
UN 53 Ko compressés bundle (252 Ko minifiés, CSS inclus) optimisé pour le « contenu lourd ». Gère des documents volumineux et des fichiers volumineux sans aucun décalage d’entrée ni baisse de performances. Mises à jour d'aperçu anti-rebondies, calculs de style mis en cache et gestion du clavier sans conflit : Tab et Entrée font toujours exactement une chose.
🌍 Prise en charge RTL intégrée
Prise en charge native des langues de droite à gauche comme l'arabe, l'ourdou et le farsi. Parfait pour créer des applications accessibles à l’échelle mondiale.
✨ Mise en évidence de la syntaxe
Lisibilité améliorée avec un code clair et un formatage markdown.
🌙 Thématisation adaptative
Inclut la prise en charge automatique du mode sombre. Il se synchronise avec les paramètres de votre système ou avec le Interface utilisateur de Frutjam bibliothèque pour une expérience visuelle fluide.
📝 Édition intelligente
Suite automatique de liste de style GitHub pour les listes ordonnées, les listes non ordonnées et les listes de contrôle : appuyez sur Enter et l'éditeur continue le modèle. Les cases à cocher dans le volet d'aperçu sont cliquables et se synchronisent instantanément avec la source de démarque.
📱 Entièrement réactif
Une interface utilisateur fluide et mobile qui s'adapte parfaitement aux ordinateurs de bureau, aux tablettes et aux smartphones pour une édition en déplacement.
📦 Assistance universelle
Compatible avec ESM, UMD, CommonJS et IIFE. Fonctionne immédiatement via CDN (<script src>), npm ou tout autre bundler (Vite, webpack, Rollup) : aucune configuration supplémentaire n'est nécessaire.
♿ Accessible par défaut
Prise en charge complète d'ARIA intégrée : repère de la barre d'outils, zone d'aperçu étiquetée, boutons conviviaux pour les lecteurs d'écran, aria-pressed sur la bascule d'aperçu, disabled et aria-disabled sur les outils inactifs et restauration correcte de la mise au point à la fermeture des modaux.
🛡️ Zéro conflit CSS
Les styles d'éditeur sont entièrement adaptés à .markdown-editor-wrapper. Le contrôle en amont global de Tailwind est exclu afin que l'éditeur vive en toute sécurité aux côtés de Bootstrap, Tailwind ou de tout autre framework sans rompre leurs styles.
⌨️ Raccourcis clavier
Ctrl+B, Ctrl+I, Ctrl+K, Ctrl+`, Ctrl+Shift+S: actions de formatage courantes sans toucher la souris. Chaque raccourci est affiché dans l'info-bulle du bouton de la barre d'outils.
🔍 Rechercher et remplacer
Presse Ctrl+F pour trouver ou Ctrl+H pour ouvrir Rechercher et remplacer. Recherche insensible à la casse avec compteur de correspondances en direct, navigation suivant/précédent, remplacement unique et tout remplacement : sans quitter l'éditeur.
🔒 Aperçu sécurisé XSS
L'aperçu rendu est nettoyé via DOMPurifier avant d'être écrit dans le DOM. Les balises de script, les gestionnaires d'événements en ligne et les URL malveillantes dans les entrées de démarques spécialement conçues sont automatiquement supprimées : aucune configuration n'est requise.
▶️ Aperçu en temps réel
Voyez votre démarque rendue instantanément pendant que vous tapez.
🔗 Intégration facile
Intégrez-vous de manière transparente à n’importe quel projet Web avec une configuration minimale.
🛠️ Barre d'outils personnalisable
Configurez et réorganisez dynamiquement les options de la barre d'outils telles que le gras, l'italique, etc.
Exemple de configuration complète
Utilisez cet exemple complet pour initialiser MarkdownEditor avec toutes les fonctionnalités principales, y compris l'ordre des barres d'outils personnalisées et la gestion avancée du téléchargement d'images.
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 34 35 36 37 38 | 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'); }, labels: { 'Bold': 'Gras', 'Italic': 'Italique', 'Uploading...': 'Téléversement...', }, 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(); |
Vous avez trouvé cela utile ?
Une star de GitHub aide les autres développeurs à découvrir l'éditeur. Cela fait partie de Confiture de fruits. Une étoile là-bas aide aussi.