`

javascript开发随笔3 开发iframe富文本编辑器的一点体会

 
阅读更多

前段时间有个需求是开发富文本编辑器,这个之前随做过,但看了需求,发现有些地方还需google

就把遇到的问题记录一下。写这篇文章时用的TinyMCE编辑器就很强大,但毕竟是第三方的,项目也考虑了这些,如果做些自定义的东西不太方便。 
1. 判断光标位置的元素(或者选中的部分)的样式。光标位置改变的时候更新工具栏对应按钮的样式。什么情况下光标的位置会改变呢?是键盘方向键和鼠标点击,于是就判断键盘事件和鼠标事件来执行光标移动的处理。 
a. 获得光标位置或选中元素:首先getSelection,创建range。然后获得元素,获取到元素之后就可以或得样式、tagName等等,做更多的操作,运行代码: 

复制代码代码如下:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 
<html> 
<head> 
<title></title> 
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 
<style type="text/css"> 
p{width:600px;text-align: left;text-indent:2em;line-height:20px;font-size:12px} 
textarea{width:600px;height:100px;font-size:12px;overflow:auto} 
</style> 
</head> 
<body> 
<span style="display:block;height:150px; font-size:12px;line-height:150%">信息</span> 
<script type="text/javascript"> 
function createEditor(){ 
var iframe = document.createElement('iframe'); 
iframe.id = 'iframe'; 
iframe.frameBorder = 1; 
iframe.width = 400; 
iframe.height = 200; 
document.body.appendChild(iframe); 
return iframe; 

var bind = function(element,eventType,fn,useCapture){ 
useCapture = useCapture || false; 
if(arguments.length < 3){ 
return true 
}; 
if(window.addEventListener){ 
element.addEventListener(eventType, fn, useCapture); 
}else{ 
element.attachEvent('on'+eventType,fn, useCapture); 


//from 司徒正美 
var css = document.defaultView ? function(el,style){ 
return document.defaultView.getComputedStyle(el, null).getPropertyValue(style) 
} : function(el,style){ 
style = style.replace(/\-(\w)/g, function($, $1){ 
return $1.toUpperCase(); 
}); 
return el.currentStyle[style]; 

function bindEditor(){ 
var iframe = createEditor(); 
var ifr_win = iframe.contentWindow; 
var ifr_doc = ifr_win.document; 

var editorContent = '<span style="font-family: 黑体; font-weight: bold;">阿四大四大四</span>大<span style="font-style: italic; text-decoration: underline;">四大四大打算</span>打打<span style="font-style: italic; color: #ff0000;">双打萨斯</span>大师'; 
ifr_doc.designMode='On';//可编辑 
ifr_doc.contentEditable = true; 
ifr_doc.open(); 
ifr_doc.writeln('<html><head><style type="text/css">body{padding:10px;margin:0;font-size:13px;font-family:宋体;text-align:left;overflow:auto;word-wrap: break-word;cursor:text;background-color: transparent; }body,p,font,div,ul,li {line-height: 1.5;}p,font,div,ul,li {line-height: 1.5;margin:0;padding:0}a{color:#548DD4}</style></head><body>'+ editorContent +'</body></html>'); 
ifr_doc.close(); 

var getRange = function(){ 
var range = window.getSelection ? ifr_win.getSelection() : ifr_win.document.selection; 
if (!range) { 
return { 
node : null, 
range : null, 
text : null 
}; 

range = range.createRange ? range.createRange() : range.getRangeAt(0); 
var text = window.getSelection ? range : range.text; 
var rangeNode = null; 
if (range.commonAncestorContainer) { 
rangeNode = range.commonAncestorContainer; 
} else { 
if (range.parentElement) rangeNode = range.parentElement(); 

return { 
node : rangeNode, 
range : range, 
text : text 


var info = document.getElementsByTagName('span')[0]; 
var getStyle = function(node){ 
//console.log(node) 
var html = ''; 
html+= '<span style="font-family:'+ css(node,'font-family') +'">字体:'+ css(node,'font-family') + '</span><br />'; 
html+= '<span style="color:'+ css(node,'color') +'">颜色:'+ css(node,'color') + '</span><br />'; 
html+= '<span style="font-style:'+ css(node,'font-style') +'">斜体:'+ css(node,'font-style') + '</span><br />'; 
html+= '<span style="font-weight:'+ css(node,'font-weight') +'">粗体:'+ css(node,'font-weight') + '</span><br />'; 
html+= '<span style="text-decoration:'+ css(node,'text-decoration') +'">下划线:'+ css(node,'text-decoration') + '</span><br />'; 
html+= 'tagName:'+ node.tagName + ',style:'+ node.getAttribute('style') +'<br />'; 

info.innerHTML = html; 

//当光标位置改变时候执行 
var onselectionchange = function(event){ 
var e = event || window.event; 
if(!e.keyCode)e.keyCode = e.which; 
//方向键移动光标,获取光标位置的dom 
if((e.keyCode >= 37 && e.keyCode <= 40 )|| e.type == "click"){ 

var node = getRange().node;//获取光标位置元素 
if(node !== null){ 
while(node.nodeType != 1){ 
node = node.parentNode; 

getStyle(node); 




bind(ifr_doc,'click',onselectionchange,false); 
bind(ifr_doc,'keydown',onselectionchange,false); 

window.onload = function(){ 
bindEditor(); 

</script> 
</body> 
</html> 


2. ie不能保持光标位置,这个是在添加超链接时候出现的问题,当不使用浏览器内置的输入框,光标移动其他的文本域里,ie会失去所选中的部分,无法对选中的部分加链接了,解决办法就是:利用range的getBookmark和moveToBookmark,然后给iframe的document绑定onbeforedeactivate(getBookmark)、onactivate(moveTo),这2个事件的大致意思就是,当被激活和失去激活状态。增加事件之后,就不必保存lastRang或者再其他地方设置bookmark了,可以让ie像其他浏览器一样自动保持光标位置了 

复制代码代码如下:

if(Util.browser.msie){ 
Util.bind(this.E.ifr_win.document, "beforedeactivate", function(){ 
var Rng = _self.getRange().range; 
_self.rangeBookMark= Rng.getBookmark(); 
}); 
Util.bind(this.E.ifr_win.document, "activate", function(){ 
var Rng = _self.getRange().range; 
Rng.moveToBookmark(_self.rangeBookMark); 
Rng.select(); 
_self.rangeBookMark = null; 
}); 


3. ie中的撤销与重做 。 当iframe外部有弹出窗口、或者修改html撤销、重做功能将失效。只能归为ie的bug了。。。。也许ie没分清iframe和页面的document,把他们的撤销、重做混道义了。 
如下: 

复制代码代码如下:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 
<html> 
<head> 
<title></title> 
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 
<style type="text/css"> 
p{width:600px;text-align: left;text-indent:2em;line-height:20px;font-size:12px} 
textarea{width:600px;height:100px;font-size:12px;overflow:auto} 
</style> 
</head> 
<body> 
<span style="display:block;height:150px; font-size:12px;line-height:150%">信息</span> 
<div id="J_tool"> 
<input type="button" command="Undo" value="撤销" unselectable="on" /> 
<input type="button" command="Redo" value="重做" unselectable="on" /> 
<input type="button" command="Bold" value="粗体" unselectable="on" /> 
<input type="button" command="Italic" value="斜体" unselectable="on" /> 
</div> 
<br /> 
<input type="button" onclick="changeLayout()" value="点击下,ie将无法撤销、重做" /> 
<br /> 
<script type="text/javascript"> 
function changeLayout(){ 
var popwin = document.getElementById('popwin'); 
if(!popwin){ 
popwin = document.createElement('div'); 
popwin.id = 'popwin'; 
popwin.style.cssText = 'display:none;width:300px;height:150px;background-color:#ccc;position:absolute;left:0;top:0px;text-align:center;line-height:150px;'; 
popwin.innerHTML = '改变了layoud渲染,ie将无法撤销、重做'; 
document.body.appendChild(popwin); 
popwin.onclick= function(){this.style.display = 'none'}; 

popwin.style.display = popwin.style.display == 'none' ? 'block' : 'none'; 

function createEditor(){ 
var iframe = document.createElement('iframe'); 
iframe.id = 'iframe'; 
iframe.frameBorder = 1; 
iframe.width = 400; 
iframe.height = 200; 
document.body.appendChild(iframe); 
return iframe; 

var bind = function(element,eventType,fn,useCapture){ 
useCapture = useCapture || false; 
if(arguments.length < 3){ 
return true 
}; 
if(window.addEventListener){ 
element.addEventListener(eventType, fn, useCapture); 
}else{ 
element.attachEvent('on'+eventType,fn, useCapture); 


//from 司徒正美 
var css = document.defaultView ? function(el,style){ 
return document.defaultView.getComputedStyle(el, null).getPropertyValue(style) 
} : function(el,style){ 
style = style.replace(/\-(\w)/g, function($, $1){ 
return $1.toUpperCase(); 
}); 
return el.currentStyle[style]; 

function bindEditor(){ 
var iframe = createEditor(); 
var ifr_win = iframe.contentWindow; 
var ifr_doc = ifr_win.document; 
var editorContent = '<span style="font-family: 黑体; font-weight: bold;">阿四大四大四</span>大<span style="font-style: italic; text-decoration: underline;">四大四大打算</span>打打<span style="font-style: italic; color: #ff0000;">双打萨斯</span>大师'; 
ifr_doc.designMode='On';//可编辑 
ifr_doc.contentEditable = true; 
ifr_doc.open(); 
ifr_doc.writeln('<html><head><style type="text/css">body{padding:10px;margin:0;font-size:13px;font-family:宋体;text-align:left;overflow:auto;word-wrap: break-word;cursor:text;background-color: transparent; }body,p,font,div,ul,li {line-height: 1.5;}p,font,div,ul,li {line-height: 1.5;margin:0;padding:0}a{color:#548DD4}</style></head><body>'+ editorContent +'</body></html>'); 
ifr_doc.close(); 
var getRange = function(){ 
var range = window.getSelection ? ifr_win.getSelection() : ifr_win.document.selection; 
if (!range) { 
return { 
node : null, 
range : null, 
text : null 
}; 

range = range.createRange ? range.createRange() : range.getRangeAt(0); 
var text = window.getSelection ? range : range.text; 
var rangeNode = null; 
if (range.commonAncestorContainer) { 
rangeNode = range.commonAncestorContainer; 
} else { 
if (range.parentElement) rangeNode = range.parentElement(); 

return { 
node : rangeNode, 
range : range, 
text : text 


var info = document.getElementsByTagName('span')[0]; 
var getStyle = function(node){ 
//console.log(node) 
var html = ''; 
html+= '<span style="font-family:'+ css(node,'font-family') +'">字体:'+ css(node,'font-family') + '</span><br />'; 
html+= '<span style="color:'+ css(node,'color') +'">颜色:'+ css(node,'color') + '</span><br />'; 
html+= '<span style="font-style:'+ css(node,'font-style') +'">斜体:'+ css(node,'font-style') + '</span><br />'; 
html+= '<span style="font-weight:'+ css(node,'font-weight') +'">粗体:'+ css(node,'font-weight') + '</span><br />'; 
html+= '<span style="text-decoration:'+ css(node,'text-decoration') +'">下划线:'+ css(node,'text-decoration') + '</span><br />'; 
html+= 'tagName:'+ node.tagName + ',style:'+ node.getAttribute('style') +'<br />'; 
info.innerHTML = html; 

//当光标位置改变时候执行 
var onselectionchange = function(event){ 
var e = event || window.event; 
if(!e.keyCode)e.keyCode = e.which; 
//方向键移动光标,获取光标位置的dom 
if((e.keyCode >= 37 && e.keyCode <= 40 )|| e.type == "click"){ 
var node = getRange().node;//获取光标位置元素 
if(node !== null){ 
while(node.nodeType != 1){ 
node = node.parentNode; 

getStyle(node); 



bind(ifr_doc,'click',onselectionchange,false); 
bind(ifr_doc,'keydown',onselectionchange,false); 
bind(document.getElementById('J_tool'),'click',function(event){ 
event = event || window.event; 
var target = event.srcElement || event.target; 
var command = target.getAttribute('command'); 
var param = target.getAttribute('param') || ''; 
ifr_doc.execCommand(command,false,param); 
return false; 
}) 

window.onload = function(){ 
bindEditor(); 

</script> 
</body> 
</html> 


如何解决呢? 只能依靠javascript模拟撤销与重做了。网络这方面的资源还是不少的,就不在此详细说明了 

分享到:
评论

相关推荐

    富文本编辑器组件.zip

    在前端开发中,富文本编辑器是构建内容管理系统、博客平台、论坛等交互式内容创建界面不可或缺的组成部分。本压缩包“富文本编辑器组件.zip”包含了一个用于前端实现此类功能的组件,允许用户轻松地实现多种编辑操作...

    富文本编辑器 带表情

    富文本编辑器是一种用于网页和应用程序的工具,它允许用户以类似于Word文档的方式输入、编辑和格式化文本。这类编辑器通常支持多种功能,如字体样式调整、颜色修改、插入图片、链接添加以及表情符号的使用。在描述中...

    一款基于vue3+typescript+element plus的HTML5富文本编辑器.zip

    标题中的“一款基于vue3+typescript+element plus的HTML5富文本编辑器”揭示了这个项目的核心技术栈,包括Vue.js的最新版本Vue 3、TypeScript以及Element Plus UI库。Vue 3是当前非常流行的前端框架,它提供了组件化...

    H5下最简洁好用的富文本编辑器

    在现代Web开发中,富文本编辑器是必不可少的工具,特别是在需要用户输入格式化内容的场景下,如博客、论坛、评论系统等。H5(HTML5)作为最新的Web标准,提供了更多的API和功能,使得富文本编辑器的实现更加方便和...

    百度Ueditor在线富文本编辑器

    百度Ueditor是一款由百度公司开发的开源、免费的在线富文本编辑器,主要用于Web应用中提供便捷的文本编辑功能。它以其强大的功能、良好的用户体验以及易于集成的特点,被广泛应用在各类网站和项目中。 ### 主要特点...

    PHP版百度富文本编辑器ueditor

    在Web开发中,富文本编辑器是不可或缺的工具,它允许用户以类似于Microsoft Word的方式创建和编辑内容,然后以HTML格式存储。其中,百度开发的ueditor是一款非常受欢迎的开源富文本编辑器,尤其在PHP环境下广泛使用...

    网易javascript富文本编辑器,很好很强大

    **JavaScript富文本编辑器概述** JavaScript富文本编辑器是一种基于Web的文本编辑工具,它允许用户在网页上进行类似于Word的文本编辑操作。这些编辑器通常由JavaScript编写,结合HTML和CSS技术,提供一个交互式的...

    真正移动端可用富文本编辑器

    标题中的“真正移动端可用富文本编辑器”指的是适用于移动设备的高级文本编辑工具,它能够提供丰富的格式选项,如字体、字号、颜色、对齐方式等,同时还支持插入图片和其他多媒体元素。这样的编辑器对于在手机或平板...

    TinyMCE 富文本编辑器

    TinyMCE是一款强大的开源富文本编辑器,广泛应用于网页内容编辑和内容管理系统中。它提供了丰富的功能,使得用户可以像在Word中一样编辑文本,包括字体样式、字号、颜色、对齐方式、列表、图像插入、链接添加等。...

    富文本编辑器周报工具

    【富文本编辑器周报工具】是一款基于SpringBoot框架开发的应用,主要功能是提供一个便捷的周报生成和管理平台。这款工具利用了H2内存数据库来存储数据,H2是一款轻量级、高性能的关系型数据库,适用于快速开发和测试...

    springboot集成富文本编辑器

    在这个例子中,可能使用的是"ueditor",这是一款功能强大的JavaScript富文本编辑器,广泛应用于网页内容编辑。ueditor提供了丰富的API和配置选项,可以定制编辑器的功能和样式。 集成步骤通常包括以下几个关键环节...

    html5文本编辑器_在线文本编辑器_富文本编辑器

    因此,富文本编辑器应运而生,它们通常基于JavaScript构建,为用户提供类似于Word的界面,可以进行字体设置、段落调整、插入图片和链接等操作。 富文本编辑器的实现主要依赖于DOM(文档对象模型)和APIs,如...

    Bootstrap富文本编辑器

    Bootstrap富文本编辑器是网页开发中的一个重要工具,它利用了流行的Bootstrap框架,为网页内容的创建和编辑提供了方便且美观的界面。Bootstrap是一款开源的前端框架,由Twitter开发,广泛应用于快速构建响应式和移动...

    富文本编辑器组件.rar

    在Web开发领域,富文本编辑器扮演着至关重要的角色,因为它们为用户提供了一种方便的方式来创建复杂的内容,如文章、博客、论坛帖子等。本压缩包文件“富文本编辑器组件.rar”很可能包含一个或多个用于构建这种功能...

    xadmin的富文本编辑器

    UEditor是一款由百度开发的开源富文本编辑器,具有丰富的功能和良好的性能。它支持图片上传、视频插入、代码高亮、表格操作等,使得用户在网页上编辑内容时如同在桌面软件中操作一样方便。 **django-ueditor插件** ...

    微信小程序富文本编辑器demo源码.zip

    微信小程序富文本编辑器是一个非常实用的开发工具,尤其对于那些需要在小程序中实现多样化文本格式展示或编辑的场景。这个“微信小程序富文本编辑器demo源码”提供了一个示例,可以帮助开发者快速理解和实现此类功能...

    百度富文本编辑器

    富文本编辑器是网页开发中常用的一种工具,它允许用户在浏览器端进行格式化的文本输入,比如插入图片、链接、列表、字体样式等,极大地提升了用户体验。百度团队开发的富文本编辑器因其易用性和丰富的功能而备受推崇...

    sdEditor富文本编辑器 v1.1.1.zip

    "sdEditor富文本编辑器 v1.1.1.zip" 是一个包含富文本编辑器相关资源的压缩包,主要用于提供用户界面友好的文本编辑功能。这个编辑器可能被用于网站内容管理、论坛发帖、博客撰写等场景,适用于网页开发和计算机应用...

    富文本编辑器以及表格操作,可拉伸单元格宽度、增加行、增加列、合并单元格

    富文本编辑器是一种常见的网页和应用中的文本输入组件,它允许用户在编辑环境中进行复杂的文本格式设置,如字体、字号、颜色、对齐...对于想要提升自己在富文本编辑器开发领域的技能的程序员来说,这是一个宝贵的资源。

Global site tag (gtag.js) - Google Analytics