富文本兼容性问题归纳(win)
上周抽空把去年写的富文本重写了一下,封装成基本UI组件,就可以在聊天框之外的地方复用了。个人觉得富文本是个兼容问题最多的模块之一,尤其是文档也没几个,把mozilla的api文档和IE的dom api关于selection和range的看了一个遍,一个个试,总算找到勉强能用的方法。
其实之前的富文本代码太乱,而且还有不少bug,只是产品经理不给时间改,O__O”…
这个富文本没有用iframe来做输入框,原因有二:
- iframe是所有dom节点中消耗性能最大的,开多几个ie6/7就会卡到不行了;
- 因为webqq是多窗口聊天的,当光标点击到输入框时,iframe会捕获鼠标事件,通知不了聊天窗设置样式;
所以就用了div,设置contentEditable=”true”,这个属性基本浏览器都支持,除了firefox2.0(不过还真有用户还在用ff2.0⊙﹏⊙b汗)
这次修改发现了不少蛋疼的兼容性问题,挑几个归纳一下:
1. 光标位置的保存/还原
富文本很大一部分兼容问题在于保存和还原光标的位置。说起光标位置,有个要注意的地方就是不要随便调用focus方法,连续调用两次focus会导致光标失去, 跟调用blur的效果一样,最好的方式就是让调用方在调用的时候保证光标在输入框中,内部代码中不要调用focus。
保存就不说了,keyup/mouseup的时候把当前的range存起来(这里有性能问题,但是blur事件又不能用,产生这个事件的时候,光标已经移到别处了),但是要保证光标在输入框中,否则range就是document的。
这里要注意到是,ie9支持了window.getSelection方法,但是,它拿到的range对象没有createContextualFragment方法,这个方法可以传入一个html字符串,直接生成dom节点,跟pasteHTML有点类似,具体说明可以点击这里查看。因此自己封装的getSelection方法,要把document.selection放在前面。
还原光标位置,对于高级浏览器,直接把原来的range添加到selection就行,像这样
1
2
|
selection.removeAllRanges(); selection.addRange( this ._lastRange); |
ie则有两种方法:
- getBookmark和moveToBookmark
双击选中源代码 123var
range = RichEditor.getRange();
range.moveToBookmark(
this
._lastBookmark);
range.select();
- setEndPoint
双击选中源代码 1234range.setEndPoint(
'EndToStart'
,
this
._lastRange);
range.collapse(
false
);
range.setEndPoint(
'EndToEnd'
,
this
._lastRange);
range.select();
这里说下setEndPoint的原理:
- 先保存lastRange,如”ABCDEFG”中的”CDE”
- 把新的range的结尾移动到lastRange的开头(即”C”的左边)
- 然后调用 collapse(false)把光标的插入点移动到range的结尾,也就是把range的开头和结尾合并在一起,不这样处理的话,调用select之后会选中”AB”(即选中”C”之前的所有内容)
- 把range的结尾移动到lastRange的结尾(即”E”的右边)
- 选中该range就能把上次保存的选区还原了(即选中”CDE”)
2. 换行处理
当在输入框按下回车键之后,ie会生成一个新的<p></p>段落标签,ff是<br>,chrome则是<div></div>。这也不是什么大问题,但是会让后续的处理产生麻烦, 理想的情况就是任何浏览器里输入框的内容都一样。所以这里要监控输入框的keydown事件,如果是回车,则阻止浏览器的默认行为,使用代码插入一个换行标签<br>。
注意1: opera的keydown事件是没办法阻止默认行为的,要用keypress事件代替。
注意2:当chrome的光标在一行的末尾的时候,插入一个<br/>并不能让光标移动到下一行,还需要在<br/>后面插入一个额外的节点才能跳到下一样。因此可以先插入<br/> ,然后把html空格” ”删除即可。
3. 删除处理(ie)
ie中如果选中一个图片或input等节点,按下退格键的话,会触发浏览器的后退处理,跟调用history.back()一样的效果,可以在keydown的时候判断选中内容的类型,如果是control类型,则阻止浏览器的行为,使用代码删除。
1
2
3
4
5
|
var selection = RichEditor.getSelection(); if (selection.type.toLowerCase() === 'control' ) { e.preventDefault(); selection.clear(); } |
PS: 这种情况只存在于使用div做输入框的情况,iframe没有。
4. 粘贴&拖拽处理
聊天窗的输入框跟一般的富文本不太一样,想发表文章用的富文本,是可以允许粘贴html片段进来的。但是聊天框里贴入html片段会导致样式很乱,影响体验。而且里面的图片都必须先上传到服务器才能使用。因此要对贴入的内容进行过滤。
之前的处理是直接把所有内容用正则过滤一遍,放过<br>和部分有标识的标签,其余一概删掉,然后再重新插入输入框。这样处理比较简单,但是会导致过滤后的光标无法找回原来的地方,体验不好。
现在是用遍历dom的方法,遍历输入框的直接子节点,把其中的文本提取出来,创建TextNode,并替换掉它的父节点,这里用到两个比较重要的属性:
- textContent(标准浏览器): textContent保存了它所有的子孙节点的文本,去除了所有Element节点
- innerText(ie): ie没有textContent属性,但是可以用innerText代替
注意: opera没有onpaste事件,只能捕捉到ctrl+v的粘贴行为,而且很意外的keypress的v键keyCode 还是86。右键贴入的就没办法了,连编辑的div连oninput事件也触发不了 O__O”…
5. 插入处理
标准浏览器(非ie)要在光标处插入内容,可以用range.createContextualFragment创建一个html片段,调用range.insertNode插入。用这种方法插入后,光标会消失,要把光标重新定位显示。
1
2
3
4
5
6
7
8
9
|
var fragment = range.createContextualFragment(html); var lastNode = fragment.lastChild; range.insertNode(fragment); //插入后把开始和结束位置都放到lastNode后面, 然后添加到selection range.setEndAfter(lastNode); range.setStartAfter(lastNode); var selection = RichEditor.getSelection(); selection.removeAllRanges(); selection.addRange(range); |
ie就简单多了, 虽然也不见得是什么好事
1
2
3
|
range.pasteHTML(html); range.collapse( false ); range.select(); |
6. 插入后的光标定位
插入html片段后,如果出现了滚动条,在非ie浏览器里,光标已经在可视区下面,而且不会自动滚动到可视区域。解决办法是插入html片段的时候,在后面添加多一个宽高都是0的图片,然后计算图片相对输入框的位置是否已经超出了输入框的可视范围。如果是,将输入框滚动定位到图片处,之后将图片删除。
这里之所以用图片,是因为他是display: inline;的元素,不会导致内容换行,又可以设置宽高,让其对用户不可见,是在是杀人越货必备之品。
代码如下:
1
2
3
4
5
6
7
8
|
html += '<img class="focus_mark" alt="" />' ; var fragment = range.createContextualFragment(html); var lastNode = fragment.lastChild; //.......... var divArea = this ._divArea; var pos = $D.getRelativeXY(lastNode, divArea); divArea.scrollTop = pos[1] < divArea.scrollHeight ? divArea.scrollHeight : pos[1]; document.execCommand( 'Delete' , false , null ); // 删除附加的节点 |
这里也可以用lastNode.scrollIntoView()滚动到可视区域的, 只是ff如果打开了firebug, 会导致webqq的样式错乱, 其他网站也许可以测试看看.
7. 保证range在输入框中
前面很多方法的执行前提都是当前焦点在输入框中,否则如果焦点在document上的话,插入的html会显示在页面的左上角,就是一个大bug了。
判断一个range是否在输入框中,可以对range的父节点进行判断,如果其parentNode是输入框或者在输入框里面,则是正确的range。 标准浏览器可以用range.commonAncestorContainer获得父节点,ie则是range.parentElement()。比较的方法是compareDocumentPosition(w3c)和contains(ie),具体怎么用就不说了,这里有个说明及封装好的代码。
以上的问题都是windows平台的,linux上也有问题,但是还没测,待续…
富文本兼容性问题归纳(win)的更多相关文章
- 重构wangEditor(web富文本编辑器),欢迎指正!
提示:最新版wangEditor请参见:wangEditor.github.io 或者 https://github.com/wangfupeng1988/wangEditor 1. 前言 (下载源码 ...
- JavaScript Iframe富文本编辑器中的光标定位
最近在项目中碰到一个比较棘手的问题: 在iframe富文本编辑器中,有个工具栏,这个工具栏在iframe标签之外,工具栏上有一个按钮,点击该按钮向iframe正在编辑中的光标处插入一个图片,图片会插入 ...
- ASP.NET MVC + 百度富文本编辑器 + EasyUi + EntityFrameWork 制作一个添加新闻功能
本文将交大伙怎么集成ASP.NET MVC + 百度富文本编辑器 + EasyUi + EntityFrameWork来制作一个新闻系统 先上截图: 添加页面如下: 下面来看代码部分 列表页如下: @ ...
- 富文本编辑器UEditor自定义工具栏(二、插入图片、音频、视频个性化功能按钮和弹层及自定义分页符)
导读:本篇将简单探讨插入图片.音频.视频的功能按钮实现方式 传送门:富文本编辑器UEditor自定义工具栏(一.基础配置与字体.背景色.行间距.超链接实现) 一.效果图 1.UEditor自定义工具栏 ...
- Javascript高级编程学习笔记(82)—— 富文本操作(2)
操作富文本 与富文本编辑器的交互的主要方式就是使用 document.execCommand() 方法 该方法可以对文档执行自定义命令,并且可以应用大多数格式 该方法接收三个参数: 要执行命令的名称 ...
- SpringMvc + Jsp+ 富文本 kindeditor 进行 图片ftp上传nginx服务器 实现
一:html 原生态的附件上传 二:实现逻辑分析: 1.1.1 需求分析 Common.js 1.绑定事件 2.初始化参数 3.上传图片的url: /pic/upload 4.上图片参数名称: upl ...
- wysiwyg 富文本编辑器(附带图片上传功能)
Fist: 需要的文件 font 文件夹下面的也是需要的哟 Then: 引入文件 <link href="bootstrap/css/bootstrap.css" rel=& ...
- Extjs4.2x与富文本框编辑器KindEditor的整合
Extjs4本身的HtmlEditor编辑器,太鸡肋了,简单的html能够应付一下,稍加复杂的就无能为力了. 对于Extjs的HtmlEditor扩展主要有三个方向,一个是扩展其本身的htmlEdit ...
- web项目中nicedit富文本编辑器的使用
web项目中nicedit富文本编辑器的使用 一.为什么要用富文本编辑器? 先说什么是富文本编辑器吧,普通的html中input或textarea标签只能进行简单的输入,而做不到其他的文本调整功能,甚 ...
随机推荐
- Nodejs 实现ESL内联FreeSWITCH设定说明
一.背景说明: SIP Server IP (Centos):192.168.11.61 ,服务器IP(Windows):192.168.11.19 二.目的: 能够从192.168.11.19上通 ...
- ubuntu系统下Python虚拟环境的安装和使用
ubuntu系统下Python虚拟环境的安装和使用 前言:进行python项目开发的时候,由于不同的项目需要使用不同的资源包和相关的配置,因此创建多个python虚拟环境,在虚拟环境下开 ...
- PYTHON-面向对象 类 绑定方法
面向对象三大特性(*****) 1继承 1. 什么是继承 继承是一种新建类的方式,新建的类称之为子类/派生类,被继承的类称之为父类/基类/超类 继承与现实生活中的继承一个意思 (例如 小明继承小明他爹 ...
- Map<String,String>集合的四种遍历方式 其中有一种针对大容量的数据集合
- java多线程快速入门(十六)
ThreadLocal关键字实现每个线程有自己的变量 package com.cppdy; class Number { private int num; public static ThreadLo ...
- Renderscript图像处理
作者:慧能 最近正在学习renderscript,后期会整理一篇完整的文档,先记着.... https://blog.csdn.net/codemydream/article/details/5346 ...
- python 全栈开发,Day65(索引)
索引 一.索引的介绍 数据库中专门用于帮助用户快速查找数据的一种数据结构.类似于字典中的目录,查找字典内容时可以根据目录查找到数据的存放位置吗,然后直接获取. 二 .索引的作用 约束和加速查找 三.常 ...
- 字节、字、bit、byte的关系【转】
字 word 字节 byte 位 bit 字长是指字的长度 1字=2字节(1 word = 2 byte) 1字节=8位(1 byte = 8bit) 一个字的字长为16 一个字节的字长是8 bps ...
- 《剑指offer》-斐波那契数列
大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项. n<=39 这么直接的问fibonacci,显然是迭代计算.递归的问题在于重复计算,而迭代则避免了这一点:递归是自 ...
- 《剑指offer》-孩子们的游戏(圆圈中最后剩下的数)
每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此.HF作为牛客的资深元老,自然也准备了一些小游戏.其中,有个游戏是这样的:首先,让小朋友们围成一个大圈.然后,他随机指定一个数m ...