Skip to main content

Thanh công cụ, bản xem trước trực tiếp và WYSIWYG trên vùng văn bản bạn đã có

Cập nhật lần cuối:

Trình chỉnh sửa đánh dấu giúp nâng cao vùng văn bản của bạn thay vì thay thế nó, nhờ đó việc gửi biểu mẫu, xác thực và các trường bắt buộc sẽ tiếp tục hoạt động. WYSIWYG và các chế độ Markdown đơn giản, xem trước trực tiếp, tìm và thay thế, hỗ trợ RTL, chế độ tối. Hoạt động độc lập với Django, Laravel, Rails, Node.js, PHP và bất kỳ ngăn xếp nào.

Không cần Frutjam và không cần Tailwind. Các kiểu được đóng gói sẵn nên người chỉnh sửa có thể tùy ý thực hiện bất kỳ dự án nào.

Cài đặt

NPM (các gói: Vite, webpack, Rollup, v.v.)

bash
npm install markdown-text-editor
javascript
1
2
import MarkdownEditor from 'markdown-text-editor';
new MarkdownEditor('#markdown-editor');

Các định nghĩa TypeScript đi kèm với gói, do đó các tùy chọn, mục thanh công cụ và hình dạng biến đổi được kiểm tra và tự động hoàn thành mà không cần cài đặt thêm.

CDN: mô-đun ES

html
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: thẻ tập lệnh toàn cầu (IIFE)

Không cần nhập: MarkdownEditor tự động có sẵn dưới dạng biến toàn cục.

html
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>

Bản trình diễn trình chỉnh sửa Markdown

Bắt đầu nhanh

Truyền một đối tượng tùy chọn để tùy chỉnh trình chỉnh sửa. Tất cả các tùy chọn đều là tùy chọn: bỏ qua bất kỳ tùy chọn nào để sử dụng giá trị mặc định.

javascript
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'],
});

Triết lý: "Bản địa đầu tiên"

Hầu hết các biên tập viên đều phá vỡ quy trình làm việc web tiêu chuẩn. MarkdownEditor đón nhận nó. Bởi vì nó nằm ngay trên đầu một <textarea>, bạn không cần phải học cách mới để xử lý dữ liệu.

  • Không cần ràng buộc dữ liệu: Hoạt động với <form method="POST"> ra khỏi hộp
  • Quyền truy cập tiêu chuẩn: Sử dụng document.getElementById('editor').value giống như một đầu vào bình thường.
  • Backend Agnostic: Hoạt động với mọi backend (Python, Node.js, PHP, v.v.) giống như trường biểu mẫu thông thường

MarkdownEditor so với EasyMDE/SimpleMDE

Hầu hết các trình soạn thảo đánh dấu JavaScript (EasyMDE, SimpleMDE, trình soạn thảo dựa trên CodeMirror) ẩn <textarea> và chỉnh sửa một bản sao, viết lại nội dung khi biểu mẫu được gửi. Điều đó hoạt động cho đến khi một cái gì đó khác cần giá trị: a required trường trình duyệt không thể tập trung chặn hoàn toàn việc gửi và .value hoặc FormData đọc trước khi gửi trả về một chuỗi trống, chuỗi này phá vỡ các bộ bảo vệ htmx, Turbo, tự động lưu và các thay đổi chưa được lưu. MarkdownEditor thì khác. Nó tạo kiểu cho vùng văn bản bạn đã có và giữ nguyên nó như trường bạn nhập vào, vì vậy giá trị luôn chính xác.

Tính năngTrình chỉnh sửa MarkdownEasyMDE / SimpleMDE
Vùng văn bản gốc được bảo tồn✅❌ Ẩn, chỉnh sửa dưới dạng bản sao
Hoạt động với required lĩnh vực✅❌ Trình duyệt chặn gửi
.value sửa trước khi gửi✅❌ Trống cho đến khi biểu mẫu được gửi
Nối tiếp với FormData, htmx, Turbo✅❌ Cần API riêng của người biên tập
Chế độ kết hợp WYSIWYG✅❌
Tích hợp Tìm & Thay thế✅❌
Hỗ trợ RTL✅ Tích hợp sẵnThông qua CodeMirror direction lựa chọn
Trình xử lý sự kiện nội tuyến (CSP)✅ Không cóMột số
Chế độ tối / chủ đề✅Giới hạn
Kích thước gói, được nén53 KB107 KB (JS + CSS)

Tích hợp khung

Bởi vì MarkdownEditor bảo tồn kiểu gốc <textarea>, nó tích hợp với mọi khung phụ trợ mà không cần bất kỳ mã bổ sung nào. Máy chủ của bạn nhận được nội dung đánh dấu chính xác như từ bất kỳ trường biểu mẫu tiêu chuẩn nào. Đối với React và Vue, cần có vài dòng, được trình bày ở cuối phần này.

Django

Thêm một class vào tiện ích vùng văn bản của bạn và khởi tạo trình chỉnh sửa: request.POST['content'] hoạt động không có bước bổ sung.

python
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'}),
        }
javascript
1
2
new MarkdownEditor('.markdown-editor');
// request.POST['content'] contains the markdown on submit

Laravel

Sử dụng f.text_area với một lớp: $request->input('content') nhận được sự giảm giá trực tiếp.

html
1
2
<textarea name="content" class="markdown-editor">{{ old('content') }}</textarea>
<script>new MarkdownEditor('.markdown-editor');</script>

Ruby trên đường ray

Hoạt động với form_with ra khỏi hộp: params[:content] chứa phần đánh dấu. Đối với Turbo Drive, hãy sử dụng turbo:load thay vì DOMContentLoaded.

javascript
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 nhận được mức giảm giá khi gửi biểu mẫu: không có bước đồng bộ hóa, không có trích xuất tùy chỉnh.

