利用浏览器中原生的execCommand()方法实现简易的富文本编辑器


本文于去年发布在我的CSDN上,现在搬运来我自己的小站点。原文链接:https://blog.csdn.net/gogoend/article/details/78438242
看了一下自己去年写的代码,好乱啊,毕竟我去年是个菜鸡,今年好像也是。。。😂
鉴于浏览器各种API实现的方式不相同,因此本编辑器仅仅用于拿来玩玩,或是尝鲜,真没什么卵用。

本文正文仅两张图片,直接上代码。

<!doctype html>
<!--
/**
*这是一个利用浏览器原生execCommand()方法实现的富文本编辑器,同时具有本地保存、切换为纯文本编辑器的功能,仅用于参考(以及本人做期末作业233333)
*新手上路,Bug太多,功能不完善,代码、变量太乱,大佬别笑话,第一次写博,还请多多指教
*@date:2017年11月3日 下午6:13:48
*@author:JackieHan<gogoend#qq。com>
*/
-->
<html>
<head>
<meta charset="utf-8">
<title>Jackie的富文本编辑器</title>
<style>
@font-face {
/*这里的字体文件用Base64进行了编码,嵌入到了CSS中。字体文件用于显示编辑器工具栏的图标*/
font-family: JackieHan_FontIcon;
src: url('');/*字体base64 url请参阅CSDN原文。原文链接在文章第一行。*/
}
* {
font-family: "微软雅黑", " 黑体", " 华文细黑", Microsoft YaHei UI, " 等线";
}
ul,li{
margin:0px;
padding: 0px;
}
.editor_container{
margin: 0px auto;
}
.input-wrap{
margin: 0px auto;
width: 80%;
min-width: 500px;
max-width:1000px;
}
.input_area{
margin: 10px 0px;
}
.input_area input{
height: 25px;
border: 0px;
border: 1px solid rgba(0,0,0,1.00);
}
.visual_editor {
margin: 0 auto;
box-shadow: 5px 5px 20px rgba(128,128,128,0.7);
}

#visual_editor_control_btn_area {
clear: both;
display: block;
margin: 0 auto;
width: 100%;
background: linear-gradient(rgba(244,244,244,1), rgba(200,200,200,1.00));
height: 44px;
user-select: none;
}
.visual_editor_display_area {
display: block;
margin: 7px auto;
width: 100%;
}
.visual_editor_edit_content {
display: block;
box-sizing: border-box;
margin: 0 auto;
width: 100%;
min-height: 300px;
border-top: 1px rgba(128,128,128,1.00) solid;
border-right: 0px rgba(0,84,138,1.00) solid;
border-bottom: 0px rgba(0,84,138,1.00) solid;
border-left: 0px rgba(0,84,138,1.00) solid;
overflow: hidden;
resize: vertical;
}
#visual_editor_control_btn_area ul {
padding: 0px;
margin: 5px;
}
.editor_toolbar_btn {
display: inline-block;
list-style: none;
box-sizing: content-box;
margin: 0px;
float: left;
transition: ease 0.2s all;
cursor: pointer;
line-height: 30px; /*行距设为与div高度一致*/
text-align: center;
border: 0px rgba(0,0,0,1.00) solid;
padding: 2px;
color: rgba(60,60,60,1.00)
}

.editor_text_btn {
margin: 0px;
float: left;
transition: ease 0.2s all;
cursor: pointer;
line-height: 30px; /*行距设为与div高度一致*/
text-align: center;
border: 0px rgba(0,0,0,1.00) solid;
padding: 2px;
color: rgba(60,60,60,1.00)
}

