<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Markdown 转 HTML 转换器 (Marked版)</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.1.0/github-markdown.min.css">
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.0.5/purify.min.js"></script>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background-color: #f6f8fa;
color: #24292e;
}
.container {
display: flex;
flex-direction: column;
gap: 20px;
}
@media (min-width: 768px) {
.container {
flex-direction: row;
}
.editor-container, .preview-container {
width: 50%;
}
}
.editor-container, .preview-container {
display: flex;
flex-direction: column;
}
h1 {
text-align: center;
color: #2c3e50;
margin-bottom: 30px;
}
h2 {
font-size: 1.2em;
padding-bottom: 10px;
border-bottom: 1px solid #e1e4e8;
color: #2c3e50;
}
textarea {
width: 100%;
height: 300px;
padding: 14px;
box-sizing: border-box;
border: 1px solid #ddd;
border-radius: 6px;
resize: vertical;
font-family: monospace;
line-height: 1.5;
}
#preview {
flex-grow: 1;
padding: 14px;
border: 1px solid #ddd;
border-radius: 6px;
background-color: white;
overflow-y: auto;
height: 300px;
}
.github-markdown-body {
box-sizing: border-box;
min-width: 200px;
max-width: 980px;
margin: 0 auto;
padding: 20px;
}
.button-container {
display: flex;
justify-content: center;
gap: 10px;
margin-top: 20px;
}
button {
padding: 10px 20px;
background-color: #2ea44f;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 16px;
transition: background-color 0.2s;
}
button:hover {
background-color: #22863a;
}
#copyBtn {
background-color: #0366d6;
}
#copyBtn:hover {
background-color: #0256b3;
}
.instructions {
background-color: #f8f9fa;
border-left: 4px solid #0366d6;
padding: 15px;
margin-bottom: 20px;
border-radius: 0 4px 4px 0;
}
.error {
color: #d73a49;
background-color: #ffebeb;
padding: 10px;
border-radius: 4px;
}
</style>
</head>
<body>
<h1>Markdown 转 HTML 转换器 (Marked版)</h1>
<div class="instructions">
<p><strong>使用说明:</strong> 在左侧输入 Markdown 文本,右侧将实时显示转换后的 HTML 预览。点击"复制 HTML"按钮可将生成的 HTML 代码复制到剪贴板。</p>
</div>
<div class="container">
<div class="editor-container">
<h2>Markdown 输入</h2>
<textarea id="markdownInput" placeholder="在此输入 Markdown 文本..."># Hello World!
这是一个简单的Markdown示例。
- 列表项一
- 列表项二
- 列表项三
**粗体文本** *斜体文本*
[这是一个链接](https://www.example.com)
> 这是一个引用块
`行内代码`
```javascript
// 代码块示例
function helloWorld() {
console.log('Hello, world!');
}
```</textarea>
</div>
<div class="preview-container">
<h2>HTML 预览</h2>
<div id="preview" class="markdown-body"></div>
</div>
</div>
<div class="button-container">
<button id="copyBtn">复制 HTML</button>
<button id="clearBtn">清空内容</button>
</div>
<script>
// 配置 marked 选项
marked.setOptions({
gfm: true, // 启用 GitHub Flavored Markdown
breaks: true, // 将换行符转换为 <br>
});
// 获取DOM元素
const markdownInput = document.getElementById('markdownInput');
const preview = document.getElementById('preview');
const copyBtn = document.getElementById('copyBtn');
const clearBtn = document.getElementById('clearBtn');
// 更新预览函数
function updatePreview() {
const markdownText = markdownInput.value;
try {
// 使用 marked 解析 Markdown
const rawHtml = marked.parse(markdownText);
// 使用 DOMPurify 对生成的 HTML 进行消毒
const cleanHtml = DOMPurify.sanitize(rawHtml);
preview.innerHTML = cleanHtml;
} catch (error) {
console.error('Markdown 解析错误:', error);
preview.innerHTML = '<p class="error">解析 Markdown 时出现错误。</p>';
}
}
// 初始加载时更新预览
updatePreview();
// 输入时实时更新预览(移除防抖功能)
markdownInput.addEventListener('input', updatePreview);
// 复制HTML代码到剪贴板
copyBtn.addEventListener('click', function() {
const markdownText = markdownInput.value;
const rawHtml = marked.parse(markdownText);
const cleanHtml = DOMPurify.sanitize(rawHtml);
navigator.clipboard.writeText(cleanHtml).then(() => {
alert('HTML 代码已复制到剪贴板!');
}).catch(err => {
console.error('无法复制文本: ', err);
// 降级方案
const textArea = document.createElement('textarea');
textArea.value = cleanHtml;
document.body.appendChild(textArea);
textArea.select();
try {
document.execCommand('copy');
alert('HTML 代码已复制到剪贴板!');
} catch (fallbackErr) {
console.error('降级复制方案也失败了: ', fallbackErr);
alert('复制失败,请手动复制预览内容。');
}
document.body.removeChild(textArea);
});
});
// 清空内容
clearBtn.addEventListener('click', function() {
if (confirm('确定要清空所有内容吗?')) {
markdownInput.value = '';
preview.innerHTML = '';
}
});
</script>
</body>
</html>