html
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'] hoạt động chính xác như với bất kỳ vùng văn bản tiêu chuẩn nào: thả nó vào và việc xử lý biểu mẫu hiện tại của bạn không cần thay đổi gì.

html
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>

Phản ứng

Tạo trình chỉnh sửa trong một hiệu ứng và hủy nó khi ngắt kết nối. Sử dụng defaultValue còn hơn là value: trình soạn thảo ghi trực tiếp vào vùng văn bản, do đó, liên kết được kiểm soát sẽ ghi đè lên nội dung người dùng đang nhập.

javascript
 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 &lt;textarea ref={ref} name={name} defaultValue={defaultValue} /&gt;;
}

Trở về editor.destroy() từ hiệu ứng này cũng bao gồm StrictMode, chạy các hiệu ứng hai lần trong quá trình phát triển và nếu không thì sẽ để lại hai trình soạn thảo trên một vùng văn bản. Mảng phụ thuộc trống là có chủ ý: các tùy chọn được đọc một lần khi trình soạn thảo được tạo, do đó, việc chạy lại hiệu ứng sẽ phá bỏ nó và xây dựng lại nó sau mỗi thay đổi.

Vue

Cùng một ý tưởng. Đặt giá trị ban đầu một lần trong onMounted và không ràng buộc :valuehoặc mỗi lần nhấn phím được phát ra qua v-model sẽ ghi đè lên vùng văn bản.

javascript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
&lt;script setup&gt;
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 =&gt; emit('update:modelValue', value)
    });
});

onBeforeUnmount(() =&gt; editor?.destroy());
&lt;/script&gt;

&lt;template&gt;&lt;textarea ref="el"&gt;&lt;/textarea&gt;&lt;/template&gt;

Được sử dụng như <MarkdownField v-model="content" />. Ở dạng đơn giản, bạn có thể bỏ qua v-model hoàn toàn và đọc giá trị từ vùng văn bản khi gửi, như với bất kỳ khung công tác nào khác.

Cấu hình

Bạn hoàn toàn có thể tùy chỉnh hành vi và giao diện của trình soạn thảo bằng cách chuyển một options sự vật. Nếu bạn bỏ qua một tùy chọn, giá trị mặc định sẽ được sử dụng.

Các tùy chọn được đọc một lần khi trình soạn thảo được xây dựng. Thay đổi chúng sau đó không có tác dụng: gọi destroy() và thay vào đó hãy tạo một trình soạn thảo mới.

Tài sảnKiểuMặc địnhMục đích
modestring'plain'Đặt chế độ xem ban đầu. Sử dụng kết hợp để có trải nghiệm WYSIWYG hoặc đơn giản cho cú pháp thô.
placeholderstring'Write...'Văn bản hiển thị khi trình chỉnh sửa trống.
toolbararray[...]Xác định những công cụ nào xuất hiện và theo thứ tự nào.
footersai | đối tượngtất cả có thể nhìn thấyĐiều khiển thanh trạng thái hiển thị bên dưới trình chỉnh sửa. Đặt thành false để ẩn nó hoàn toàn hoặc chuyển một đối tượng để chuyển đổi các số liệu thống kê riêng lẻ.
themestringinheritedĐặt rõ ràng chủ đề của trình chỉnh sửa (sáng, tối, dâu tuyết, dâu đen). Nếu bị bỏ qua, trình soạn thảo sẽ kế thừa data-theme từ phần tử tổ tiên gần nhất hoặc <textarea> chính nó.
minHeightnumber200Chiều cao tối thiểu tính bằng pixel mà trình chỉnh sửa sẽ thu nhỏ lại khi nội dung ngắn. Cặp với maxHeight để thiết lập phạm vi tự động phát triển.
maxHeightnumber500Chiều cao tối đa tính bằng pixel mà trình chỉnh sửa có thể tăng lên ở chế độ không toàn màn hình. Khi nội dung vượt quá chiều cao này, thanh cuộn sẽ xuất hiện bên trong trình chỉnh sửa. Trình chỉnh sửa cũng có tay cầm kéo để người dùng có thể thay đổi kích thước thủ công vượt quá giới hạn này.
rendererfunctionmarkedThay thế trình phân tích cú pháp đánh dấu được sử dụng để xem trước. Nhận chuỗi đánh dấu và phải trả về chuỗi HTML.
sanitizerfunctionDOMPurifyThay thế trình khử trùng HTML. Nhận HTML được hiển thị và phải trả về HTML an toàn để hiển thị.
onChangefunctionundefinedLệnh gọi lại được thực hiện khi có mọi thay đổi về nội dung: gõ, thao tác trên thanh công cụ, hoàn tác/làm lại và tiếp tục danh sách. Nhận chuỗi đánh dấu hiện tại làm đối số duy nhất của nó.
labelsobjectundefinedBản dịch cho giao diện riêng của người soạn thảo, được khóa bằng chuỗi tiếng Anh. Bất cứ điều gì bạn bỏ đi vẫn bằng tiếng Anh.

🛠 Tùy chỉnh thanh công cụ

Thanh công cụ có tính mô-đun. Bạn có thể tạo trải nghiệm tối thiểu hoặc bộ năng lượng đầy đủ tính năng bằng cách sửa đổi mảng.

Công cụ có sẵn

LoạiPhím công cụ
kiểu chữheading, bold, italic, strikethrough, blockquote
Danh sáchul (bullet), ol (numbered), checklist
Mã sốcode (nội tuyến), codeblock (khối có rào chắn)
Phụ tranghr (quy tắc ngang), table (mẫu bảng)
Phương tiện truyền thônglink, image
Chỉnh sửaundo, redo, indent, outdent
Xempreview
Mẫu{ variables: [...] }, được định cấu hình nội tuyến

Tham khảo công cụ