.editor_toolbar_btn {
width: 30px;
height: 30px;
font-size: 30px;
}
.editor_toolbar_btn:hover {
box-shadow: 2px 2px 2px rgba(129,129,129,0.5);
background: rgba(180,180,180,0.5);
border-radius: 8px;
}
.editor_toolbar_btn:active {
box-shadow: 2px 2px 2px rgba(129,129,129,0.5) inset;
background: rgba(200,200,200,0);
}
.editor_left_btn_group {
float: left;
}
.editor_right_btn_group{
float:right;
}
.editor_buttom_btn_group {
margin: 9px 0px;
display: block;
}
.editor_buttom_btn_group .editor_text_btn{
display: block;
color: white;
padding: 1px 8px;
margin: auto 3px auto 3px;
background: rgba(60,60,60,1.00);
}
.editor_buttom_btn_group .editor_text_btn:hover{
background: rgba(0,64,160,1.00);
border-radius: 8px;
}
.font_icon {
font-family: JackieHan_FontIcon;
}
.editor_separate_line {
display: inline-block;
width: 1px;
height: 32px;
vertical-align: middle;
background: rgba(67,67,67,1.00);
float: left;
margin: 0px 2px;
}
.article_title_box{
text-align: center;
overflow: hidden;
}
.article_title_box input{
width: 100%;
height: 36px;
text-align: center;
font-size: 30px;
border: 0px;
}
.left_float{
float:left;
}

.right_float{
float: right;
}

.code_editor_edit_content{
resize: vertical;
display: block;
margin: 0 auto;
box-sizing: border-box;
width: 100%;
border: 0px;
border-top: 1px solid;
min-height: 300px;
}
.editor_msg{
display: inline-block;
margin: 9px 10px;
padding: 6px 10px;
background: ;
text-align: right;
color: rgba(6,120,0,1.00);
float: right;
}

</style>
</head>

<body style="margin: 0px">

<div class="editor_container">
<div class="input-wrap">

<form>
<!--
<div class="input_area" style="clear: both;">
<lable>文章分类</lable>
<input type="text" >
</div>
-->

<div class="editor_area">

<div class="visual_editor">
<div id="visual_editor_control_btn_area">
<ul class="editor_left_btn_group" id="editor_left_btn_group">
<!--
<li class="font_icon editor_toolbar_btn" id="btn_undo">U</li>
<li class="font_icon editor_toolbar_btn" id="btn_redo">r</li>
<li class="editor_separate_line "></li>
<li class="font_icon editor_toolbar_btn" id="btn_paste">P</li>
<li class="font_icon editor_toolbar_btn" id="btn_cut">X</li>
<li class="font_icon editor_toolbar_btn" id="btn_copy">C</li>
<li class="font_icon editor_toolbar_btn" id="btn_selectAll">S</li>
<li class="editor_separate_line "></li>
-->
<li class="font_icon editor_toolbar_btn" id="btn_bold">b</li>
<li class="font_icon editor_toolbar_btn" id="btn_italic">i</li>
<li class="font_icon editor_toolbar_btn" id="btn_underline">u</li>
<li class="font_icon editor_toolbar_btn" id="btn_fontSize">T</li>
<li class="editor_separate_line "></li>
<li class="font_icon editor_toolbar_btn" id="btn_leftAlign">L</li>
<li class="font_icon editor_toolbar_btn" id="btn_centerAlign">M</li>
<li class="font_icon editor_toolbar_btn" id="btn_rightAlign">R</li>
<li class="editor_separate_line "></li>
<li class="font_icon editor_toolbar_btn" id="btn_ul">V</li>
<li class="font_icon editor_toolbar_btn" id="btn_ol">W</li>
<li class="editor_separate_line "></li>
<li class="font_icon editor_toolbar_btn" id="btn_cleanFormat">e</li>
</ul>

<!--
<div id="btn_quote">块引用</div>
<div id="btn_hyperlink">超链接</div>
<div id="btn_deleteHyperlink">取消超链接</div>
<div id="btn_code">代码块</div>
-->

<ul class="editor_right_btn_group">
<li class="font_icon editor_toolbar_btn" id="btn_empty">x</li>
<li class="font_icon editor_toolbar_btn" id="btn_richTextMode" style="display: none">J</li>
<li class="font_icon editor_toolbar_btn" id="btn_codeMode">Z</li>
</ul>
</div>
<div class="article_title_box">
<input id="article_title" name="article_title" placeholder="在这里写下文章标题" type="text">
</div>
<div class="visual_editor_display_area">
<!--设置属性使得div中的内容可以编辑-->
<iframe id="visual_editor_edit_content" class="visual_editor_edit_content" style="display: block"></iframe>
<!--<iframe id="visual_editor_edit_content" contenteditable="true"></iframe>-->
<textarea id="code_editor_edit_content" class="code_editor_edit_content" name="article_content_code"
style="display: none"></textarea>
</div>
</div>
<div class="editor_buttom_btn_group left_float" id="editor_buttom_btn_group">
<div class="editor_text_btn" id="btn_saveRichText">保存</div>
<div class="editor_text_btn" id="btn_publish">发布</div>
</div>
<div class="editor_msg" id="editor_msg"></div>
</div>
</form>
</div>
</div>

