一个 Markdown 编辑器,可以增强您的文本区域而不是替换它,因此表单提交、验证和必填字段可以继续工作。所见即所得和纯 Markdown 模式、实时预览、查找和替换、RTL 支持、深色模式。可与 Django、Laravel、Rails、Node.js、PHP 和任何堆栈独立工作。
不需要 Frutjam 和 Tailwind。 样式是捆绑的,因此编辑器可以放入任何项目中。
安装
NPM(捆绑器:Vite、webpack、Rollup 等)
npm install markdown-text-editor |
1 2 | import MarkdownEditor from 'markdown-text-editor'; new MarkdownEditor('#markdown-editor'); |
TypeScript 定义随包一起提供,因此选项、工具栏条目和变量形状都会被检查并自动完成,无需额外安装。
CDN: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:全局脚本标签(IIFE)
无需导入:MarkdownEditor 可自动作为全局变量使用。
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 编辑器演示
快速入门
传递一个选项对象来自定义编辑器。所有选项都是可选的:省略任何选项即可使用默认值。
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'], }); |
理念:“本土第一”
大多数编辑器都打破了标准的网络工作流程。 MarkdownEditor 拥抱它。因为它直接位于 <textarea>,您不需要学习处理数据的新方法。
- 无需数据绑定: 适用于
<form method="POST">开箱即用 - 标准访问: 使用
document.getElementById('editor').value就像普通输入一样。 - 与后端无关: 可与任何后端(Python、Node.js、PHP 等)一起使用,就像普通表单字段一样
MarkdownEditor 与 EasyMDE / SimpleMDE
大多数 JavaScript Markdown 编辑器(EasyMDE、SimpleMDE、基于 CodeMirror 的编辑器)隐藏您的 <textarea> 并编辑副本,在提交表单时写回内容。这一直有效,直到其他东西需要该值为止: required 浏览器无法聚焦的字段会完全阻止提交,并且 .value 或者 FormData 在提交之前读取会返回一个空字符串,这会破坏 htmx、Turbo、自动保存和未保存更改防护。 MarkdownEditor 是不同的。 它对您已有的文本区域进行样式设置,并将其保留为您输入的字段,因此该值在任何时候都是正确的。
| 特征 | Markdown编辑器 | EasyMDE / SimpleMDE |
|---|---|---|
| 保留本机文本区域 | ✅ | ❌ 隐藏,编辑为副本 |
与 required 领域 | ✅ | ❌ 浏览器阻止提交 |
.value 提交前更正 | ✅ | ❌ 表单提交之前为空 |
连载于 FormData, htmx, 涡轮增压 | ✅ | ❌ 需要编辑器自己的API |
| 所见即所得混合模式 | ✅ | ❌ |
| 内置查找和替换 | ✅ | ❌ |
| RTL 支持 | ✅ 内置 | 通过 CodeMirror 的 direction 选项 |
| 内联事件处理程序 (CSP) | ✅ 无 | 一些 |
| 深色模式/主题 | ✅ | 有限的 |
| 捆绑包大小,gzip 压缩 | 53 KB | 107 KB(JS + CSS) |
框架整合
因为 MarkdownEditor 保留了原生 <textarea>,它与每个后端框架集成,无需任何额外代码。您的服务器接收 Markdown 内容,就像从任何标准表单字段接收一样。对于 React 和 Vue,它需要几行,在本节末尾介绍。
姜戈
添加一个 class 到您的文本区域小部件并初始化编辑器: request.POST['content'] 无需额外步骤即可工作。
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 |
拉维尔
使用 f.text_area 有一个类: $request->input('content') 直接接收markdown。
1 2 | <textarea name="content" class="markdown-editor">{{ old('content') }}</textarea> <script>new MarkdownEditor('.markdown-editor');</script> |
红宝石 on Rails
与 form_with 开箱即用: params[:content] 包含降价。对于涡轮驱动器,请使用 turbo:load 而不是 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 接收表单提交时的降价:没有同步步骤,没有自定义提取。
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'] 与任何标准文本区域完全相同:将其放入,您现有的表单处理需要零更改。
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> |
反应
在效果中创建编辑器并在卸载时销毁它。使用 defaultValue 而不是 value:编辑器直接写入文本区域,因此受控绑定将覆盖用户输入的内容。
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} />; } |
返回 editor.destroy() from theeffect 还涵盖了 StrictMode,它在开发过程中运行两次效果,否则会在一个文本区域上留下两个编辑器。空依赖数组是故意的:创建编辑器时会读取一次选项,因此重新运行效果会拆除它并在每次更改时重建它。
维埃
同样的想法。设置一次初始值 onMounted 并且不绑定 :value,或通过发送回的每个击键 v-model 会覆盖文本区域。
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> |
用作 <MarkdownField v-model="content" />。在简单的形式中,您可以跳过 v-model 与任何其他框架一样,完全并在提交时从文本区域读取值。
配置
您可以通过传递一个来完全自定义编辑器的行为和界面 options 目的。如果省略选项,则使用 默认 值。
构建编辑器时,选项将被读取一次。之后更改它们没有任何效果:调用 destroy() 并创建一个新的编辑器。
| 财产 | 类型 | 默认 | 目的 |
|---|---|---|---|
mode | string | 'plain' | 设置初始视图。使用混合以获得所见即所得的体验,或使用普通的原始语法。 |
placeholder | string | 'Write...' | 编辑器为空时显示的文本。 |
toolbar | array | [...] | 定义显示哪些工具以及显示顺序。 |
footer | 假| 对象 | 全部可见 | 控制编辑器下方显示的状态栏。设置为 false 完全隐藏它,或者传递一个对象来切换单个统计数据。 |
theme | string | inherited | 显式设置编辑器主题(浅色、深色、雪莓、黑莓)。如果省略,编辑器继承 data-theme 从最近的祖先元素或 <textarea> 本身。 |
minHeight | number | 200 | 当内容较短时,编辑器将缩小到的最小高度(以像素为单位)。配对 maxHeight 设置自动增长范围。 |
maxHeight | number | 500 | 编辑器在非全屏模式下可以增长到的最大高度(以像素为单位)。一旦内容超过此高度,编辑器内就会出现滚动条。该编辑器还有一个拖动手柄,因此用户可以手动将其大小调整到超出此限制。 |
renderer | function | marked | 替换用于预览的 Markdown 解析器。接收 markdown 字符串并且必须返回 HTML 字符串。 |
sanitizer | function | DOMPurify | 取代 HTML 清理程序。接收渲染的 HTML,并且必须返回安全的 HTML 来显示。 |
onChange | function | undefined | 每次内容更改时都会触发回调:键入、工具栏操作、撤消/重做和列表延续。接收当前的 markdown 字符串作为其唯一的参数。 |
🛠 工具栏定制
工具栏是模块化的。您可以通过修改阵列来创建最低限度的体验或功能齐全的电源套件。
可用工具
| 类别 | 工具键 |
|---|---|
| 版式 | heading, bold, italic, strikethrough, blockquote |
| 列表 | ul (bullet), ol (numbered), checklist |
| 代码 | code (排队), codeblock (围栏块) |
| 刀片 | hr (水平规则), table (表格模板) |
| 媒体 | link, image |
| 编辑 | undo, redo, indent, outdent |
| 看法 | preview |
| 模板 | { variables: [...] }, 内联配置 |
工具参考
| 工具 | 描述 |
|---|---|
heading | 打开下拉菜单以选择标题级别 H1–H6 |
bold | 启用粗体文本格式。 |
italic | 启用斜体文本格式。 |
strikethrough | 允许文本删除线。 |
ol | (有序列表):将文本转换为编号列表格式。 |
ul | (无序列表):将文本转换为项目符号列表。 |
checklist | 在文本中添加复选框,使其非常适合任务、待办事项列表或跟踪完成状态。 |
blockquote | 突出显示引用或强调的文本。 |
code | 将选定的文本包含在单个反引号中以实现内联代码。再次单击可删除反引号。 |
codeblock | 将选定的文本包装在三重反引号围栏代码块中。再次单击可移除栅栏。 |
hr | 插入一个 --- 水平线位于其自己的行上的光标位置。 |
table | 在光标位置插入起始 2x3 Markdown 表格模板。 |
image | 允许您通过 Markdown 语法插入图像。 |
link | 允许您向文本添加超链接。 |
undo | 撤销最后的更改。 |
redo | 重新应用上次撤消的更改。 |
indent | 增加缩进级别。 |
outdent | 降低压痕级别。 |
preview | 切换全屏并排预览。预览窗格中的复选框可单击并立即更新 Markdown 源。按 Esc 键退出全屏。如果固定标题覆盖全屏编辑器,请参阅 --mte-fullscreen-z-index. |
💡实施技巧:
- 重新排序:按钮按照您在数组中列出的确切顺序显示
- 删除:只需省略任何键(例如
image) 从数组中完全为用户禁用该功能 - 本机后备:如果您不提供
placeholder在 JS 中,插件会自动使用placeholder来自 HTML 的属性<textarea>
📊 页脚(状态栏)
页脚位于编辑器下方,显示光标的行、列、文档的字符数以及可选的字数,所有这些都是实时更新的。默认情况下它是可见的,并且每个统计数据都可以独立切换。
| 钥匙 | 类型 | 默认 | 描述 |
|---|---|---|---|
line | boolean | true | 显示当前行号。 |
col | boolean | true | 显示当前的列号。 |
chars | boolean | true | 显示总字符数。 |
words | boolean | false | 显示总字数。默认关闭:设置为 true 启用。 |
使用示例
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 } }); |
🔀 编辑模式
MarkdownEditor 提供两种不同的方式来编写和格式化内容。您可以在传统的以语法为中心的视图或现代的视觉优先体验之间切换。
plain(默认):一个干净、高性能的 Markdown 环境,其中语法(例如**bold**或者# heading) 可见。开发人员和 Markdown 纯粹主义者的理想选择hybrid:一种所见即所得的体验,可在您键入时实时呈现格式(粗体、斜体、标题),同时仍保持底层 Markdown 结构。
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' }); |
混合和普通模式预览:
混合模式
当您键入时,视觉格式会实时呈现。
普通模式(默认)
专注于原始 Markdown 语法以提供轻量级体验。
🌙 主题化
MarkdownEditor 自动从周围页面继承其主题:无需配置。编者读到 data-theme 从初始化时最近的祖先开始,因此它与您网站的开箱即用主题保持同步。
主题如何解决(优先顺序)
themeoption:在选项对象中传递的显式覆盖data-theme于<textarea>:直接在元素上设置data-theme任何祖先:例如<html>,<body>,或包装器<div>
可用主题
'light' (默认), 'dark', 'snowberry', 'darkberry'
选项1,继承自 <html> 或任何祖先(零配置)
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> |
选项 2:设置 data-theme 直接在 <textarea>
1 2 3 4 | <textarea id="markdown-editor" data-theme="dark"></textarea> <script> new MarkdownEditor('#markdown-editor'); </script> |
选项 3:显式 theme 选项(覆盖所有内容)
1 2 3 | new MarkdownEditor('#markdown-editor', { theme: 'dark' }); |
🎨 通过 CSS 变量自定义主题
您可以通过覆盖其 CSS 变量来完全自定义编辑器的外观 .markdown-editor-wrapper 元素或任何 [data-theme] 选择器。所有颜色均使用 OKLCH 色彩空间 以获得感知上统一的结果。
| 多变的 | 目的 | 浅色默认 | 深色默认 |
|---|---|---|---|
--color-base | 编辑背景 | oklch(100% 0 0) | oklch(10.9% 0 0) |
--color-on-base | 主要文本颜色 | oklch(22% 0 0) | oklch(98% 0 0) |
--color-primary | 主要重音(工具栏活动,链接) | oklch(51.1% .262 277) | oklch(66.4% .184 286) |
--color-on-primary | 原色表面上的文字 | oklch(96.2% .018 272) | oklch(10% .01 270) |
--color-secondary | 次要口音 | oklch(59.1% .293 323) | oklch(65% .18 220) |
--color-accent | 突出显示重音符号(内联代码,斜体) | oklch(54.1% .281 293) | oklch(75% .18 50) |
--color-neutral | 中性表面(边界、分隔线) | oklch(15% 0 0) | oklch(85% 0 0) |
--color-error | 错误状态颜色 | oklch(57.7% .245 27) | oklch(60% .22 30) |
--border-radius | 编辑器框架的圆角 | 0.25rem |
自定义主题示例
覆盖任何变量 .markdown-editor-wrapper 编辑器初始化后,或定义自定义 [data-theme] 样式表中的块:
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' }); |
🏷 变量
添加用于插入占位符的工具栏下拉列表。当编写文档的人不是定义占位符语法的开发人员时很有用,例如电子邮件、发票或合同模板:他们选择一个可读的名称,并为其插入正确的语法。
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' ] }); |
该工具是内联配置的 toolbar 数组,因此它在其他按钮中的位置取决于您。该按钮列出了标签,单击其中一个即可插入其标签 value 在光标处,替换任何选择。如果列表为空,则不会呈现任何内容。
预览中的示例值
给定一个条目 sample 在预览中显示该示例,而文本区域保留真实的占位符。没有其中之一的条目将按书面形式显示,因此您可以将两者混合使用。
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}} |
分组
给出一个条目 items 而不是 value 渲染一个有标题的部分。分组条目和平面条目可以混合在一个列表中。
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}}' } ]} ]} |
要知道的事情
- 编辑器从不解析变量。 它插入文本,然后您的应用程序通常在服务器端替换实际值。
sample仅影响预览显示的内容。 - 标签显示为纯文本,因此标签中的标记永远不会呈现。
- 格式错误的条目将被跳过而不是抛出,并且当未配置任何可用的内容时,根本不会呈现按钮。
🖋 自定义渲染器
预览呈现为 标记的 并用消毒 DOM纯化。两者都可以更换。当您的应用程序已经使用另一个库呈现 Markdown 并且您希望预览与生产完全匹配时,请使用此选项。
这两个选项都是普通函数,它们接受一个字符串并返回一个字符串,因此任何解析器和任何清理器都可以工作。
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 仍然在输出上运行,因此您无需配置任何内容即可保持 XSS 保护。
定制消毒液
仅当默认删除渲染器发出的内容时才需要。 DOMPurify 删除 <iframe> 默认情况下,因此视频嵌入需要明确允许。 DOMPurify 必须在您自己的代码中导入,因为与编辑器捆绑的副本是内部的。
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'] }) }); |
使用脚本标签而不是捆绑器,将 DOMPurify 与编辑器一起加载:
1 2 | <script src="https://cdn.jsdelivr.net/npm/dompurify"></script> <script src="https://cdn.jsdelivr.net/npm/markdown-text-editor"></script> |
要知道的事情
- 仅限预览。 混合模式的实时格式使用单独的内部渲染器,并且不受影响。
- **任务列表需要插件支持。**可通过查找找到可点击的复选框
input[type="checkbox"]在输出中,所以 markdown-it 需要markdown-it-task-lists。如果编辑器检测到任务列表语法且没有复选框,则会记录一条警告。 - 两者必须同步并返回一个字符串。一个
async函数写入[object Promise]进入预览。 - 更换消毒剂就取代了您的保护。 传递,例如
html => html完全禁用清理,并且仅对完全信任的内容是安全的。
🎨 设置内部元素的样式
CSS 变量涵盖了大多数主题,因为它们是继承的,因此设置一个 .markdown-editor-wrapper 到达工具栏、按钮、预览和页脚。仅当没有变量公开您需要的内容时才使用类名。
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); } |
类参考
这些类名对于样式来说是稳定且安全的。
| 班级 | 元素 |
|---|---|
.markdown-editor-wrapper | 包裹整个编辑器的外容器 |
.toolbar | 编辑区域上方的工具栏条 |
.markdown-btn | 单独的工具栏按钮 |
.preview-btn | 预览/全屏切换按钮 |
.editor-layout | 网格并排容纳编辑区域和预览 |
.textarea-wrapper | 编辑区域周围的包装 |
.editor-textarea | 底层文本区域元素 |
.display-layer | 渲染格式化图层,仅限混合模式 |
.preview-wrapper | 预览专栏 |
.preview-content | 在预览中渲染的 Markdown |
.editor-footer | 编辑器下方的状态栏 |
.find-replace-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); } |
定位一位编辑者
在一页上有多个编辑器时,将其中一个编辑器定位为 data-editor。包装器反映了它的 id <textarea>, 所以 <textarea id="notes"> 给你 [data-editor="notes"]。 id 本身保留在文本区域中,所以 getElementById 继续工作。
1 2 3 4 | /* one editor only */ [data-editor="notes"] { --border-radius: 0; } |
🪟 全屏和分层(z-index)
在全屏模式下,编辑器使用 z-index: 10000,这会清除大多数 UI 框架为固定标题和覆盖层保留的层。如果固定标题或侧边栏仍然覆盖编辑器,请提高此值。
覆盖它 --mte-fullscreen-z-index。该值仅适用于全屏,并且会继承,因此在任何祖先上设置它都会覆盖其下面的每个编辑器。
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
MarkdownEditor 的核心优势之一是它保留了底层 <textarea> 完美同步。无论您使用现代 JavaScript 框架还是传统后端(例如 Django、PHP 或 Laravel),工作流程都保持简单且原生。
阅读和写作内容
1.本土方式(推荐)
由于编辑器增强了标准文本区域,因此您可以使用熟悉的 DOM 方法。这是无需学习新 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.使用变量引用
如果您有对 textarea 元素的引用,则可以直接使用它:不需要特定于库的 API。
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.设置初始内容服务器端
设置初始内容的推荐方法是直接在 <textarea> HTML:这可以自然地与每个后端框架(Django、Laravel、Rails、PHP 等)配合使用,并且编辑器会在 init 上自动呈现它。
1 2 | <!-- Recommended: set content server-side --> <textarea id="markdown-editor"># Hello World</textarea> |
要在运行时读取或更新内容,请使用本机 textarea 价值。称呼 editor.render() 更新后刷新预览和混合层。
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.拆掉编辑器: destroy()
称呼 editor.destroy() 删除编辑器 DOM 包装并恢复原始状态 <textarea> 到其在文档中的位置。在单页应用程序中卸载视图时很有用。
1 2 3 4 | const editor = new MarkdownEditor('#markdown-editor'); // Remove the editor and restore the plain textarea editor.destroy(); |
对变化做出反应 onChange
通过一个 onChange 每次内容更改时都会收到回调通知。接收当前的 Markdown 字符串。
1 2 3 4 5 | const editor = new MarkdownEditor('#markdown-editor', { onChange(value) { console.log('Content changed:', value.length, 'characters'); } }); |
草稿自动保存 localStorage
使用 onChange 每次击键时保存草稿。在初始化编辑器之前通过预填充文本区域来恢复它。
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); }); |
表格提交
因为 MarkdownEditor 是直接构建在原生之上的 <textarea>,它开箱即用,与每个后端框架(Django、Laravel、PHP、Ruby on Rails 等)兼容。
这就是“本地优先”理念的闪光点。您无需在提交表单之前手动同步数据。浏览器将编辑器视为标准输入字段。
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> |
笔记: MarkdownEditor 插件必须初始化
只需使用标准 HTML <form>。这 name 文本区域上的属性是您的服务器将用来识别内容的属性。
🚀 为什么它会改变后端的游戏规则
由于编辑器保留了原生 <textarea> 行为,您的服务器将数据作为标准字符串处理。需要零额外逻辑:没有 preventDefault() 并且没有说明书 FormData 建造。
💡 为什么这是一个“杀手级功能”:
大多数编辑器(如 Quill、Editor.js、simpleMDE、easyMDE)以复杂的 JSON 结构保存数据。如果开发人员使用这些,他们必须重写他们的数据库模式和渲染逻辑。
使用 MarkdownEditor,开发人员可以使用旧网站,替换普通网站 <textarea> 与您的编辑器一起使用,后端甚至不知道它发生了变化。它只是收到与往常相同的原始文本,但用户获得了 10 倍更好的体验。
| 框架/语言 | 如何访问 Markdown 内容 |
|---|---|
| PHP | $_POST['content'] |
| 姜戈 | request.POST.get('content') |
| Node.js(Express) | req.body.content |
| 拉维尔 | $request->input('content') |
| 红宝石 on Rails | params[:content] |
🖼️ 高级图像上传
本地处理图像上传:而不是依赖缓慢、占用大量内存的 Base64 字符串,这对于性能和 SEO 来说都是一个重大胜利。
配置选项
图像工具支持 fileInput 配置来处理直接服务器上传。
accept:定义允许的图像格式数组(例如“webp”、“avif”)uploadUrl:指定后端端点File对象将通过发送POSTparams:可选对象,用于与图像文件一起发送附加数据(例如 CSRF 令牌、用户 ID 或文件夹名称)
使用示例(完整配置)
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); |
📡 服务器集成
请求
编辑发来一条 POST 请求为 multipart/form-data。默认情况下,它包括:
image_file: 实际的文件对象image_alt:用户输入的替代文本- ...加上中定义的任何自定义数据
params目的
所需的回应
要确认上传成功并将图像插入编辑器,您的服务器必须返回以下 JSON 结构:
1 2 3 4 | { "success": true, "image_path": "https://cdn.yourdomain.com/uploads/image.webp" } |
注意:确保您使用密钥 image_path 为上传图像的 URL。
图像替代文本验证(altInput)
为了确保您的内容保持可访问性和 SEO 友好性,MarkdownEditor 默认强制执行替代文本验证。
- 默认行为:如果
altInput没有定义,默认为{ required: true } - 强制可访问性:在提供替代说明之前,将阻止用户插入图像
1.默认(无需配置)
1 2 3 4 | // Alt text is REQUIRED by default image: { fileInput: { uploadUrl: '/api/upload' } } |
2.速记(禁用验证)
如果您想允许没有描述的图像,只需将布尔值设置为 false.
1 2 3 | image: { altInput: false // Users can now skip the alt text field } |
3.基于对象(显式)
1 2 3 4 5 | image: { altInput: { required: false // Disables alt text validation — users can skip the alt field } } |
标准图像使用(否 fileInput)
如果 fileInput 未配置时,编辑器默认为简单的基于 URL 的模式。如果您的用户主要链接到外部图像主机,那么这是理想的选择。
1 2 3 4 5 6 7 8 | const options = { toolbar: [ 'link', 'image', 'preview' ], } const editor = new MarkdownEditor('#markdown-editor', options); |
💡 为什么使用参数?
在 Laravel 或 Django 等框架中,如果没有 CSRF 令牌,则无法上传文件。通过添加 _token 到 params 对象,您的请求将无缝地通过后端的安全中间件,维护服务器端控制器的“零逻辑”理念。
⌨️ 键盘快捷键
常见的格式化操作可以直接从键盘触发,无需触摸工具栏。每个快捷方式也显示在相应的工具栏按钮的工具提示中。
| 捷径 | 行动 |
|---|---|
Ctrl + B / ⌘ B | 切换 粗体 |
Ctrl + I / ⌘ I | 切换斜体 |
Ctrl + K / ⌘ K | 插入链接 |
Ctrl + ` / ⌘ ` | 切换内联 Code |
Ctrl + Shift + S / ⌘ ⇧ S | 切换 |
Ctrl + 1 / ⌘ 1 | 标题 1 |
Ctrl + 2 / ⌘ 2 | 标题 2 |
Ctrl + 3 / ⌘ 3 | 标题 3 |
Ctrl + L / ⌘ L | 切换项目符号列表 |
Ctrl + Z / ⌘ Z | 撤消 |
Ctrl + Shift + Z / ⌘ ⇧ Z | 重做 |
Tab | 缩进选定的行 |
Shift + Tab | 减少缩进选定的行 |
Ctrl + F / ⌘ F | 打开查找面板 |
Ctrl + H / ⌘ H | 打开查找和替换面板 |
Ctrl + Shift + F / ⌘ ⇧ F | 切换全屏预览 |
F11 | 切换全屏预览 |
Escape | 关闭查找面板/退出全屏预览 |
🔍 查找和替换
编辑器内提供内置查找和替换面板:无需浏览器扩展或单独的工具。
- 按
Ctrl + F(或者⌘ F) 打开 查找 面板 - 按
Ctrl + H(或者⌘ H) 打开 查找和替换 面板 - 搜索不区分大小写并显示实时比赛计数器(例如 3 of 12)
- 使用 ▲ / ▼ 按钮导航匹配或
Enter/Shift + Enter - 替换 替换当前突出显示的匹配项; 全部替换 一次替换所有出现的内容
- 按
Escape关闭面板并将焦点返回到编辑器
该面板浮动在编辑器内容区域的右上角,不会中断写入。
特征
🔌 原生表单集成
工作原理与标准完全相同 <textarea>。没有复杂的 API:只需使用 value 或者 name 属性。它“只适用于”PHP、Django 或 Node.js 中的标准 HTML 表单提交。
🖼️ 高级图像上传
通过 API 配置本机服务器上传。通过在您自己的 CDN 上托管图像,避免使用繁重的 Base64 字符串,以确保更快的页面加载和卓越的 SEO。
🔀 混合模式和普通模式
之间切换 混合(所见即所得) 视觉编辑经验或 普通降价 传统编码感觉的模式。
🚀 高性能
一个 53 KB gzip 压缩 捆绑包(缩小为 252 KB,包含 CSS)针对“重内容”进行了优化。处理大量文档和大文件,不会出现任何输入延迟或性能下降。去抖预览更新、缓存样式计算和无冲突键盘处理:因此 Tab 和 Enter 始终只做一件事。
🌍 内置 RTL 支持
对阿拉伯语、乌尔都语和波斯语等从右到左语言的本机支持。非常适合构建全球可访问的应用程序。
✨ 语法高亮
通过清晰的代码和 Markdown 格式增强可读性。
🌙 自适应主题
包括自动深色模式支持。它与您的系统设置或 水果果酱用户界面 图书馆提供无缝的视觉体验。
📝 智能编辑
GitHub 风格的自动列表延续,适用于有序列表、无序列表和清单:按 Enter 编辑继续这个模式。预览窗格中的复选框可单击并立即同步回 Markdown 源。
📱 完全响应
流畅、移动优先的用户界面,完美适应台式机、平板电脑和智能手机,以便随时随地进行编辑。
📦 普遍支持
兼容于 ESM、UMD、CommonJS 和 IIFE。通过 CDN 开箱即用(<script src>)、npm 或任何捆绑器(Vite、webpack、Rollup):无需额外配置。
♿ 默认可访问
内置完整的 ARIA 支持:工具栏地标、带标签的预览区域、屏幕阅读器友好的按钮、 aria-pressed 在预览开关上, disabled 和 aria-disabled 在非活动工具上,并在模态关闭时正确恢复焦点。
🛡️ 零 CSS 冲突
编辑器样式完全适用于 .markdown-editor-wrapper。 Tailwind 的全局预检被排除,因此编辑器可以安全地与 Bootstrap、Tailwind 或任何其他框架一起使用,而不会破坏它们的风格。
⌨️ 键盘快捷键
Ctrl+B, Ctrl+I, Ctrl+K, Ctrl+`, Ctrl+Shift+S:无需触摸鼠标的常见格式化操作。每个快捷方式都显示在工具栏按钮的工具提示中。
🔍 查找和替换
按 Ctrl+F 寻找或 Ctrl+H 打开查找和替换。不区分大小写的搜索,具有实时匹配计数器、下一个/上一个导航、单个替换和全部替换:无需离开编辑器。
🔒 XSS 安全预览
渲染的预览通过以下方式进行清理 DOM纯化 在写入 DOM 之前。精心设计的 Markdown 输入中的脚本标签、内联事件处理程序和恶意 URL 都会被自动删除:无需配置。
▶️实时预览
在您键入时查看立即呈现的 Markdown。
🔗 轻松集成
只需最少的设置即可无缝集成到任何 Web 项目中。
🛠️ 可定制的工具栏
动态配置和重新排序工具栏选项,例如粗体、斜体等。
完整配置示例
使用这个综合示例来初始化 MarkdownEditor 的所有主要功能,包括自定义工具栏排序和高级图像上传处理。
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(); |
觉得这有用吗?
GitHub 之星可以帮助其他开发人员发现该编辑器。它是的一部分 弗鲁贾姆。那里的明星也有帮助。