Dụng cụSự miêu tả
headingMở menu thả xuống để chọn cấp tiêu đề H1–H6
boldCho phép định dạng văn bản in đậm.
italicCho phép định dạng văn bản in nghiêng.
strikethroughCho phép gạch ngang văn bản.
ol(Danh sách có thứ tự): Chuyển văn bản sang dạng danh sách đánh số.
ul(Danh sách không có thứ tự): Chuyển văn bản thành danh sách dấu đầu dòng.
checklistThêm các hộp kiểm vào văn bản của bạn, giúp nó trở nên tuyệt vời cho các nhiệm vụ, danh sách việc cần làm hoặc theo dõi trạng thái hoàn thành.
blockquoteĐánh dấu văn bản được trích dẫn hoặc nhấn mạnh.
codeBao bọc văn bản đã chọn bằng dấu backticks đơn cho mã nội tuyến. Nhấp lại sẽ loại bỏ dấu tích ngược.
codeblockBao bọc văn bản đã chọn trong khối mã có hàng rào ba dấu ngược. Nhấp chuột một lần nữa sẽ loại bỏ hàng rào.
hrChèn một --- quy tắc ngang tại vị trí con trỏ trên dòng riêng của nó.
tableChèn mẫu bảng đánh dấu 2x3 ban đầu vào vị trí con trỏ.
imageCho phép bạn chèn hình ảnh thông qua cú pháp đánh dấu.
linkCho phép bạn thêm siêu liên kết vào văn bản của mình.
undoĐể đảo ngược những thay đổi cuối cùng.
redoĐể áp dụng lại những thay đổi được hoàn tác gần đây nhất.
indentĐể tăng mức độ thụt lề.
outdentĐể giảm mức độ thụt lề.
previewChuyển đổi chế độ xem trước toàn màn hình cạnh nhau. Các hộp kiểm trong khung xem trước có thể nhấp vào được và cập nhật nguồn đánh dấu ngay lập tức. Nhấn Escape để thoát toàn màn hình. Nếu tiêu đề cố định bao phủ trình chỉnh sửa ở chế độ toàn màn hình, hãy xem --mte-fullscreen-z-index.
💡 Mẹo triển khai:
  • Sắp xếp lại: Các nút xuất hiện theo đúng thứ tự bạn liệt kê trong mảng
  • Xóa: Chỉ cần bỏ qua bất kỳ phím nào (như image) khỏi mảng để tắt hoàn toàn tính năng đó cho người dùng
  • Dự phòng gốc: Nếu bạn không cung cấp placeholder trong JS, plugin sẽ tự động sử dụng placeholder thuộc tính từ HTML của bạn <textarea>

📊 Chân trang (Thanh trạng thái)

Chân trang nằm bên dưới trình chỉnh sửa và hiển thị dòng, cột của con trỏ, số ký tự của tài liệu và số từ tùy chọn, tất cả đều được cập nhật theo thời gian thực. Nó hiển thị theo mặc định và mỗi chỉ số có thể được chuyển đổi độc lập.

Chìa khóaKiểuMặc địnhSự miêu tả
linebooleantrueHiển thị số dòng hiện tại.
colbooleantrueHiển thị số cột hiện tại.
charsbooleantrueHiển thị tổng số ký tự.
wordsbooleanfalseHiển thị tổng số từ. Tắt theo mặc định: đặt thành true để kích hoạt.

Ví dụ sử dụng

javascript
 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 } });

🔀 Chế độ chỉnh sửa

MarkdownEditor cung cấp hai cách riêng biệt để viết và định dạng nội dung của bạn. Bạn có thể chuyển đổi giữa chế độ xem tập trung vào cú pháp truyền thống hoặc trải nghiệm hiện đại, ưu tiên hình ảnh.

  • plain (Mặc định): Môi trường Markdown rõ ràng, hiệu suất cao trong đó cú pháp (như **bold** hoặc # heading) có thể nhìn thấy được. Lý tưởng cho các nhà phát triển và những người theo chủ nghĩa thuần túy Markdown
  • hybrid: Trải nghiệm lấy cảm hứng từ WYSIWYG hiển thị định dạng (Đậm, Nghiêng, Tiêu đề) trong thời gian thực khi bạn nhập, trong khi vẫn duy trì cấu trúc Markdown cơ bản.
javascript
 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'
});

Xem trước chế độ kết hợp và đơn giản:

Chế độ kết hợp

Định dạng trực quan được hiển thị theo thời gian thực trong khi bạn nhập.

Chế độ đơn giản (Mặc định)

Tập trung vào cú pháp Markdown thô để có trải nghiệm nhẹ nhàng.

🌙 Chủ đề

MarkdownEditor tự động kế thừa chủ đề của nó từ trang xung quanh: không cần cấu hình. Biên tập viên đọc data-theme từ tổ tiên gần nhất khi khởi tạo, do đó, nó luôn đồng bộ với chủ đề trang web của bạn.

Cách giải quyết chủ đề (thứ tự ưu tiên)

  1. theme tùy chọn: ghi đè rõ ràng được truyền trong đối tượng tùy chọn
  2. data-theme trên <textarea>: đặt trực tiếp trên phần tử
  3. **data-theme trên bất kỳ tổ tiên nào **: ví dụ: <html>, <body>hoặc một trình bao bọc <div>

Chủ đề có sẵn

'light' (mặc định), 'dark', 'snowberry', 'darkberry'

Tùy chọn 1, kế thừa từ <html> hoặc bất kỳ tổ tiên nào (cấu hình không)
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>
Tùy chọn 2: đặt data-theme trực tiếp trên <textarea>
html
1
2
3
4
<textarea id="markdown-editor" data-theme="dark"></textarea>
<script>
  new MarkdownEditor('#markdown-editor');
</script>
Tùy chọn 3: rõ ràng theme tùy chọn (ghi đè mọi thứ)
javascript
1
2
3
new MarkdownEditor('#markdown-editor', {
    theme: 'dark'
});