<script>
//获得当前时间
function now_time() {
date = new Date();
Y = date.getFullYear();
M = date.getMonth();
D = date.getDate();
h = date.getHours();
m = date.getMinutes();
if (m <= 9) {
m = "0" + m;
}
s = date.getSeconds();
if (s <= 9) {
s = "0" + s;
}
now = h + ":" + m + ":" + s;
return now;

}

//过滤HTML标签函数
function filter_html_tag(text, mode) {
var filter_result;
switch (mode) {
case 0: filter_result = text.replace(/</?.+?>/gi, ""); break;//js正则过滤所有html标签
case 1: filter_result = text.replace(/</?script[Ss]*?1>|</?(html|body|meta|style|title|link|base|head|i?frame|frameset|object|applet|embed|input|button|form)[^>]*>/gi, ""); break;//js正则过滤存在安全隐患的html标签,|s表示匹配空格、空行等空白符
default: return filter_result; break;
}
return filter_result;
}

//检查框架内的body是否为空,如果是空的,就检查本地存储中的内容是否为空,如果不是空的就把其中的内容写入框架内的body
function if_editable_area_empty() {
article_content = document.getElementById("visual_editor_edit_content").contentWindow.document.getElementsByTagName("body")[0].innerHTML;
filtered_article_content = filter_html_tag(article_content, 0);
if (filtered_article_content == "" || filtered_article_content == null) {
if (localStorage.article_content) {
document.getElementById("visual_editor_edit_content").contentWindow.document.getElementsByTagName("body")[0].innerHTML = localStorage.article_content;
document.getElementById("code_editor_edit_content").value = localStorage.article_content;
}
}
article_title = document.getElementById("article_title").value;
if (article_title == "" || article_title == null) {
if (localStorage.article_title) {
document.getElementById("article_title").value = localStorage.article_title;
}
}
}
//清空编辑区域
function empty_editor() {
var message = confirm("您将清空编辑器及本地草稿箱中的所有内容,重置编辑器并创建新的文档。n该操作不可撤销,您将丢失所有您当前已编辑的内容,是否继续?");
if (message == true) {
//分别清空标题、可视化编辑器和代码编辑器中的内容,清除本地存储中的内容。
document.getElementById("article_title").value = "";
document.getElementById("visual_editor_edit_content").contentWindow.document.getElementsByTagName("body")[0].innerHTML = "";
document.getElementById("editor_msg").innerHTML = "";
document.getElementById("code_editor_edit_content").value = "";
localStorage.removeItem("article_title");
localStorage.removeItem("article_content");
}
}

function save_title() {
article_title = document.getElementById("article_title").value;
if (article_title == null || article_title == "") {
} else {
localStorage.setItem("article_title", article_title);
}
}

function save_draft_rich_text(show_msg) {
save_title();
orig_article_content = document.getElementById("visual_editor_edit_content").contentWindow.document.getElementsByTagName("body")[0].innerHTML;
filtered_article_content = filter_html_tag(orig_article_content, 1);
filtered_article_content_for_check = filter_html_tag(filtered_article_content, 0);
fail_msg = "保存失败,请在编辑器中输入内容";
success_msg = "保存成功";
if (filtered_article_content_for_check == null || filtered_article_content_for_check == "") {
switch (show_msg) {
case 0: ; break;
case 1: {
document.getElementById("editor_msg").innerHTML = fail_msg;
setTimeout(function () {
if (document.getElementById("editor_msg").innerHTML == fail_msg) {
document.getElementById("editor_msg").innerHTML = "";
}
}, 5000);
} break;
default: ; break
}
return false;
} else {
document.getElementById("code_editor_edit_content").value = filtered_article_content;
localStorage.setItem("article_content", filtered_article_content);
switch (show_msg) {
case 0: ; break;
case 1: {
document.getElementById("editor_msg").innerHTML = success_msg;
setTimeout(function () {
if (document.getElementById("editor_msg").innerHTML == success_msg) {
document.getElementById("editor_msg").innerHTML = "";
}
}, 5000);
} break;
default: ; break;
}
return true;
//console.log("富文本已保存");
//localStorage.article_content=filtered_article_content;
//console.log(filtered_article_content);
//document.getElementById("preview").innerHTML=filtered_article_content;
}
}

