JS Range HTML文档/文字内容选中、库及应用介绍 |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
本文标签:JS,Range 一、前面的些话 本文的内容基本上是基于“区域范围对象(Range objects)”这个概念来说的 。这个玩意,可以让你选择HTML文档的任意部分,并可以拿这些选择的信息做你想做的事情 。其中,最常见的Range是用户用鼠标选择的内容(user selection) 。 本文有不少篇幅就是讲如何将用户的这种选择转换为W3C Range或Microsoft Text Range对象 。 二、什么是Range? 所谓"Range",是指HTML文档中任意一段内容 。一个Range的起始点和结束点位置任意,甚至起始点和结束点可以是一样的(也就是空Range) 。最常见的Range是用户文本选择范围(user text selection) 。当用户选择了页面上的某一段文字后,你就可以把这个选择转为Range 。当然,你也可以直接用程序定义Range 。 例如下面这个模样的例子: 2011-04-12
负责调查切尔诺贝利核事故对人与环境造成影响的俄科学家亚布罗科夫博士指出,因福岛核电站使用的燃料较切尔诺贝利核电站多,且有反应堆使用了含有高毒性的钚的燃料,因此"福岛核电站事故可能会比切尔诺贝利带来更严重的后果" 。
上面选中状态的那些文字就可以转换成 上面的例子可以说是最简单的 <time>2011-04-12</time>
<p>据日本广播协会电视台12日报道,日本经济产业省原子能安全保安院决定将福岛第一核电站核泄漏事故等级提高至7级 。这使日本核泄漏事故等级与苏联切尔诺贝利核电站核泄漏事故等级相同 。</p>
<p>负责调查切尔诺贝利核事故对人与环境造成影响的俄科学家亚布罗科夫博士指出,因福岛核电站使用的燃料较切尔诺贝利核电站多,且有反应堆使用了含有高毒性的钚的燃料,因此"福岛核电站事故可能会比切尔诺贝利带来更严重的后果" 。</p>
同样的, 泄漏事故等级与苏联切尔诺贝利核电站核泄漏事故等级相同 。</p> <p>负责调查切尔诺贝 显然,上面的HTML属于1级残废,基本无效 。然而幸运的是,所有的浏览器都会自动调整HTML片段使其有效,就像变成下面这样: <p>泄漏事故等级与苏联切尔诺贝利核电站核泄漏事故等级相同 。</p> <p>负责调查切尔诺贝</p> 可以看到,浏览器自动补全了一定数目的HTML来让 如果您看到下面的文字,可能是由于在其他网站或是RSS中阅读本文,本文原地址:http://www.zhangxinxu.com/wordpress/?p=1591,本文作者:张鑫旭,来自张鑫旭-鑫空间-鑫生活,访问原出处更多优秀技术文章 。
三、浏览器的兼容性在真正操刀JavaScript之前我们需要大致知道 支持: 1. W3C Range
说明:
2. Mozilla Selection
说明:
3. Microsoft TextRange
说明:
4. 总的兼容性
说明:
总的来说, 四、获得用户选择内容婆婆妈妈的解释就免了,直接看相关代码: 复制代码 代码如下: var userSelection; if (window.getSelection) { //现代浏览器 userSelection = window.getSelection(); } else if (document.selection) { //IE浏览器 考虑到Opera,应该放在后面 userSelection = document.selection.createRange(); } 由于兼容性的问题,IE浏览器吃IE的包子,其他浏览器吃Mozilla的馒头 。 在Mozilla、Safari、Opera下上面的userSelection是个Selection对象,而在IE下则是Text Range对象 。这种差异会影响到你后面的脚本:Internet Explorer的Text Ranges完全不同于Mozilla的Selection或是W3C的Range对象,你需要分别为IE和其他浏览器写两套不同的脚本 。 需要注意脚本书写的顺序:Mozilla Selection需放在前面 。原因在于Opera支持两种对象,如果你使用window.getSelection()去读取用户选择的内容,Opera会创建一个Selection对象;而使用document.selection则会创建一个Text Range对象 。 因为Opera支持Mozilla Selection和W3C Range非常好,但是其对Microsoft Text Range的支持却差强人意 。所以显然优先考虑标准浏览器,即使用window.getSelection() 。 五、userSelection的内容 userSelection变量现在的内容要么是Mozilla Selection要么就是Microsoft Text Range对象 。因此它允许访问定义在对象上的全部方法和属性 。 Mozilla Selection对象包含用户选择的文本内容,如下操作: alert(userSelection) 虽然格式并不是字符串,但是在现代浏览器下还是会弹出类似下面的内容: 泄漏事故等级与苏联切尔诺贝利核电站核泄漏事故等级相同 。负责调查切尔诺贝 为了从Microsoft Text Range 对象上获得同样的信息,你需要使用userSelection.text 。为了读取旋转的文字,可以使用类似下面代码: 复制代码 代码如下: var selectedText = userSelection; if (userSelection.text) { selectedText = userSelection.text; } 现在selectedText就包含了用户所选中的文字了 。 您可以狠狠地点击这里:获取用户所选文字demo 例如在IE7浏览器下,选中一段文字再点击demo页面上的测试按钮,就会有类似下面的弹出内容: ![]() 六、从Selection对象创建Range对象 在IE浏览器下,userSelection是Text Range,在现代浏览器下,userSelection仍然是Selection对象,要想同样创建和Selection对象内容一样的Range对象可以使用类似下面代码: 复制代码 代码如下: var getRangeObject = function(selectionObject) { if (selectionObject.getRangeAt) return selectionObject.getRangeAt(0); else { // 较老版本Safari! var range = document.createRange(); range.setStart(selectionObject.anchorNode,selectionObject.anchorOffset); range.setEnd(selectionObject.focusNode,selectionObject.focusOffset); return range; } } var rangeObject = getRangeObject(userSelection); 理想情况下,我们通过Selection对象的getRangeAt()方法就可以得到W3C Range对象 。此方法可以返回给定索引值的range对象 。通常情况下,在JavaScript中第一个Range的索引值是0 。 使用程序创建Range Safari 1.3不支持getRangeAt(),因此我们要想兼顾此浏览器,需要使用其他的方法创建新的Range对象 。显然,显示创建一个对象: var range = document.createRange(); 上面的一行代码创建了一个空的Range,为了插入内容,我们需要通过setStart()和setEnd()方法定义起止点 。 这两个方法需要两个参数: 1. Range起止的DOM节点 2. Range起止的文本偏移 。该偏移指选中文字第一个字符和最后一个字符在文本节点中的位置 。 setStart()的两个参数属性为startContainer和startOffset;setEnd()两个参数属性为endContainer和endOffset 。 以下面这个例子举例: <p>男人,即使到了50岁,也千万不要碰超过26岁还没有结婚的女人 。她可以是离婚,丧偶等等的,但是绝对不能是没有结婚 。超过了26岁没有结婚,这种女人一般心理变态,不然就是有严重问题 。市场很少犯错 。即使它犯了错,那被你捡到宝的概率也很小 。</p> <p>婚姻市场未来的变化将会是很有趣的问题,而且对未来大陆经济的走势也有举足轻重的影响,对于行业的分布,经济的整体效率有决定性的影响 。</p> <ol> <li>为什么是26这个准确的数字?</li> <li>找骂帖</li> <li>言论是对的,在100年前,lz穿越了而已 。</li> </ol>此处Range开始于第二个<p>节点,结束与第一个<li>节点 。(通常文本节点的第一个字符的索引是0 。) 由于<p>节点处的文字偏移值是8, <li>节点处的偏移是5,因而有: 复制代码 代码如下: var startP = [the p node]; var endLi = [the second li node]; range.setStart(startP, 8); range.setEnd(endLi, 5); 读取起止选中内容 上面提到了setStart(startContainer, startOffset)以及setEnd(endContainer, endOffset) 。考虑到实际情况,你很难准确知道用户选择的文字的起始位置,所以,上面一板一眼赋予偏移值的方法显然有很大的局限性 。好在任何(看参见上面的兼容性表格)Range对象有4个属性是用来定义选择内容起止点的,这4个属性与Selection对象相似,但是却是不同的名称:anchorNode/anchorOffset定义选择的起始,focusNode/focusOffset定义结束 。 因此,上面的脚本创建选区可以使用如下代码实现: range.setStart(selectionObject.anchorNode,selectionObject.anchorOffset); range.setEnd(selectionObject.focusNode,selectionObject.focusOffset); Safari的多虑 现在已经是2011年了,释小龙都有绯闻了,Safari 5已经出来好些日子了 。所以,如果仅仅是为了兼顾低版本的Safari而去使用程序创建Range,我觉得是一点必要都没有 。尤其在我们这个神奇的国度上,首先使用Safari就少,低版本的就少之又少,Safari老早就已经支持getRangeAt()了,Chrome浏览器也是如此 。 您可以狠狠地点击这里:Safari下getRangeAt测试demo 选择demo页面中的任意一部分文字,然后点击测试按钮,在较新版本的Safari浏览器下就会出现类似下图的结果: ![]() 所以,在当前环境下,要想将Selection对象转换成Range对象,直接如下代码就OK了(完整版): 复制代码 代码如下: var userSelection, rangeObject; if (window.getSelection) { //现代浏览器 userSelection = window.getSelection(); } else if (document.selection) { //IE浏览器 考虑到Opera,应该放在后面 userSelection = document.selection.createRange(); } //Range对象 rangeObject = userSelection; if (userSelection.getRangeAt) { //现代浏览器 rangeObject = userSelection.getRangeAt(0); } 七、rangy – JavaScript Range&Selection库 项目地址:http://code.google.com/p/rangy/ 就在几天前,rangy更新到了版本1.1,作者还新更新了四五个示意的页面,展示了相关的API,方法和属性等 。虽然如此,由于实例较少,还是让人很难知道此JavaScript库如何使用 。这里就举几个简单的例子示意下 。//zxx:此插件非压缩达115K,个人觉得有些庞大,在实际项目中的应用价值不大 ![]() 示例1,获取用户选中的文字: 您可以狠狠地点击这里:rangy获取用户选中文字demo 选中部分文字点击按钮,会有如下弹出(截自Firefox3.6): ![]() 相关JavaScript代码如下: 复制代码 代码如下: var sel = rangy.getSelection(); alert(sel.toString()); 示例2,给选中文字添加背景 您可以狠狠地点击这里:文字选中添加背景图demo 选中页面上一段文字,然后失去焦点,就会看到文字后面有了个美女背景图,如下截图,截自IE7浏览器: ![]() 完整JavaScript代码如下: 复制代码 代码如下: <script type="text/javascript" src="http://www.zhangxinxu.com/study/201104/rangy/rangy-core.js"></script> <script type="text/javascript" src="http://www.zhangxinxu.com/study/201104/rangy/rangy-cssclassapplier.js"></script> <script> var cssApplier; window.onload = function() { rangy.init(); cssApplier = rangy.createCssClassApplier("selectClass", true); document.body.onmouseup = function() { cssApplier.toggleSelection(); }; }; </script> 如果您看到下面的文字,可能是由于在其他网站或是RSS中阅读本文,本文原地址:http://www.zhangxinxu.com/wordpress/?p=1591,本文作者:张鑫旭,来自张鑫旭-鑫空间-鑫生活,访问原出处更多优秀技术文章 。 八、实际的应用 微博之插入话题 差不多去年这个时候,自己折腾过JS 文本域光标处添加文字并选中的内容,也是拿的新浪微博示例的,文章是“新浪微博插入话题后部分文字选中的js实现”,但是去年这篇文章多实现的话题插入效果是比较弱的: 1. 选中普通文字不能作为话题插入 2. 话题只能插在文本域最后二不是光标处 3. 默认文字的话题可以重复插入 所以,趁这个机会,正好把微博之插入话题这个功能完善下 。 您可以狠狠地点击这里:微博插入话题的效果实现demo 欢迎输入内容,点击测试 。大致会有类似下面的效果(截自Chrome): ![]() 源代码有些高度,为了节约篇幅,这里就不展示出来了,您可以在demo页面中看到完整的CSS/HTML/JS代码 。不过JS部分半封装,您要是有兴趣可以在外面包裹一个函数使其插件化,我是懒得再去折腾了 。 九、结语相关对于 文中多展示的 跌跌撞撞,滚滚爬爬 。文章难免有表述不准确的地方,欢迎指正 。也欢迎提交相关的脚本的bug 。 参考文章及相关页面:
原创文章,转载请注明来自张鑫旭-鑫空间-鑫生活 |