🎨 Chủ đề tùy chỉnh thông qua các biến CSS

Bạn hoàn toàn có thể tùy chỉnh giao diện của trình soạn thảo bằng cách ghi đè các biến CSS của nó trên .markdown-editor-wrapper phần tử hoặc bất kỳ [data-theme] bộ chọn. Tất cả các màu đều sử dụng Không gian màu OKLCH cho kết quả thống nhất về mặt nhận thức.

BiếnMục đíchÁnh sáng mặc địnhMặc định tối
--color-baseNền soạn thảooklch(100% 0 0)oklch(10.9% 0 0)
--color-on-baseMàu văn bản chínhoklch(22% 0 0)oklch(98% 0 0)
--color-primaryGiọng chính (thanh công cụ đang hoạt động, liên kết)oklch(51.1% .262 277)oklch(66.4% .184 286)
--color-on-primaryVăn bản trên bề mặt màu cơ bảnoklch(96.2% .018 272)oklch(10% .01 270)
--color-secondaryGiọng phụoklch(59.1% .293 323)oklch(65% .18 220)
--color-accentĐánh dấu trọng âm (mã nội tuyến, in nghiêng)oklch(54.1% .281 293)oklch(75% .18 50)
--color-neutralBề mặt trung tính (viền, vách ngăn)oklch(15% 0 0)oklch(85% 0 0)
--color-errorMàu trạng thái lỗioklch(57.7% .245 27)oklch(60% .22 30)
--border-radiusLàm tròn góc của khung soạn thảo0.25rem
Ví dụ về chủ đề tùy chỉnh

Ghi đè bất kỳ biến nào trên .markdown-editor-wrapper sau khi trình soạn thảo khởi tạo hoặc xác định một tùy chỉnh [data-theme] chặn trong biểu định kiểu của bạn:

css
 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;
}
javascript
new MarkdownEditor('#markdown-editor', { theme: 'brand' });

🌐 Ngôn ngữ giao diện

Theo mặc định, văn bản, chú giải công cụ, mục menu, trường phương thức và nút của trình chỉnh sửa là tiếng Anh. Vượt qua một labels đối tượng dịch nó. Khóa là các chuỗi tiếng Anh, cách thức hoạt động của gettext, vì vậy bạn chỉ liệt kê những gì bạn muốn thay đổi và mọi thứ còn thiếu sẽ quay lại tiếng Anh.

javascript
 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...',
    },
});

Điều này quan trọng nhất bên cạnh nội dung từ phải sang trái. Tiếng Ả Rập, tiếng Urdu và tiếng Farsi được sắp xếp chính xác nhưng thanh công cụ vẫn đọc chữ "Đậm" trên văn bản từ phải sang trái chỉ được dịch một nửa.

🏷 Biến

Thêm thanh công cụ thả xuống để chèn phần giữ chỗ. Hữu ích khi người viết tài liệu không phải là nhà phát triển đã xác định cú pháp giữ chỗ, như với các mẫu email, hóa đơn hoặc hợp đồng: họ chọn một tên dễ đọc và cú pháp chính xác được chèn cho chúng.

javascript
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'
    ]
});

Công cụ này được cấu hình nội tuyến trong toolbar mảng, vì vậy vị trí của nó trong số các nút khác tùy thuộc vào bạn. Nút liệt kê các nhãn và việc nhấp vào một nhãn sẽ chèn nhãn đó value tại con trỏ, thay thế bất kỳ lựa chọn nào. Không có gì được hiển thị nếu danh sách trống.

Giá trị mẫu trong bản xem trước

Một mục được đưa ra một sample hiển thị mẫu đó trong bản xem trước, trong khi vùng văn bản giữ phần giữ chỗ thực. Các mục không có mục nào sẽ xuất hiện dưới dạng văn bản, vì vậy bạn có thể kết hợp cả hai.

javascript
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}}

Nhóm

Đưa ra một mục items thay vì một value để hiển thị một phần có tiêu đề. Các mục được nhóm và phẳng có thể được trộn lẫn trong một danh sách.

javascript
 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}}' }
    ]}
]}

Những điều cần biết

  • Trình chỉnh sửa không bao giờ giải quyết các biến. Nó chèn văn bản và ứng dụng của bạn sẽ thay thế các giá trị thực sau đó, thường là phía máy chủ. sample chỉ ảnh hưởng đến những gì bản xem trước hiển thị.
  • Nhãn được hiển thị dưới dạng văn bản thuần túy, do đó, đánh dấu trong nhãn không bao giờ được hiển thị.
  • Các mục không đúng định dạng sẽ bị bỏ qua thay vì ném và nút này hoàn toàn không được hiển thị khi không có cấu hình nào có thể sử dụng được.

🖋 Trình kết xuất tùy chỉnh

Bản xem trước được hiển thị với đánh dấu và khử trùng bằng DOMThanh lọc. Cả hai đều có thể được thay thế. Sử dụng tính năng này khi ứng dụng của bạn đã hiển thị đánh dấu với một thư viện khác và bạn muốn bản xem trước khớp chính xác với bản sản xuất.

Cả hai tùy chọn đều là các hàm đơn giản nhận một chuỗi và trả về một chuỗi, do đó, mọi trình phân tích cú pháp và bất kỳ trình khử trùng nào đều hoạt động.

javascript
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 vẫn chạy ở đầu ra, vì vậy bạn tiếp tục bảo vệ XSS mà không cần định cấu hình bất cứ thứ gì.

chất khử trùng tùy chỉnh

Chỉ cần thiết khi mặc định loại bỏ thứ gì đó mà trình kết xuất của bạn phát ra. DOMPurify loại bỏ <iframe> theo mặc định, vì vậy việc nhúng video cần được cho phép rõ ràng. DOMPurify phải được nhập bằng mã của riêng bạn vì bản sao đi kèm với trình chỉnh sửa là nội bộ.