function save_draft_code(show_msg) {
save_title();
orig_article_content = document.getElementById("code_editor_edit_content").value;
filtered_article_content = filter_html_tag(orig_article_content, 1);
filtered_article_content_for_check = filter_html_tag(filtered_article_content, 0);
fail_msg = "保存失败,请在编辑器中输入内容";
success_msg = "保存成功";
if (filtered_article_content_for_check == null || filtered_article_content_for_check == "") {
switch (show_msg) {
case 0: ; break;
case 1: {
document.getElementById("editor_msg").innerHTML = fail_msg;
setTimeout(function () {
if (document.getElementById("editor_msg").innerHTML == fail_msg) {
document.getElementById("editor_msg").innerHTML = "";
}
}, 5000);
} break;
default: ; break
}
return false;
} else {
document.getElementById("visual_editor_edit_content").contentWindow.document.getElementsByTagName("body")[0].innerHTML = filtered_article_content;
localStorage.setItem("article_content", filtered_article_content);
switch (show_msg) {
case 0: ; break;
case 1: {
document.getElementById("editor_msg").innerHTML = success_msg;
setTimeout(function () {
if (document.getElementById("editor_msg").innerHTML == success_msg) {
document.getElementById("editor_msg").innerHTML = "";
}
}, 5000);
} break;
default: ; break
}
return true;
//console.log("代码已保存");
//localStorage.article_content=filtered_article_content;
//console.log(filtered_article_content);
//document.getElementById("preview").innerHTML=filtered_article_content;
}
}

function editor_codeMode(operation) {
switch (operation) {
case "save": {
document.getElementById("editor_msg").innerHTML = "";
document.getElementById("code_editor_edit_content").style.height = document.getElementById("visual_editor_edit_content").style.height;
document.getElementById("visual_editor_edit_content").style.display = "none";
document.getElementById("editor_left_btn_group").style.display = "none";
document.getElementById("code_editor_edit_content").style.display = "block";
document.getElementById("btn_saveRichText").id = "btn_saveCode";
save_draft_rich_text(0);
document.getElementById("btn_codeMode").style.display = "none";
document.getElementById("btn_richTextMode").style.display = "inline-block";
//console.log("代码编辑模式");
}
case "status": {
if (document.getElementById("code_editor_edit_content").style.display == "block") {//console.log("代码编辑模式已打开");
return true;
}
else { return false; }
}
}
}

function editor_richTextMode(operation) {
switch (operation) {
case "save": {
document.getElementById("editor_msg").innerHTML = "";
document.getElementById("visual_editor_edit_content").style.height = document.getElementById("code_editor_edit_content").style.height;
document.getElementById("code_editor_edit_content").style.display = "none";
document.getElementById("editor_left_btn_group").style.display = "block";
document.getElementById("visual_editor_edit_content").style.display = "block";
document.getElementById("btn_saveCode").id = "btn_saveRichText";
save_draft_code(0);
document.getElementById("btn_richTextMode").style.display = "none";
document.getElementById("btn_codeMode").style.display = "inline-block";
//console.log("富文本编辑模式");
}
case "status": {
if (document.getElementById("visual_editor_edit_content").style.display == "block") {//console.log("富文本编辑模式已打开");
return true;
}
else { return false; }
}
}
}

//设置每20秒执行一次保存
function autosave_timer_start() {
autosave_timer = setInterval(function () {
if (editor_richTextMode("status") != true) {
if (save_draft_code(0) == true) {
msg = "您的文章已于" + now_time() + "自动保存到本地草稿箱";
document.getElementById("editor_msg").innerHTML = msg;
setTimeout(function () {
if (document.getElementById("editor_msg").innerHTML == msg) {
document.getElementById("editor_msg").innerHTML = "";
}
}, 5000);

}
} else {
if (save_draft_rich_text(0) == true) {
msg = "您的文章已于" + now_time() + "自动保存到本地草稿箱";
document.getElementById("editor_msg").innerHTML = msg;
setTimeout(function () {
if (document.getElementById("editor_msg").innerHTML == msg) {
document.getElementById("editor_msg").innerHTML = "";
}
}, 5000);
}
}
}, 20000);
}

