/*
为什么要用iframe来开发富文本编辑器是基于多方面考虑的。以下是我个人的看法:
1.用iframe可以解决浏览器的兼容性问题。在iframe下可以很方便的获取选中的文字等。
2.在iframe下编辑可以实现所见即所得的效果。相当于是iframe下显示的是浏览器解析源码后的内容。
3.在iframe下是直接在iframe下的document中进行操作并不会影响到当前文档的document。
因此,iframe就是一种所见即所得的编辑器。
*/
window.onload = function(){
new R_editor({
editor: "editor",
editorCon: "editor-con"
})
}
//通过元素ID取得该元素
var GE = function(id){
return typeof id === "string" ? document.getElementById(id) : id;
}
//添加事件监听
var EventUtil = {};
EventUtil.addHandler = function (ele, type, handler) {
if (ele.addEventListener) {
ele.addEventListener(type, handler, false);
} else if (ele.attachEvent) {
ele.attachEvent("on" + type, handler);
} else {
ele["on" + type] = handler;
}
};
//移除事件监听
EventUtil.removeHandler = function (ele, type, handler) {
if (ele.removeEventListener) {
ele.removeEventListener(type, handler, false);
} else if (ele.detachEvent) {
ele.detachEvent("on" + type, handler);
} else {
ele["on" + type] = null;
}
};
//通过className取得元素
var getClass = function(cls,pat,tag) {
pat = pat || document;
tag = tag || "*";
if("getElementsByClassName" in pat) {
return pat.getElementsByClassName(cls)
}else {
var _all = pat.getElementsByTagName(tag),
reg = new RegExp('(\\s|^)' + cls + '(\\s|$)'),
_arr = [],
i = 0,
len = _all.length;
for(; i<len; i++) {
if(reg.test(_all[i].className))
_arr.push(_all[i]);
}
return _arr;
}
}
//是否存在该类名
var hasClass = function(ele, cls) {
return (new RegExp('(\\s|^)' + cls + '(\\s|$)')).test(ele.className);
}
//如果不存在该类名,则添加该类名
var addClass = function(ele, cls) {
if (!this.hasClass(ele, cls))
ele.className += " " + cls;
}
//移除该类名
var removeClass = function(ele, cls) {
if (hasClass(ele, cls)) {
var reg = new RegExp('(\\s|^)' + cls + '');
ele.className = ele.className.replace(reg, '');
}
}
//R_editor函数初始化
var R_editor = function(options){
this.editor = GE(options.editor);
this.editorCon = GE(options.editorCon);
this.weight = options.weight || "normal";
this.decoration = options.decoration || "none";
this.iframe;
this.iframeDoc;
this.start();//一调用R_editor,就开始执行本程序
}
//R_editor函数原型
R_editor.prototype = {
//开始执行
start:function(){
//初始化-载入
this.init();
//绑定控制-绑定每个按钮的操作
this.bindControl();
},
//初始化-载入
init:function(){
//创建iframe框架,用于显示解析后的内容
this.creatIframe();
},
//创建框架
//它最开始是为富文本编辑器而开发的,
//富文本输入框最流行的做法是把要输入的内容放到iframe中,
//这就涉及到两种document,一个主页面的document,另一个是iframe的document。iframe的document又涉及到兼容问题
creatIframe:function(){
var iframe = document.createElement("iframe");
this.iframe = iframe;
//框架大小与编辑区大小一样
iframe.width = this.editorCon.clientWidth + "px";
iframe.height = this.editorCon.clientHeight + "px";
iframe.style.margin="6px";
iframe.frameBorder = 0;
iframe.scrolling = "auto";
//在this.editorCon子节点前插入iframe子节点,因此需要取得this.editorCon.parentNode父节点
this.editorCon.parentNode.insertBefore(iframe, this.editorCon);
iframe.style.display="block";
//textarea不显示
this.editorCon.style.display = "none";
//操作iframe内contentDocument中的元素
this.iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
//在iframe中编辑,则设置为on,如果是要想在div中编辑,设置其contentEditable = "true"
this.iframeDoc.designMode = "on";
},
//取得当前窗口或父窗口
getWindow : function (node) {
var doc = node.ownerDocument || node;
return doc.defaultView || doc.parentWindow;
},
//在父节点中查找相同的元素类型
findParentByTagName:function(node,tagName,includeSelf){
if(node && tagName){
if(!includeSelf) node = node.parentNode;
while (node && node.tagName && node.nodeType != 9) {
//alert(node.tagName)
if (tagName == node.tagName.toLowerCase()) return node;
node = node.parentNode;
}
}
return null;
},
// execCommand方法是执行一个对当前文档,当前选择或者给出范围的命令
// command:要执行的命令的名称
// showUI:boolean是否向用户显示命令特定的对话框或消息框
// value类型:string要使用该命令分配的值
execCommand:function(command,show,value){
//blockquote之间的所有文本都会从常规文本中分离出来,
//经常会在左、右两边进行缩进(增加外边距),而且有时会使用斜体。
//也就是说,块引用拥有它们自己的空间
if(command == "blockquote"){
var bl= this.iframeDoc.createElement("blockquote"),
range,
par;
if(browser.ie){
range = document.selection.createRange();
par = range.parentElement();
if( par.getElementsByTagName("blockquote").length || this.findParentByTagName(par,"blockquote",true)){
}else{
bl.innerHTML = par.innerHTML;
while(par.firstChild){
alert(par.firstChild)
par.removeChild(par.firstChild);
}
par.appendChild(bl);
bl.focus();
}
}else{
this.iframeDoc.execCommand("formatBlock",true,"<blockquote>");
}
}else if(command == "html"){
}else if(command == "insertImage"){
//prompt() 方法用于显示可提示用户进行输入的对话框,
//且阻碍其他进程,即会一直停留在该对话框直到结束该操作后才继续执行其他操作
var src = prompt("图片地址", "");
this.iframeDoc.execCommand(command, show, src);
}else if(command == "createLink"){
var href = prompt("链接地址", "http://");
this.iframeDoc.execCommand(command,show, href);
}else{
this.iframeDoc.execCommand(command,show,value);
}
},
//绑定控制
bindControl:function(){
var self = this;
// tBold,通过类名editor-toolbar-item,查找全部
var toolBtn = getClass("editor-toolbar-item");
for(var i=0, len = toolBtn.length; i<len; i++){
//为每一个按钮添加鼠标移进去和移出去的事件响应
EventUtil.addHandler(toolBtn[i], "mouseover", function(){
if(!hasClass(this, "editor-toolbar-item-hover") && !hasClass(this, "editor-toolbar-item-disable"))
addClass(this, "editor-toolbar-item-hover");
});
EventUtil.addHandler(toolBtn[i], "mouseout", function(){
if(hasClass(this, "editor-toolbar-item-hover") && !hasClass(this, "editor-toolbar-item-disable"))
removeClass(this, "editor-toolbar-item-hover");
});
//为每个按钮添加鼠标点击事件响应
EventUtil.addHandler(toolBtn[i], "click", function(){
//如果editor-toolbar-item关闭,则不操作
if(hasClass(this, "editor-toolbar-item-disable")){
return;
}
//文字布局区域(左、中、右)
else if(hasClass(this, "editor-toolbar-align")){
//如果按钮已经打开,则不操作
if(hasClass(this, "editor-toolbar-item-on")){
}
//如果该按钮没有打开,则移除所有editor-toolbar-item-on后,再打开该按钮editor-toolbar-item-on
//同一时刻只能打开一个该区域的按钮
else{
var align = getClass("editor-toolbar-align");
for(var j=0,len = align.length; j<len; j++){
removeClass(align[j], "editor-toolbar-item-on");
}
addClass(this, "editor-toolbar-item-on");
}
}
//有序、无序列表区域(同一时刻只能打开一个该区域的按钮)
else if(hasClass(this, "editor-toolbar-list")){
//如果按钮已经打开,则不操作
if(hasClass(this, "editor-toolbar-item-on")){
//removeClass(this, "editor-toolbar-item-on");
}
else{
var align = getClass("editor-toolbar-list");
for(var j=0,len = align.length; j<len; j++){
removeClass(align[j], "editor-toolbar-item-on");
}
addClass(this, "editor-toolbar-item-on");
}
}
//图片&链接区域(如果已经打开其中的一个按钮,则不操作)
else if(hasClass(this, "editor-toolbar-image") || hasClass(this, "editor-toolbar-link")){
}
//HTML源代码区域
else if(hasClass(this, "editor-toolbar-html")){
//如果已经打开该按钮,再次点击则关闭该按钮,并开放其他按钮
if(hasClass(this, "editor-toolbar-item-on")){
removeClass(this, "editor-toolbar-item-on");
for(var j=0, len = toolBtn.length; j<len; j++){
removeClass(toolBtn[j], "editor-toolbar-item-disable");
}
//隐藏editorCon区域面板,editorCon用于显示HTML源码文本,
//显示iframe区域面板,iframe用于显示解析后的文本
self.editorCon.style.display = "none";
//如果要获取源码解析后的内容直接取它的value就行
self.iframeDoc.body.innerHTML = self.editorCon.value;
self.iframe.style.display="block";
//设置鼠标焦点后,发生内容粘贴事件时就会粘贴在该焦点区域
self.iframe.focus();
}
//如果没有打开该按钮,再次点击时,关闭其他按钮,开放本按钮
else{
for(var j=0, len = toolBtn.length; j<len; j++){
addClass(toolBtn[j], "editor-toolbar-item-disable");
}
removeClass(this, "editor-toolbar-item-disable");
addClass(this, "editor-toolbar-item-on");
//editorCon显示HTML源码文本
self.iframe.style.display = "none";
self.editorCon.value = self.iframeDoc.body.innerHTML;
self.editorCon.style.display="block";
//设置鼠标焦点后,发生内容粘贴事件时就会粘贴在该焦点区域
self.editorCon.focus();
}
return;
}
else{
if(hasClass(this, "editor-toolbar-item-on")){
removeClass(this, "editor-toolbar-item-on");
}
else{
addClass(this, "editor-toolbar-item-on");
}
}
self.execCommand(this.getAttribute("ec"), false, null);
self.iframeDoc.body.focus();
});
}
}
}
<body>
<div class="head clearfix">
<h1>
<a href="http://www.cnblogs.com/twobin/">twobin’
<span id="blog">blog</span>
</a>
</h1>
<span id="subtitle"> | 富文本编辑器...</span>
</div>
<div id="editor">
<div id="editor-toolbar">
<div class="editor-toolbar-item" id="editor-toolbar-bold" title="加粗" ec="bold" unselectable="on">
<b>粗</b>
</div>
<div class="editor-toolbar-item" id="editor-toolbar-italic" title="斜体" ec="italic" unselectable="on">
<b>斜</b>
</div>
<div class="editor-toolbar-item" id="editor-toolbar-underline" title="下划线" ec="underline" unselectable="on">
<b>划</b>
</div>
<div class="editor-toolbar-item" id="editor-toolbar-linethrough" title="删除线" ec="StrikeThrough" unselectable="on">
<b>删</b>
</div>
<div class="editor-toolbar-blank" >
|
</div>
<div class="editor-toolbar-item editor-toolbar-align" id="editor-toolbar-justifyleft" title="左对齐" ec="justifyleft" unselectable="on">
<b>左</b>
</div>
<div class="editor-toolbar-item editor-toolbar-align" id="editor-toolbar-justifycenter" title="居中对齐" ec="justifycenter" unselectable="on">
<b>中</b>
</div>
<div class="editor-toolbar-item editor-toolbar-align" id="editor-toolbar-justifyright" title="右对齐" ec="justifyright" unselectable="on">
<b>右</b>
</div>
<div class="editor-toolbar-blank" >
|
</div>
<div class="editor-toolbar-item editor-toolbar-list" id="editor-toolbar-ol" title="有序列表" ec="insertorderedlist" unselectable="on">
<b>有序</b>
</div>
<div class="editor-toolbar-item editor-toolbar-list" id="editor-toolbar-ul" title="无序列表" ec="insertunorderedlist" unselectable="on">
<b>无序</b>
</div>
<div style="display:none;" class="editor-toolbar-item" id="editor-toolbar-quoteright" title="引用" ec="blockquote"
unselectable="on">
<b>引用</b>
</div>
<div class="editor-toolbar-blank" >
|
</div>
<div class="editor-toolbar-item editor-toolbar-image" id="editor-toolbar-image" title="图片" ec="insertImage" unselectable="on">
<b>图片</b>
</div>
<div class="editor-toolbar-item editor-toolbar-link" id="editor-toolbar-link" title="链接" ec="createLink" unselectable="on">
<b>链接</b>
</div>
<div class="editor-toolbar-blank" >
|
</div>
<div class="editor-toolbar-item editor-toolbar-html" id="editor-toolbar-html" title="源代码" ec="html"
unselectable="on">
HTML
</div>
</div>
<textarea id="editor-con" spellcheck="false" ></textarea>
</div>
</body>
body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,input,button,textarea,p,blockquote,th,td{
padding:0;
margin:0;
}
body{
font-family: "Helvetica Neue", "Hiragino Sans GB", "Segoe UI", "Microsoft Yahei", "微软雅黑", Tahoma, Arial, STHeiti, sans-serif;
font-size:12px;
background:#f8f8f8;
}
a{
outline:none;
-moz-outline:none;
text-decoration:none;
}
.clearfix{
zoom:1;
_height:1px;
}
.clearfix:after{
content:".";
display:block;
height:0;
clear:both;
visibility:hidden;
}
.head{
height:50px;
line-height:50px;
padding-left:200px;
border-bottom:1px solid #eee;
box-shadow: 1px 1px 5px #ccc;
background:#fff;
}
.head h1{
float:left;
width:200px;
height: 51px;
font-weight:bold;
font-size:24px;
background-color: rgb(31, 123, 155);
text-align: center;
}
.head h1 a{
color: #fff;
}
#blog{
font-weight:normal;
font-size:16px;
color: #fff;
}
#subtitle{
display:block;
float:right;
font-size:16px;
color:#999;
line-height:16px;
margin:20px 200px 0 0;
}
#editor{
width:600px;
margin:100px auto 0;
border: 1px solid #dddddd;
border-top: 1px solid #ebebeb;
border-bottom: 1px solid #b7b7b7;
background-color: white;
position: relative;
overflow: visible;
border-radius: 4px;
box-shadow: 0 1px 1px #d3d1d1;
}
#editor-toolbar{
height:25px;
padding:10px 10px;
border-bottom:1px solid #eee;
-webkit-user-select: none;
}
.editor-toolbar-item{
float:left;
height:25px;
line-height:25px;
padding:0 10px;
margin:0px auto;
text-align: center;
cursor:default;
font-size:14px;
color:rgb(20,68,101);
-moz-user-select: none; /*火狐*/
-webkit-user-select: none; /*webkit浏览器*/
-ms-user-select: none; /*IE10*/
-khtml-user-select: none; /*早期浏览器*/
user-select: none;
}
.editor-toolbar-item i{
-moz-user-select: none; /*火狐*/
-webkit-user-select: none; /*webkit浏览器*/
-ms-user-select: none; /*IE10*/
-khtml-user-select: none; /*早期浏览器*/
user-select: none;
}
.editor-toolbar-item-on,.editor-toolbar-item-hover{
background:rgb(20,68,101);
color:#fff;
}
.editor-toolbar-item-disable{
background:#fff;
color:rgb(20,68,101);
opacity: 0.3;
-ms-filter: 'alpha(opacity=30)';
filter: alpha(opacity=30);
}
.editor-toolbar-blank{
float:left;
height:25px;
line-height:25px;
padding:0 0px;
margin-right:4px;
cursor:default;
color:#e5e5e5;
font-size:12px;
}
#editor-toolbar-html{
font-size:12px;
}
#editor-con{
height:220px;
margin:6px;
font-size:14px;
border:0;
outline:none;
display:block;
width:588px;
overflow:auto;
}