javascript
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']
    })
});

Sử dụng thẻ script thay vì bộ đóng gói, tải DOMPurify cùng với trình chỉnh sửa:

html
1
2
<script src="https://cdn.jsdelivr.net/npm/dompurify"></script>
<script src="https://cdn.jsdelivr.net/npm/markdown-text-editor"></script>

Những điều cần biết

  • Chỉ xem trước. Định dạng trực tiếp của chế độ kết hợp sử dụng trình kết xuất bên trong riêng biệt và không bị ảnh hưởng.
  • Danh sách nhiệm vụ cần hỗ trợ plugin. Tìm thấy các hộp kiểm có thể nhấp bằng cách tìm kiếm input[type="checkbox"] ở đầu ra, vì vậy markdown-nó cần markdown-it-task-lists. Trình chỉnh sửa sẽ ghi lại cảnh báo nếu phát hiện cú pháp danh sách nhiệm vụ và không có hộp kiểm.
  • Cả hai phải đồng bộ và trả về một chuỗi. MỘT async chức năng viết [object Promise] vào bản xem trước.
  • Thay thế chất khử trùng sẽ thay thế sự bảo vệ của bạn. Việc lây truyền như html => html vô hiệu hóa hoàn toàn quá trình khử trùng và chỉ an toàn cho nội dung hoàn toàn đáng tin cậy.

🎨 Tạo kiểu cho các yếu tố bên trong

Các biến CSS bao gồm hầu hết các chủ đề vì chúng kế thừa, do đó, hãy đặt một biến trên .markdown-editor-wrapper đến thanh công cụ, nút, xem trước và chân trang. Chỉ liên hệ với tên lớp khi không có biến nào hiển thị những gì bạn cần.

css
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);
}

Tham khảo lớp

Những tên lớp này ổn định và an toàn để tạo kiểu.

Lớp họcYếu tố
.markdown-editor-wrapperVùng chứa bên ngoài bao bọc toàn bộ trình soạn thảo
.toolbarDải thanh công cụ phía trên khu vực chỉnh sửa
.markdown-btnNút thanh công cụ riêng lẻ
.preview-btnNút chuyển đổi xem trước/toàn màn hình
.editor-layoutLưới giữ vùng chỉnh sửa và xem trước cạnh nhau
.textarea-wrapperBao bọc xung quanh khu vực chỉnh sửa
.editor-textareaPhần tử vùng văn bản cơ bản
.display-layerLớp định dạng được hiển thị, chỉ ở chế độ kết hợp
.preview-wrapperCột xem trước
.preview-contentĐánh dấu được hiển thị bên trong bản xem trước
.editor-footerThanh trạng thái bên dưới trình chỉnh sửa
.find-replace-panelBảng tìm kiếm và thay thế
css
 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);
}

Nhắm mục tiêu một biên tập viên

Với nhiều người chỉnh sửa trên một trang, hãy nhắm mục tiêu một trong số họ bằng data-editor. Trình bao bọc phản ánh id của nó <textarea>, Vì thế <textarea id="notes"> mang lại cho bạn [data-editor="notes"]. Bản thân id vẫn nằm trên vùng văn bản, vì vậy getElementById tiếp tục làm việc.

css
1
2
3
4
/* one editor only */
[data-editor="notes"] {
    --border-radius: 0;
}

🪟 Toàn màn hình & Phân lớp (z-index)

Ở chế độ toàn màn hình, trình soạn thảo sử dụng z-index: 10000, thao tác này sẽ xóa các lớp mà hầu hết các khung giao diện người dùng dành riêng cho các tiêu đề và lớp phủ cố định. Nếu tiêu đề hoặc thanh bên cố định vẫn bao phủ trình chỉnh sửa, hãy tăng giá trị này.

Ghi đè nó bằng --mte-fullscreen-z-index. Giá trị chỉ áp dụng ở chế độ toàn màn hình và nó kế thừa, do đó, việc đặt nó trên bất kỳ tổ tiên nào sẽ bao gồm mọi trình soạn thảo bên dưới nó.

css
 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 nội dung

Một trong những điểm mạnh cốt lõi của MarkdownEditor là nó giữ được các yếu tố cơ bản <textarea> được đồng bộ hóa hoàn hảo. Cho dù bạn đang sử dụng khung JavaScript hiện đại hay chương trình phụ trợ truyền thống như Django, PHP hay Laravel, quy trình làm việc vẫn đơn giản và nguyên bản.

Đọc và viết nội dung

1. Cách bản địa (Được khuyến nghị)

Vì trình chỉnh sửa nâng cao vùng văn bản tiêu chuẩn nên bạn có thể sử dụng các phương thức DOM quen thuộc. Đây là cách nhanh nhất để tương tác với dữ liệu của bạn mà không cần tìm hiểu API mới.

javascript
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. Sử dụng tham chiếu biến

Nếu bạn có tham chiếu đến phần tử vùng văn bản, bạn có thể sử dụng trực tiếp: không cần API dành riêng cho thư viện.

javascript
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. Đặt phía máy chủ nội dung ban đầu

Cách được đề xuất để đặt nội dung ban đầu là trực tiếp trong <textarea> HTML: tính năng này hoạt động tự nhiên với mọi khung công tác phụ trợ (Django, Laravel, Rails, PHP, v.v.) và trình soạn thảo sẽ tự động hiển thị nó trên init.

html
1
2
<!-- Recommended: set content server-side -->
<textarea id="markdown-editor"># Hello World</textarea>

Để đọc hoặc cập nhật nội dung trong thời gian chạy, hãy sử dụng kiểu gốc textarea giá trị. Gọi editor.render() sau khi cập nhật để làm mới lớp xem trước và lớp kết hợp.

javascript
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. Xé trình soạn thảo: destroy()

