CSDN 的 Markdown 编辑器是一个功能强大的工具,允许用户在编辑器中输入 Markdown 格式的文本,并实时预览 HTML 渲染结果。
- Markdown 解析:将 Markdown 格式的内容转换为 HTML。
- 实时渲染:用户输入时动态更新预览内容。
- 用户交互:提供友好的编辑体验(如语法高亮、工具栏等)。
以下是实现一个类似 CSDN Markdown 编辑器的完整代码示例,每行代码都添加了详细的注释,解释其作用和为什么这样写。
完整代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Markdown 编辑器</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
display: flex;
gap: 20px; /* 设置左右两栏之间的间距 */
}
textarea {
width: 45%; /* 左侧编辑框占据一半宽度 */
height: 500px;
padding: 10px;
font-family: monospace; /* 使用等宽字体,便于代码编辑 */
border: 1px solid #ccc;
resize: none; /* 禁止调整大小 */
}
#preview {
width: 45%; /* 右侧预览区域占据一半宽度 */
height: 500px;
padding: 10px;
border: 1px solid #ccc;
overflow-y: auto; /* 如果内容超出高度,显示滚动条 */
background-color: #f9f9f9;
}
h1, h2, h3, p, ul, ol {
margin: 10px 0;
}
code {
background-color: #f0f0f0;
padding: 2px 5px;
border-radius: 3px;
font-family: monospace;
}
</style>
</head>
<body>
<!-- 左侧Markdown编辑框 -->
<textarea id="editor" placeholder="在这里输入Markdown内容..."></textarea>
<!-- 右侧HTML预览区域 -->
<div id="preview">预览内容将显示在这里。</div>
<script>
// 确保页面加载完成后执行脚本
window.onload = function () {
// 获取编辑框和预览区域的引用
const editor = document.getElementById('editor'); // Markdown编辑框
const preview = document.getElementById('preview'); // 预览区域
// 定义一个简单的Markdown解析函数
function parseMarkdown(markdown) {
let html = markdown;
// 1. 解析标题 (#, ##, ###, ####, #####, ######)
html = html.replace(/^(#{1,6})\s+(.+)$/gm, (match, hashes, content) => {
const level = hashes.length;
return `<h${level}>${content}</h${level}>`;
});
// 2. 解析加粗 (**text** 或 __text__)
html = html.replace(/(\*\*|__)(.*?)\1/g, '<strong>$2</strong>');
// 3. 解析斜体 (*text* 或 _text_)
html = html.replace(/(\*|_)(.*?)\1/g, '<em>$2</em>');
// 4. 解析无序列表 (- 或 *)
html = html.replace(/^\s*[-*]\s+(.*)$/gm, '<li>$1</li>');
html = html.replace(/(<li>.*<\/li>)+/g, '<ul>$&</ul>');
// 5. 解析有序列表 (数字.)
html = html.replace(/^\s*(\d+)\.\s+(.*)$/gm, '<li>$2</li>');
html = html.replace(/(<li>.*<\/li>)+/g, '<ol>$&</ol>');
// 6. 解析链接 [text](url)
html = html.replace(/\[(.*?)\]\((.*?)\)/g, '<a href="$2">$1</a>');
// 7. 解析图片 
html = html.replace(/!\[(.*?)\]\((.*?)\)/g, '<img src="$2" alt="$1">');
// 8. 解析段落
html = html.replace(/\n\n+/g, '</p><p>');
html = `<p>${html}</p>`;
// 9. 清理多余的换行符
html = html.replace(/\n/g, '<br>');
return html.trim();
}
// 监听编辑框的内容变化事件
editor.addEventListener('input', function () {
// 获取用户输入的Markdown内容
const markdownText = editor.value;
// 调用parseMarkdown函数解析Markdown内容
const htmlContent = parseMarkdown(markdownText);
// 将解析后的HTML插入到预览区域中
preview.innerHTML = htmlContent;
});
};
</script>
</body>
</html>
代码详细说明
HTML部分
<textarea id="editor" placeholder="在这里输入Markdown内容..."></textarea>
- 作用:提供一个文本框,用户可以在其中输入Markdown内容。
- 为什么这样写:通过
<textarea>
标签创建一个多行文本输入框,方便用户输入较长的Markdown内容。
<div id="preview">预览内容将显示在这里。</div>
- 作用:用于显示通过Markdown解析后生成的HTML内容。
- 为什么这样写:使用
<div>
作为容器,可以通过JavaScript动态修改其innerHTML
属性来显示解析后的HTML。
CSS部分
textarea {
width: 45%;
height: 500px;
padding: 10px;
font-family: monospace;
border: 1px solid #ccc;
resize: none;
}
- 作用:设置编辑框的样式,使其占据一半宽度,并提供舒适的编辑体验。
- 为什么这样写:通过宽度、高度、内边距和等宽字体优化用户体验。
#preview {
width: 45%;
height: 500px;
padding: 10px;
border: 1px solid #ccc;
overflow-y: auto;
background-color: #f9f9f9;
}
- 作用:设置预览区域的样式,使其更具可读性。
- 为什么这样写:通过宽度、高度、内边距和背景色提升视觉效果。
JavaScript部分
window.onload = function () {
- 作用:确保页面完全加载后再执行脚本。
- 为什么这样写:避免因DOM元素未加载完成而导致的错误。
const editor = document.getElementById('editor');
const preview = document.getElementById('preview');
- 作用:获取编辑框和预览区域的引用。
- 为什么这样写:通过
getElementById
获取DOM元素,便于后续操作。
function parseMarkdown(markdown) { ... }
- 作用:定义一个函数,用于将Markdown内容解析为HTML。
- 为什么这样写:通过正则表达式逐一匹配Markdown语法并转换为对应的HTML标签。
editor.addEventListener('input', function () { ... });
- 作用:监听编辑框的内容变化事件,实时更新预览内容。
- 为什么这样写:当用户输入Markdown内容时,触发解析逻辑并更新预览区域。
const htmlContent = parseMarkdown(markdownText);
preview.innerHTML = htmlContent;
- 作用:调用
parseMarkdown
函数解析Markdown内容,并将结果插入到预览区域中。 - 为什么这样写:通过动态修改
innerHTML
属性,实时显示解析后的HTML内容。
底层原理
-
Markdown解析:
- 使用正则表达式逐行匹配Markdown语法(如标题、加粗、斜体、列表等)。
- 将匹配到的内容替换为对应的HTML标签。
-
实时渲染:
- 利用
input
事件监听用户输入,每当内容发生变化时,立即调用解析函数并更新预览区域。
- 利用
-
性能优化:
- 对于简单的Markdown语法,直接使用正则表达式进行解析。
- 对于复杂的Markdown语法(如表格、代码块),可以引入第三方库(如
marked.js
)或扩展解析逻辑。
运行流程
- 用户在左侧的
<textarea>
中输入Markdown内容。 input
事件触发,调用parseMarkdown
函数解析Markdown内容。- 将解析后的HTML插入到右侧的
<div>
中,实时显示预览结果。
注意事项
- 复杂语法支持:如果需要支持更复杂的Markdown语法(如表格、代码块等),可以扩展
parseMarkdown
函数或使用第三方库。 - 安全性:如果Markdown内容来自用户输入,请对生成的HTML进行转义处理,以防止XSS攻击。
- 性能优化:对于非常大的Markdown文本,正则表达式的匹配可能会有性能问题。可以通过逐步优化正则表达式或拆分逻辑来提升效率。
通过以上代码,你可以实现一个简单的Markdown编辑器,类似于CSDN的Markdown编辑器。