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

就把遇到的问题记录一下。写这篇文章时用的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模拟撤销与重做了。网络这方面的资源还是不少的,就不在此详细说明了

(0)

相关推荐

  • 超漂亮的Bootstrap 富文本编辑器summernote

    Summernote 是一个简单,灵活,所见即所得(WYSIWYG)的编辑器,基于 jQuery 和 Bootstrap 构建.Summernote 所有主要的操作都支持快捷键,有一个功能强大的 API,它提供了大量的自定义选项的设计(宽,高,有效的项目等等)和功能.对于主要的脚本语言或框架(PHP,Ruby,Django,NodeJS),该项目有提供了集成示例. Bootstrap summernote,用其官网上的介绍就是"Super Simple WYSIWYG editor",

  • ThinkPHP自动转义存储富文本编辑器内容导致读取出错的解决方法

    ThinkPHP的conf文件中的Convention.php有一个配置选项 'DEFAULT_FILTER'        =>  'htmlspecialchars', // 默认参数过滤方法 用于I函数... 默认这个方法是开启的.也就是说,我们往数据库里面存储的数据中都会经过htmlspecialchars这个函数的转义处理. 我在我的项目中使用了Kindeditor富文本编辑器(或许你使用的是Ueditor\ckeditor),通过富文本编辑器编辑文章的内容然后存储到数据库中,再从数据

  • jQuery 移动端artEditor富文本编辑器

    由于手机上打字比较慢,并不适合长篇大论的文章,所以移动端的富文本编辑器很少.artEditor是一款基于jQuery的移动端富文本编辑器,支持插入图片,后续完善其他功能. 插件地址:https://github.com/baixuexiyang/artEditor 欢迎star或fork. demo 引用: <script src="http://code.jquery.com/jquery-1.11.0.min.js"></script> <script

  • 19款Javascript富文本网页编辑器

    1. AIE (演示地址) AIE是一个开源的ajax图片编辑器,基于ExtJS与PHP ImageMagick开发,易于与博客/相册等其它应用相集成.提供调整图片大小,裁剪图片,旋转/翻转图片,应用滤镜,添加文本,添加水印等功能. 2. MarkitUp (演示地址) MarkitUp是一个轻量级,可定制,灵活的WYSIWYG Editor. 3. SmartMarkUP SmartMarkUP是一个轻量级,强大的JavaScript library,它能够将Textarea控件转换成富文本编

  • ASP.NET网站使用Kindeditor富文本编辑器配置步骤

    1. 下载编辑器 下载 KindEditor 最新版本,下载页面: http://www.kindsoft.net/down.php 2. 部署编辑器 解压 kindeditor-x.x.x.zip 文件,将editor文件夹复制到web目录下  3.在网页中加入(ValidateRequest="false") 复制代码 代码如下: <%@ Page Language="C#" AutoEventWireup="true" Validat

  • ThinkPHP中使用Ueditor富文本编辑器

    具体插件下载: http://ueditor.baidu.com/website/download.html#ueditor UEditor官方文档: http://ueditor.baidu.com/website/document.html 之前于 "ThinkPHP-代码" 案例中发布版本: http://www.thinkphp.cn/code/175.html UEditor解压于:PUBLIC/Ueditor下(同级目录有:Common,Conf,Lib,Tpl等) 例:在

  • Django集成百度富文本编辑器uEditor攻略

    首先从 ueEditor官网 下载最新版本的包,目前官网上提供了ASP..NET.PHP.JSP版本的,django版本只有一个第三方个人开发的,但看上出配置起来稍微复杂一点. 这里不介绍uEditor的使用方法,也不过多解释uEditor的配置方法,官网上都有详细的文档和API介绍,下载的Demo中也有常用的方法的示例代码,这里主要介绍uEditor和django集成需要修改的地方. 这里下载任意一个版本的都可以,因为我们只需要把关于ueEditor前端部分的抽取出来,至于后端服务器的,我们自

  • iOS实现富文本编辑器的方法详解

    前言 富文本编辑器不同于文本编辑器,国内做的比较好的比如有百度的UEditor和kindEditor.但是这两个也有它的缺点:界面过于复杂.不够简洁.UI设计也比较落后.不够轻量化,这篇文章我们将给大家介绍利用iOS如何实现富文本编辑器. 实现的效果 解决思路 采用webview加载一个本地html文件,该html内部编写好js方法用于与oc相互调用 最终输出该富文本字符串传输给服务器 为什么选择这样的方式 服务端要求我最终返回的数据格式为: { @"Id":"当时新建模板这

  • 学习js在线html(富文本,所见即所得)编辑器

    你要的是所见即所得HTML编辑器,简单来说需要几个基本步骤: 1,需要一个可以编辑同时又可显效果的编辑框.textarea不行,它只能用来输入纯文本,不能显示颜色.斜体之类的文字样式,就像记事本.你可以使用iframe来实现,修改iframe的designMode属性使其可以被编辑. 复制代码 代码如下: <iframe id="myEditer" width="100%" height="150px"></iframe>

  • Javascript实现简单的富文本编辑器附演示

    复制代码 代码如下: <span style="font-size:14px;"><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml&

随机推荐