Gọi editor.destroy() để xóa trình bao bọc DOM của trình soạn thảo và khôi phục bản gốc <textarea> vào vị trí của nó trong tài liệu. Hữu ích trong các ứng dụng một trang khi ngắt kết nối một chế độ xem.

javascript
1
2
3
4
const editor = new MarkdownEditor('#markdown-editor');

// Remove the editor and restore the plain textarea
editor.destroy();

Phản ứng với những thay đổi bằng onChange

Vượt qua một onChange gọi lại để được thông báo về mọi thay đổi nội dung. Nhận chuỗi đánh dấu hiện tại.

javascript
1
2
3
4
5
const editor = new MarkdownEditor('#markdown-editor', {
    onChange(value) {
        console.log('Content changed:', value.length, 'characters');
    }
});

Tự động lưu bản nháp với localStorage

Sử dụng onChange để lưu bản nháp sau mỗi lần nhấn phím. Khôi phục nó bằng cách điền trước vùng văn bản trước khi khởi tạo trình chỉnh sửa.

javascript
 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);
});

Gửi biểu mẫu

Vì MarkdownEditor được xây dựng trực tiếp trên mã gốc <textarea>, nó tương thích với mọi khung công tác phụ trợ (Django, Laravel, PHP, Ruby on Rails, v.v.) ngay lập tức.

Đây là nơi triết lý "Bản địa là trên hết" tỏa sáng. Bạn không cần phải đồng bộ hóa dữ liệu theo cách thủ công trước khi gửi biểu mẫu. Trình duyệt xử lý trình chỉnh sửa chính xác như trường nhập tiêu chuẩn.

html
 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>

Ghi chú: Bắt buộc khởi tạo plugin MarkdownEditor

Chỉ cần sử dụng HTML tiêu chuẩn <form>. các name thuộc tính trên vùng văn bản là thuộc tính mà máy chủ của bạn sẽ sử dụng để xác định nội dung.

🚀 Tại sao nó là công cụ thay đổi cuộc chơi cho phần phụ trợ

Vì trình soạn thảo giữ nguyên bản gốc <textarea> hành vi, máy chủ của bạn xử lý dữ liệu dưới dạng chuỗi tiêu chuẩn. Không cần thêm logic nào: không preventDefault() và không có hướng dẫn sử dụng FormData sự thi công.

💡 Tại sao đây là "Tính năng sát thủ":

Hầu hết các trình soạn thảo (như Quill, Editor.js, simpleMDE, easyMDE) đều lưu dữ liệu ở dạng cấu trúc JSON phức tạp. Nếu nhà phát triển sử dụng những thứ đó, họ phải viết lại lược đồ cơ sở dữ liệu và logic kết xuất của họ.

Với MarkdownEditor, nhà phát triển có thể lấy một trang web cũ, thay thế một trang web đơn giản <textarea> bằng trình chỉnh sửa của bạn và phụ trợ thậm chí không biết nó đã thay đổi. Nó chỉ nhận được văn bản thô giống như mọi khi nhưng người dùng sẽ có được trải nghiệm tốt hơn gấp 10 lần.

Khung/Ngôn ngữCách truy cập nội dung Markdown
PHP$_POST['content']
Djangorequest.POST.get('content')
Node.js (Express)req.body.content
Laravel$request->input('content')
Ruby trên đường rayparams[:content]

🖼️ Tải lên hình ảnh nâng cao

Xử lý nguyên bản tải lên hình ảnh: thay vì dựa vào các chuỗi Base64 chậm và tốn nhiều bộ nhớ, đây là một lợi thế đáng kể cho cả hiệu suất và SEO.

Dán và thả

Dán ảnh chụp màn hình hoặc kéo tệp hình ảnh vào trình chỉnh sửa và tải lên thông qua cùng điểm cuối với nút thanh công cụ. Một trình giữ chỗ xuất hiện ngay lập tức để trình chỉnh sửa không bao giờ bị đóng băng và được thay thế bằng đường dẫn thực sau khi quá trình tải lên hoàn tất. Nếu tải lên không thành công, trình giữ chỗ sẽ được thay thế bằng điểm đánh dấu hiển thị thay vì biến mất một cách im lặng.

Điều này cần uploadUrl được cấu hình bên dưới. Nếu không có nó, việc dán hình ảnh sẽ không có tác dụng gì và trình chỉnh sửa sẽ ghi lại cảnh báo giải thích nội dung cần thêm vì giải pháp thay thế sẽ là chuỗi Base64 trong cơ sở dữ liệu của bạn.

Một số tệp được tải lên cùng lúc, do đó hình ảnh sẽ xuất hiện theo thứ tự xuất hiện.

Tùy chọn cấu hình

Công cụ hình ảnh hỗ trợ fileInput cấu hình để xử lý tải lên máy chủ trực tiếp.

  • accept: Xác định một loạt các định dạng hình ảnh được phép (ví dụ: 'webp', 'avif')
  • uploadUrl: Chỉ định điểm cuối phụ trợ nơi File đối tượng sẽ được gửi qua POST
  • params: Đối tượng tùy chọn để gửi dữ liệu bổ sung (như mã thông báo CSRF, ID người dùng hoặc tên thư mục) cùng với tệp hình ảnh

Ví dụ sử dụng (Cấu hình đầy đủ)

javascript
 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);

📡 Tích hợp máy chủ

Yêu cầu

Biên tập viên gửi một POST yêu cầu như multipart/form-data. Theo mặc định, nó bao gồm:

  • image_file: Đối tượng tập tin thực tế
  • image_alt: Văn bản thay thế được người dùng nhập vào
  • ...cộng với mọi dữ liệu tùy chỉnh được xác định trong params sự vật

Phản hồi bắt buộc

Để xác nhận tải lên thành công và chèn hình ảnh vào trình chỉnh sửa, máy chủ của bạn phải trả về cấu trúc JSON sau:

json
1
2
3
4
{
  "success": true,
  "image_path": "https://cdn.yourdomain.com/uploads/image.webp"
}