//页面打开时执行以下脚本
window.onload = function init_editor() {
if_editable_area_empty();
autosave_timer_start();
var editor = document.getElementById("visual_editor_edit_content").contentWindow;//获得iframe Window对象
var content = document.getElementById("visual_editor_edit_content").contentDocument;//获得iframe Document对象
var toolbar_btn_group = document.getElementById("visual_editor_control_btn_area");
//设置事件监听
toolbar_btn_group.addEventListener("click", function (event) {
//用event获得点击的按钮id
switch (event.target.id) {
case "btn_undo": text_undo(); break;
case "btn_redo": text_redo(); break;
case "btn_bold": add_bold(); break;
case "btn_italic": add_italic(); break;
case "btn_underline": add_underline(); break;
case "btn_fontSize": set_fontSize(); break;
case "btn_paste": text_paste(); break;
case "btn_cut": text_cut(); break;
case "btn_copy": text_copy(); break;
case "btn_selectAll": text_selectAll(); break;
case "btn_leftAlign": text_leftAlign(); break;
case "btn_centerAlign": text_centerAlign(); break;
case "btn_rightAlign": text_rightAlign(); break;
case "btn_ul": insert_ul(); break;
case "btn_ol": insert_ol(); break;
case "btn_publish": publish(); break;
case "btn_empty": empty_editor(); break;
case "btn_cleanFormat": text_cleanFormat(); break;
case "btn_codeMode": editor_codeMode("save"); break;
case "btn_richTextMode": editor_richTextMode("save"); break;

/*
case "btn_quote":insert_quote();break;
case "btn_hyperlink":insert_hyperlink();break;
case "btn_hyperlink":insert_hyperlink();break;
case "btn_deleteHyperlink":insert_deleteHyperlink();break;
*/

}
});
document.getElementById("editor_buttom_btn_group").addEventListener("click", function (event) {
switch (event.target.id) {
case "btn_saveRichText": save_draft_rich_text(1); break;
case "btn_saveCode": save_draft_code(1); break;
case "btn_publish": publish(); break;
}
});

//打开iframe的设计模式,设置元素可以编辑
editor.document.designMode = "On";
editor.document.contentEditable = true;
if_editable_area_empty();

//使用document.execCommand(aCommandName, aShowDefaultUI, aValueArgument)方法实现各个按钮功能

function text_undo() {
editor.document.execCommand("undo", true, null);
}

function text_redo() {
editor.document.execCommand("redo", true, null);
}

function add_bold() {
editor.document.execCommand("bold", true, null);
}

function add_italic() {
editor.document.execCommand("italic", true, null);
}

function add_underline() {
editor.document.execCommand("underline", true, null);
}

function set_fontSize() {
editor.document.execCommand("fontSize", true, 7);
}
function text_paste() {
editor.document.execCommand("paste", true, null);
}
function text_copy() {
editor.document.execCommand("copy", true, null);
}
function text_cut() {
editor.document.execCommand("cut", true, null);
}
function text_selectAll() {
editor.document.execCommand("selectAll", true, null);
}
function insert_ul() {
editor.document.execCommand("insertUnorderedList", true, null);
}
function insert_ol() {
editor.document.execCommand("insertOrderedList", true, null);
}
function text_leftAlign() {
editor.document.execCommand("justifyLeft", true, null);
}
function text_rightAlign() {
editor.document.execCommand("justifyRight", true, null);
}
function text_centerAlign() {
editor.document.execCommand("justifyCenter", true, null);
}
function text_cleanFormat() {
editor.document.execCommand("removeFormat", true, null);
}

/*function insert_hyperlink(){
editor.document.execCommand("createLink",true,"http://h-cube.ga");
}

function insert_deleteHyperlink(){
editor.document.execCommand("unlink",true,null);
}*/
}

</script>
</body>

</html>

本文评论未开启。