/* 为什么要用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; }