Lưu ý: Đảm bảo bạn sử dụng chìa khóa image_path cho URL của hình ảnh được tải lên.

Xác thực văn bản thay thế hình ảnh (altInput)

Để đảm bảo nội dung của bạn vẫn có thể truy cập được và thân thiện với SEO, MarkdownEditor thực thi xác thực văn bản thay thế theo mặc định.

  • Hành vi mặc định: Nếu altInput không được xác định, nó mặc định là { required: true }
  • Thực thi khả năng truy cập: Người dùng sẽ bị ngăn chèn hình ảnh cho đến khi cung cấp mô tả thay thế

1. Mặc định (Không cần cấu hình)

javascript
1
2
3
4
// Alt text is REQUIRED by default
image: {
  fileInput: { uploadUrl: '/api/upload' }
}

2. Viết tắt (Tắt xác thực)

Nếu bạn muốn cho phép hình ảnh không có mô tả, chỉ cần đặt boolean thành false.

javascript
1
2
3
image: {
  altInput: false // Users can now skip the alt text field
}

3. Dựa trên đối tượng (Rõ ràng)

javascript
1
2
3
4
5
image: {
    altInput: {
        required: false // Disables alt text validation — users can skip the alt field
    }
}

Sử dụng hình ảnh tiêu chuẩn (Không fileInput)

Nếu như fileInput không được định cấu hình, trình soạn thảo sẽ mặc định sử dụng phương thức dựa trên URL đơn giản. Điều này lý tưởng nếu người dùng của bạn chủ yếu liên kết đến các máy chủ hình ảnh bên ngoài.

javascript
1
2
3
4
5
6
7
8
const options = {
  toolbar: [
    'link',
    'image',
    'preview'
  ],
}
const editor = new MarkdownEditor('#markdown-editor', options);

💡 Tại sao nên sử dụng thông số?

Trong các framework như Laravel hoặc Django, bạn không thể tải tệp lên mà không có mã thông báo CSRF. Bằng cách thêm _token đến params object, yêu cầu của bạn sẽ chuyển qua phần mềm trung gian bảo mật của chương trình phụ trợ một cách liền mạch, duy trì triết lý "Zero Logic" cho bộ điều khiển phía máy chủ của bạn.

⌨️ Phím tắt

Các hành động định dạng phổ biến có thể được kích hoạt trực tiếp từ bàn phím mà không cần chạm vào thanh công cụ. Mỗi phím tắt cũng được hiển thị trong chú giải công cụ của nút thanh công cụ tương ứng.

Phím tắtHoạt động
Ctrl + B  /  ⌘ BChuyển đổi Đậm
Ctrl + I  /  ⌘ IChuyển đổi nghiêng
Ctrl + K  /  ⌘ KChèn liên kết
Ctrl + `  /  ⌘ `Chuyển đổi nội tuyến Code
Ctrl + Shift + S  /  ⌘ ⇧ SChuyển đổi Gạch ngang
Ctrl + 1  /  ⌘ 1Tiêu đề 1
Ctrl + 2  /  ⌘ 2Tiêu đề 2
Ctrl + 3  /  ⌘ 3Tiêu đề 3
Ctrl + L  /  ⌘ LChuyển đổi danh sách dấu đầu dòng
Ctrl + Z  /  ⌘ ZHoàn tác
Ctrl + Shift + Z  /  ⌘ ⇧ ZLàm lại
TabThụt lề các dòng đã chọn
Shift + TabNhô ra các dòng đã chọn
Ctrl + F  /  ⌘ FMở bảng Tìm kiếm
Ctrl + H  /  ⌘ HMở bảng Tìm & Thay thế
Ctrl + Shift + F  /  ⌘ ⇧ FChuyển đổi xem trước toàn màn hình
F11Chuyển đổi xem trước toàn màn hình
EscapeĐóng Tìm bảng điều khiển/Thoát xem trước toàn màn hình

🔍 Tìm & Thay thế

Bảng điều khiển tìm và thay thế tích hợp sẵn có sẵn bên trong trình chỉnh sửa: không cần tiện ích mở rộng trình duyệt hoặc công cụ riêng biệt.

  • Nhấn Ctrl + F (hoặc ⌘ F) để mở bảng Tìm
  • Nhấn Ctrl + H (hoặc ⌘ H) để mở bảng Tìm & Thay thế
  • Tìm kiếm không phân biệt chữ hoa chữ thường và hiển thị bộ đếm trận đấu trực tiếp (ví dụ: 3 trên 12)
  • Điều hướng các kết quả khớp bằng các nút ▲ / ▼ hoặc Enter / Shift + Enter
  • Thay thế thay thế kết quả được đánh dấu hiện tại; Thay thế tất cả thay thế mọi lần xuất hiện cùng một lúc
  • Nhấn Escape để đóng bảng điều khiển và trả lại tiêu điểm cho trình chỉnh sửa

Bảng điều khiển nổi ở góc trên bên phải của khu vực nội dung trình soạn thảo và không làm gián đoạn quá trình viết.

🔗 Liên kết từ một URL được dán

Chọn một số văn bản, dán URL lên nó và bạn sẽ nhận được liên kết đánh dấu thay vì URL thay thế những gì bạn đã chọn.

