还有很多的bugs,能简单的用
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>网页版富文本编辑器 (类Word)</title>
<!-- 引入 FontAwesome 图标库 (需要联网,为了界面美观) -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
:root {
--primary-color: #2b579a; /* Word 蓝 */
--hover-bg: #f0f0f0;
--border-color: #ccc;
--toolbar-bg: #f3f2f1;
}
body {
margin: 0;
padding: 0;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: #eef2f5;
display: flex;
flex-direction: column;
height: 100vh;
}
/* --- 工具栏区域 --- */
.toolbar-container {
background-color: var(--toolbar-bg);
border-bottom: 1px solid var(--border-color);
padding: 8px 15px;
display: flex;
flex-wrap: wrap;
gap: 5px;
align-items: center;
position: sticky;
top: 0;
z-index: 1000;
box-shadow: 0 2px 5px rgba(0,0,0,0.05);
}
.tool-group {
display: flex;
align-items: center;
border-right: 1px solid #ddd;
padding-right: 10px;
margin-right: 5px;
gap: 2px;
}
.tool-group:last-child {
border-right: none;
}
button {
background: transparent;
border: 1px solid transparent;
cursor: pointer;
padding: 6px 8px;
border-radius: 3px;
font-size: 14px;
color: #333;
transition: all 0.2s;
}
button:hover {
background-color: #e1dfdd;
}
button.active {
background-color: #cdaec7; /* 选中状态稍有不同 */
background-color: #d1e0f3;
color: var(--primary-color);
border-color: #b3d7ff;
}
select {
padding: 5px;
border: 1px solid transparent;
border-radius: 3px;
background: transparent;
cursor: pointer;
}
select:hover {
background-color: #e1dfdd;
}
/* 颜色选择器特殊处理 */
.color-wrapper {
position: relative;
display: flex;
align-items: center;
}
input[type="color"] {
width: 20px;
height: 20px;
border: none;
padding: 0;
background: transparent;
cursor: pointer;
margin-left: -25px;
opacity: 0; /* 隐藏原生控件,覆盖在图标上 */
z-index: 2;
}
.color-icon {
pointer-events: none;
}
/* --- 编辑区域 (模拟 A4 纸) --- */
.editor-workspace {
flex: 1;
overflow-y: auto;
padding: 30px 0;
display: flex;
justify-content: center;
}
#editor {
background-color: white;
width: 210mm; /* A4 宽度 */
min-height: 297mm; /* A4 高度 */
padding: 25mm; /* 页边距 */
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
outline: none;
box-sizing: border-box;
font-size: 16px;
line-height: 1.6;
color: #000;
}
/* 打印时的样式 */
@media print {
body {
background: white;
height: auto;
display: block;
}
.toolbar-container {
display: none;
}
.editor-workspace {
padding: 0;
display: block;
}
#editor {
width: 100%;
min-height: auto;
box-shadow: none;
padding: 0;
margin: 0;
}
}
/* 图片样式控制 */
#editor img {
max-width: 100%;
height: auto;
cursor: pointer;
}
#editor blockquote {
border-left: 4px solid var(--primary-color);
margin: 0;
padding-left: 15px;
color: #666;
background: #f9f9f9;
padding: 10px;
}
#editor hr {
border: 0;
border-top: 1px solid #ccc;
margin: 20px 0;
}
</style>
</head>
<body>
<!-- 工具栏 -->
<div class="toolbar-container">
<!-- 常用操作 -->
<div class="tool-group">
<button onclick="execCmd('undo')" title="撤销"><i class="fa-solid fa-rotate-left"></i></button>
<button onclick="execCmd('redo')" title="重做"><i class="fa-solid fa-rotate-right"></i></button>
<button onclick="execCmd('removeFormat')" title="清除格式"><i class="fa-solid fa-eraser"></i></button>
</div>
<!-- 字体设置 -->
<div class="tool-group">
<select onchange="execCmd('formatBlock', this.value)" title="标题格式">
<option value="P">正文</option>
<option value="H1">标题 1</option>
<option value="H2">标题 2</option>
<option value="H3">标题 3</option>
<option value="BLOCKQUOTE">引用</option>
</select>
<select onchange="execCmd('fontName', this.value)" title="字体">
<option value="Arial">Arial</option>
<option value="'Microsoft YaHei', sans-serif">微软雅黑</option>
<option value="'SimSun', serif">宋体</option>
<option value="'Courier New', monospace">等宽字体</option>
</select>
<select onchange="execCmd('fontSize', this.value)" title="字号">
<option value="3">正常</option>
<option value="1">小号</option>
<option value="4">中大</option>
<option value="5">大号</option>
<option value="6">特大</option>
<option value="7">超大</option>
</select>
</div>
<!-- 文本样式 -->
<div class="tool-group">
<button onclick="execCmd('bold')" title="加粗"><i class="fa-solid fa-bold"></i></button>
<button onclick="execCmd('italic')" title="斜体"><i class="fa-solid fa-italic"></i></button>
<button onclick="execCmd('underline')" title="下划线"><i class="fa-solid fa-underline"></i></button>
<button onclick="execCmd('strikeThrough')" title="删除线"><i class="fa-solid fa-strikethrough"></i></button>
</div>
<!-- 颜色 -->
<div class="tool-group">
<div class="color-wrapper" title="字体颜色">
<button class="color-icon"><i class="fa-solid fa-font" style="color:red; border-bottom: 3px solid red;"></i></button>
<input type="color" onchange="execCmd('foreColor', this.value)">
</div>
<div class="color-wrapper" title="背景高亮">
<button class="color-icon"><i class="fa-solid fa-highlighter" style="color:orange; border-bottom: 3px solid orange;"></i></button>
<input type="color" onchange="execCmd('hiliteColor', this.value)" value="#ffff00">
</div>
</div>
<!-- 对齐方式 -->
<div class="tool-group">
<button onclick="execCmd('justifyLeft')" title="左对齐"><i class="fa-solid fa-align-left"></i></button>
<button onclick="execCmd('justifyCenter')" title="居中"><i class="fa-solid fa-align-center"></i></button>
<button onclick="execCmd('justifyRight')" title="右对齐"><i class="fa-solid fa-align-right"></i></button>
<button onclick="execCmd('justifyFull')" title="两端对齐"><i class="fa-solid fa-align-justify"></i></button>
</div>
<!-- 列表与缩进 -->
<div class="tool-group">
<button onclick="execCmd('insertUnorderedList')" title="无序列表"><i class="fa-solid fa-list-ul"></i></button>
<button onclick="execCmd('insertOrderedList')" title="有序列表"><i class="fa-solid fa-list-ol"></i></button>
<button onclick="execCmd('indent')" title="增加缩进"><i class="fa-solid fa-indent"></i></button>
<button onclick="execCmd('outdent')" title="减少缩进"><i class="fa-solid fa-outdent"></i></button>
</div>
<!-- 插入元素 -->
<div class="tool-group">
<button onclick="insertLink()" title="插入链接"><i class="fa-solid fa-link"></i></button>
<button onclick="triggerImageUpload()" title="插入图片"><i class="fa-regular fa-image"></i></button>
<button onclick="execCmd('insertHorizontalRule')" title="插入分割线"><i class="fa-solid fa-minus"></i></button>
<!-- 隐藏的文件上传 input -->
<input type="file" id="imageInput" accept="image/*" style="display: none;" onchange="handleImageUpload(this)">
</div>
<!-- 文件操作 -->
<div class="tool-group" style="margin-left: auto; border: none;">
<button onclick="exportHTML()" title="导出为HTML"><i class="fa-solid fa-download"></i> 导出</button>
<button onclick="window.print()" title="打印"><i class="fa-solid fa-print"></i></button>
</div>
</div>
<!-- 编辑区域 -->
<div class="editor-workspace" onclick="focusEditor()">
<div id="editor" contenteditable="true">
<h1>欢迎使用简易网页版文档编辑器</h1>
<p>这是一个<b>类似 Word</b> 的富文本编辑区域。</p>
<p>你可以直接在这里输入文字,或者使用上方的工具栏进行格式化。</p>
<ul>
<li>支持加粗、斜体、下划线</li>
<li>支持插入图片(自动转为 Base64 编码,随文件保存)</li>
<li>支持导出为 HTML 文件</li>
</ul>
<p>请试着编辑这段文字...</p>
</div>
</div>
<script>
// 核心命令执行函数
function execCmd(command, value = null) {
// 重新聚焦编辑器,确保命令执行在正确位置
document.getElementById('editor').focus();
// 执行浏览器内置的编辑命令
document.execCommand(command, false, value);
updateButtonStates();
}
// 插入链接
function insertLink() {
const url = prompt("请输入链接地址 (如 https://www.example.com):", "http://");
if (url) {
execCmd('createLink', url);
}
}
// 触发图片上传
function triggerImageUpload() {
document.getElementById('imageInput').click();
}
// 处理图片上传 (转为 Base64 以便在一个文件中保存)
function handleImageUpload(input) {
if (input.files && input.files[0]) {
const reader = new FileReader();
reader.onload = function (e) {
// 插入图片到光标位置
execCmd('insertImage', e.target.result);
};
reader.readAsDataURL(input.files[0]);
}
// 清空 input 防止重复上传同一张图不触发 onchange
input.value = '';
}
// 导出为 HTML 文件
function exportHTML() {
const content = document.getElementById('editor').innerHTML;
const html = `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>导出的文档</title>
<style>
body { font-family: sans-serif; max-width: 800px; margin: 20px auto; padding: 20px; line-height: 1.6; }
img { max-width: 100%; height: auto; }
blockquote { border-left: 4px solid #ccc; margin: 0; padding-left: 15px; color: #666; }
</style>
</head>
<body>
${content}
</body>
</html>`;
const blob = new Blob([html], { type: 'text/html' });
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = 'document.html';
a.click();
}
// 点击灰色背景聚焦到编辑器
function focusEditor() {
// 只有点击的不是编辑器本身时才聚焦
if(event.target.classList.contains('editor-workspace')) {
document.getElementById('editor').focus();
}
}
// 更新按钮激活状态 (简单的 UX 优化)
const editor = document.getElementById('editor');
const buttons = document.querySelectorAll('button[onclick^="execCmd"]');
function updateButtonStates() {
// 这是一个简化的状态检测,document.queryCommandState 可以检测当前光标位置是否应用了某种样式
buttons.forEach(btn => {
const cmd = btn.getAttribute('onclick').match(/'([^']+)'/)[1];
try {
if (document.queryCommandState(cmd)) {
btn.classList.add('active');
} else {
btn.classList.remove('active');
}
} catch (e) {
// 某些命令不支持 queryCommandState,忽略
}
});
}
// 监听键盘和鼠标事件以更新工具栏状态
editor.addEventListener('keyup', updateButtonStates);
editor.addEventListener('mouseup', updateButtonStates);
editor.addEventListener('click', updateButtonStates);
// 初始化
document.getElementById('editor').focus();
</script>
</body>
</html>