一步步教你实现富本文编辑器(第二部分)
这部分是利用iframe实现我们的富文本编辑器。上面提到激活编辑模式有两个方法,contentEditable="true"与designMode="On"。contentEditable 是针对单个元素,而designMode是面向整个文档的。因此,当我们使用iframe时,我们得先取到iframe的document。
var iframeDocument = iframe.contentDocument || iframe.contentWindow.document; |
不过,在这之前,我们首先动态生成iframe,然后再对iframe进行一些样式设置,插入到原textarea之前,既然有了iframe作为我们输入的场所,那么我们就没有必要留着textarea占着空间,我们把它隐藏便是。最后,我们用iframeDocument执行execCommand()就圆满了……下面是以上过程的代码
//*********************************************************** var textarea = document.getElementById( "textarea" ); textarea.style.display = "none" ; var iframe = document.createElement( "iframe" ); iframe.style.width = "390px" ; iframe.style.height = "100px" ; iframe.frameBorder=0; textarea.parentNode.insertBefore(iframe,textarea); var iframeDocument = iframe.contentDocument || iframe.contentWindow.document; iframeDocument.designMode = "on" ; iframeDocument.open(); iframeDocument.write( '' ); iframeDocument.close(); //*********************************************************** |
嗯,基本上,所有常见游览器都能正常编辑文本了,除了FF有一个地方需要修正。在FF下,当我们试图改变文本的背影色时,发现整个iframe都变色了,原来FF的backcolor是针对文档的body元素的,要想和IE一致,我们需要用到hilitecolor,它才是针对我们选中的文本进行背景色设置。因此,修正如下:
for (i=0,l=selects.length;i < l;i++){ selects[i].onchange = new function (){ var select = selects[i]; return function (){ var command = select.getAttribute( "title" ), value = select.options[select.selectedIndex].value; if (command == 'backcolor' && /a/[-1]== 'a' ){ iframeDocument.execCommand( 'hilitecolor' , false ,value); } else { iframeDocument.execCommand(command, false ,value); } }; }; }; |
其中 /a/[-1]=='a' 是用来嗅探浏览器是否为FF。
接着下来我们实现一下插入命令,如插件图片,插入表格等等。这个相对复杂些,我们还是可以利用execCommand命令,但对于表格,我们得另寻他法了!我们先实现一些相对简单的吧!
修改表现层,增加命令按钮。
< div id="editor"> <!-- 前略 //--> < select title="backcolor"> < option style="color:#000000" value="#000000">■■</ option > < option style="color:#FF8080" value="#FF8080">■■</ option > < option style="color:#FFFF00" value="#FFFF00">■■</ option > < option style="color:#80FF00" value="#80FF00">■■</ option > < option style="color:#00FFFF" value="#00FFFF">■■</ option > < option style="color:#0000FF" value="#0000FF">■■</ option > < option style="color:#FF00FF" value="#FF00FF">■■</ option > </ select > < span class="button" title="createlink" unselectable="on">链接</ span > < span class="button" title="insertimage" unselectable="on">图片</ span > </ div > |
然后我们在点击这两个按钮时,弹出一个Promp窗口,让人们输入链接就是!
for ( var i = 0,l= buttons.length;i<l;i++){ buttons[i].onclick = new function (){ var command=buttons[i].getAttribute( "title" ); return function (){ if (command == 'createlink' || command == 'insertimage' ){ var value = prompt( '请输入超链接:' , 'http://' ); iframeDocument.execCommand(command, false ,value); } else { iframeDocument.execCommand(command, false , '' ); } } }; }; |
至于更复杂的插入,需要取得编辑光标的位置,这是个很麻烦的问题,我们留待pure DOM实现时才搞这个,现在我们试试提交内容。这个流程大抵是这个样子,当我们的iframe失去焦点(onblur)时,我们就偷偷把iframe里的内容拷贝到textarea中去,那么当我们提交时textarea里就有东西了!这看起来很简单,但当中的兼容问题会搞死一大班人!
为了说明这个问题,我们做如下实验——
iframe.onblur = function (){ //执行绑定事件 } |
IE正常,其他游览器没反应,难道我们绑定事件的方式不对吗?!没办法,奠出我们的addEvent函数!
嘿嘿,真是该死,W3C那帮家伙照旧没反应!遍历他们的iframe对象,发现它们是有blur与focus等方法,不过都是幌子,一个空实现!揭开谜底吧,在W3C标准中,iframe对象是不绑定任何事件,全部下放到其contentWindow中,并且以前onXXXX这样兼容写法也无效了,标准到底,我们要用标准的addEventListener函数才能请得动它!而要取得iframe的内容,其实就是取得其body的内容。
var deliver = function (iframe){ addEvent(iframe.contentWindow, 'blur' , function (){ var tip = document.getElementById( "exe" ); iframeDocument = iframe.contentDocument || iframe.contentWindow.document; tip.innerHTML = "失去焦点,并取得iframe的内容为:" +iframeDocument.body.innerHTML tip.style.backgroundColor = "#9999CC" ; }) } |
接着我们试图取得其编辑后的源码,也就是切换到HTML模式了。不用说HTML模式只能在textarea中显式,我们做一个按钮,用于隐藏与显示iframe与textarea就行了!
if (switchEditMode){ //切换到textarea iframe.style.display = "none" ; textarea.style.display = "block" ; textarea.value = iframeDocument.body.innerHTML; textarea.focus(); switchEditMode = false ; } else { //切换到iframe iframe.style.display = "block" ; textarea.style.display = "none" ; iframeDocument.body.innerHTML = textarea.value; iframe.contentWindow.focus(); switchEditMode = true ; } |
好了,这部分就到此为止,下次我们将对它进行封装,并实现更为复杂的插入!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义