markdown
1
2
Read the docs                        <- select "the docs", paste https://example.com
Read [the docs](https://example.com) <- what you get

Quy tắc này được cố tình nghiêm ngặt: chỉ trần trụi http hoặc https URL không có khoảng trắng được xử lý theo cách này. Dán một câu có chứa URL hoặc dán mà không chọn gì sẽ hoạt động giống như dán thông thường.

Đặc trưng

🔌 Tích hợp biểu mẫu gốc

Hoạt động chính xác như một tiêu chuẩn <textarea>. Không có API phức tạp: chỉ cần sử dụng value hoặc name thuộc tính. Nó "chỉ hoạt động" với việc gửi biểu mẫu HTML tiêu chuẩn bằng PHP, Django hoặc Node.js.

🖼️ Tải lên hình ảnh nâng cao

Định cấu hình tải lên máy chủ gốc thông qua API. Tránh các chuỗi Base64 nặng để đảm bảo tải trang nhanh hơn và SEO vượt trội bằng cách lưu trữ hình ảnh trên CDN của riêng bạn.

🔀 Chế độ lai & trơn

Chuyển đổi giữa một Lai (WYSIWYG) kinh nghiệm chỉnh sửa hình ảnh hoặc Đánh dấu đơn giản chế độ cho cảm giác mã hóa truyền thống.

🚀 Hiệu suất cao

MỘT 53 KB được nén gói (được rút gọn 252 KB, bao gồm CSS) được tối ưu hóa cho "Nội dung nặng". Xử lý các tài liệu lớn và các tệp lớn mà không có bất kỳ độ trễ đầu vào hoặc giảm hiệu suất nào. Các bản cập nhật xem trước đã được gỡ bỏ, tính toán kiểu được lưu trong bộ nhớ đệm và xử lý bàn phím không xung đột: vì vậy Tab và Enter luôn thực hiện chính xác một việc.

🌍 Hỗ trợ RTL tích hợp

Hỗ trợ gốc cho các ngôn ngữ từ phải sang trái như tiếng Ả Rập, tiếng Urdu và tiếng Farsi. Hoàn hảo để xây dựng các ứng dụng có thể truy cập toàn cầu.

✨ Đánh dấu cú pháp

Nâng cao khả năng đọc với mã rõ ràng và định dạng đánh dấu.

🌙 Chủ đề thích ứng

Bao gồm hỗ trợ Chế độ tối tự động. Nó đồng bộ với cài đặt hệ thống của bạn hoặc Giao diện người dùng Frutjam thư viện để có trải nghiệm hình ảnh liền mạch.

📝 Chỉnh sửa thông minh

Tiếp tục danh sách tự động theo kiểu GitHub cho danh sách có thứ tự, danh sách không có thứ tự và danh sách kiểm tra: nhấn Enter và trình soạn thảo tiếp tục mô hình. Các hộp kiểm trong khung xem trước có thể nhấp vào được và đồng bộ hóa trở lại nguồn đánh dấu ngay lập tức.

📱 Hoàn toàn đáp ứng

Giao diện người dùng linh hoạt, ưu tiên thiết bị di động, thích ứng hoàn hảo với máy tính để bàn, máy tính bảng và điện thoại thông minh để chỉnh sửa khi đang di chuyển.

📦 Hỗ trợ toàn cầu

Tương thích với ESM, UMD, CommonJS và IIFE. Hoạt động tốt thông qua CDN (<script src>), npm hoặc bất kỳ gói nào (Vite, webpack, Rollup): không cần cấu hình bổ sung.

♿ Có thể truy cập theo mặc định

Hỗ trợ ARIA đầy đủ được tích hợp sẵn: mốc thanh công cụ, vùng xem trước được gắn nhãn, các nút thân thiện với trình đọc màn hình, aria-pressed trên nút chuyển đổi xem trước, disabled Và aria-disabled trên các công cụ không hoạt động và khôi phục tiêu điểm chính xác khi các chế độ đóng.

🛡️ Không có xung đột CSS

Các phong cách soạn thảo hoàn toàn nằm trong phạm vi .markdown-editor-wrapper. Tính năng kiểm tra trước toàn cầu của Tailwind bị loại trừ nên trình soạn thảo hoạt động an toàn cùng với Bootstrap, Tailwind hoặc bất kỳ khung nào khác mà không phá vỡ phong cách của chúng.

⌨️ Phím tắt

Ctrl+B, Ctrl+I, Ctrl+K, Ctrl+`, Ctrl+Shift+S: các thao tác định dạng phổ biến mà không cần chạm vào chuột. Mỗi phím tắt được hiển thị trong chú giải công cụ của nút thanh công cụ.

🔍 Tìm & Thay thế

Nhấn Ctrl+F để tìm hoặc Ctrl+H để mở tìm & thay thế. Tìm kiếm không phân biệt chữ hoa chữ thường với bộ đếm so khớp trực tiếp, điều hướng tiếp theo/trước, thay thế một lần và thay thế tất cả: mà không cần rời khỏi trình chỉnh sửa.

🔒 Xem trước an toàn XSS

Bản xem trước được hiển thị được làm sạch thông qua DOMThanh lọc trước khi được ghi vào DOM. Thẻ tập lệnh, trình xử lý sự kiện nội tuyến và URL độc hại trong đầu vào đánh dấu thủ công sẽ tự động bị loại bỏ: không cần cấu hình.

♥️ Xem trước theo thời gian thực

Xem phần đánh dấu của bạn được hiển thị ngay lập tức khi bạn nhập.

🔗 Tích hợp dễ dàng

Tích hợp liền mạch vào bất kỳ dự án web nào với thiết lập tối thiểu.

🛠️ Thanh công cụ có thể tùy chỉnh

Tự động định cấu hình và sắp xếp lại các tùy chọn thanh công cụ như in đậm, in nghiêng, v.v.

Ví dụ về cấu hình đầy đủ

Sử dụng ví dụ toàn diện này để khởi tạo MarkdownEditor với tất cả các tính năng chính, bao gồm thứ tự thanh công cụ tùy chỉnh và xử lý tải lên hình ảnh nâng cao.

javascript
 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();

Tìm thấy điều này hữu ích?

Ngôi sao GitHub giúp các nhà phát triển khác khám phá trình soạn thảo. Nó là một phần của trái cây. Một ngôi sao ở đó cũng giúp ích.

Sao trên GitHub
Edit page

Cập nhật lần cuối: