前言

本例基于react,但是实际上就是用原生js做的。兼容性做到了IE9,但是按照这个思路做是可以做到IE8甚至更低的。

需求与最初的思路

当我拿到这个需求的时候以为很简单,就是可以给页面上的文章做记号,比如添加个下划线,或者背景涂色做成荧光笔的样子。

因为只需要兼容IE9,所以window.getSelection是支持的。(IE8及以下有其它的获取选中的方法)

那么思路就是选中文本,点击添加下划线后,通过 window.getSelection.getRangeAt(0) 拿到选中的文本对象,获取到文本后,通过文本对象的 surroundContents 方法来将文本替换为带有class的元素。

初步的实现

思路很简单,代码同样也很简单。

CSS代码:.custom-underline{  border-bottom: 1px solid #f00;  font-style: normal;

}.nite-writer-pen{  background-color: lightgreen;  border-radius: 5px;  box-shadow: 0 0 10px lightgreen;  font-style: normal;

}

JS代码:/**

* 用元素替换被选中的文本

*/var replaceSelectedStrByEle = function(className){  var selecter = window.getSelection();  var selectStr = selecter.toString();  if (selectStr.trim != "") {    var rang = selecter.getRangeAt(0);    var ele = document.createElement("i");

ele.className = className;

ele.textContent = selectStr

rang.surroundContents(ele);

}

}

replaceSelectedStrByEle('nite-writer-pen');

天坑出现

上面的思路实在是过于简单,如果是一个很简单的元素,那么这种做法是没有问题的。

但是我们的文章的html结构一般都没有这么简单,比如对于以下情况:

道可道,非常道。

名可名,非常名

如果在页面上我选中的操作如下:

那么上面的代码实现就会出现BUG,对于这种跨元素选中的情况,想当然的用元素去替换文本是没用的。

如果你想得更多,比如跨多个元素选中,以及选中元素为更为复杂的html结构,你就会发现这是一个多大的坑。

html结构有多复杂,这个坑就有多深。

思路的僵局与写轮眼

其实天坑也不是完全没有路走,在跨多个元素选中的过程中,我想给选中的内容加样式,那么就需要获取到所有选中的文本节点,并且批量替换成元素。

但是 window.getSelection.getRangeAt(0) 获取到的range对象只能获取到最开始选中的节点和最后选中的节点的。

那么接下来通过选中的最开始的节点和最后的节点获取到所有的文本节点。

思路就是这么个思路,但是实现起来是很复杂的。

面临深坑,肯定不可能硬刚。

毕竟我已经不是当年头铁的愣头青了,做项目是有时间和精力成本的,如果要填掉这个坑,那么加班是不可避免的,最重要的是在有限时间内填的这个坑可能还有各种BUG和兼容性问题。

对待这种天坑,一般就给需求来个做不了三连了。

但是这把我想赢,毕竟这个东西看起来确实简单。

在外人的眼里,如此之简单,分分钟搞定的事情。这都做不了,我还怎么在前端的圈子里继续划水?

所以我要动用程序员的入门技——写轮眼。

然而百度、谷歌无效,根本没有这个解决方案,有的全是些我最初的简单实现。

但是回忆我们以前见到的各种网页应用与场景,很容易就能想到上面的这种操作我们是见过的。

那就是从远古IE时代就已经出现的各种富文本编辑器组件。

目标确认,百度的ueditor,这波我要赢。

分析ueditor与复制

上github两三下拿到ueditor源码,开始读源码分析代码。

中间过程不再多说,精简代码,除去一些不需要的代码和兼容性处理后,拿到了五个文件:browser.js (浏览器版本判断,用于做兼容性处理)

domUtils.js (dom操作)

dtd.js (节点的类型与元素判断)

Range.js (封装的选中范围对象)

utils.js (工具类)

即使精简后,代码也不少,大概两三千行。不过其中还有很多注释,压缩后体积并不大。

由于代码比较多,这里就不全部展示了,文章最后会给出github的地址。

这里只给出最后的使用代码:/**

* 添加下划线

*/addUnderline = () => {  this.replaceSelectedStrByEle(styles['custom-underline'])

}/**

* 启用荧光笔

*/enableNiteWriterPen = () => {  this.replaceSelectedStrByEle(styles['nite-writer-pen'])

}/**

* 用元素替换被选中的文本

*/replaceSelectedStrByEle = (className) => {  var getRange = () => {    var me = window;    var range = new Range(me.document);    var sel = window.getSelection();    if (sel && sel.rangeCount) {      var firstRange = sel.getRangeAt(0);      var lastRange = sel.getRangeAt(sel.rangeCount - 1);

range.setStart(firstRange.startContainer, firstRange.startOffset)

.setEnd(lastRange.endContainer, lastRange.endOffset);

}    return range

}  var range = getRange();

range.applyInlineStyle('i', {    class: className

});

range.select();

}

使用起来还是比较简单的。

对i元素的处理做的一些修改

如果我们选中的是已经被包裹在i元素中的一段文本,那么调用后会发现并没有加上class属性。

这是因为富文本编辑器和我们的需求不一样,通过操作想把选中文本变为i元素,而文本外面本来就是i元素了,自然不会进行剩下的操作。

在填充元素的最后会有一个mergeToParent的操作,他会在填充元素的标签和其父级元素的标签一样后将元素替换为文本。if (parent.tagName == node.tagName || parent.tagName == "A") {  //...}

那么这里我们要修改源码加上一个判断if ((parent.tagName == node.tagName && parent.className == node.className) || parent.tagName == "A") {  //...}

至于其它的逻辑保持不变即可。

为支持回退操作做的一些修改

这里getRange拿到的对象range在选中内容并替换样式后依然可以使用。

可以调用range.removeInlineStyle('i')

移除之前添加的样式。

也就是说这里如果使用一个命令模式之类的,是可以实现回退操作的。

不过这里还是有一个坑,就是removeInlineStyle会移除掉选中内容中所有的i元素,于是我修改了

Range.js中removeInlineStyle这个方法,多加了一个className参数,每次去掉i元素时都会判断是否参数等于className。

然后我们调用时就是range.removeInlineStyle('i',styles['nite-writer-pen'])

总结

作为一个暗藏天坑的小需求,搞定之后其实还挺有成就感的。

粗略阅读了源码后才发现如果自己做会有多坑,基本上没个三五天下不来,并且在多掉了N根头发后仍然会发现到处都是考虑不周和各种BUG。

java实现给选中文字添加样式,天坑之路:用js给选中文字添加样式相关推荐

  1. 在css样式中隐藏元素,用JS改变的元素CSS样式,css里display :none 隐藏 block 显示

    CSS样式的引用有3种方式:style引用.class引用.id引用,所以js改变元素的样式我们也分3种来说. 1.js改变由style方式引用的样式: 方法一:document.divs.style ...

  2. EasyExcel 批量设置单元格样式(字体样式、底纹样式、边框样式、对齐方式、自动换行、旋转文字、竖向文字、数据格式、自动收缩)

    目录 1 Maven配置 2 CellStyleModel 3 CustomCellStyleHandler 4 调试代码 5 调试结果 注: 1 Maven配置 <!--hutool工具包-- ...

  3. html 点击文本框则选中,JS事件 内容选中事件(onselect)选中事件,当文本框或者文本域中的文字被选中时,触发onselect事件,同时调用的程序就会被执行。...

    内容选中事件(onselect) 选中事件,当文本框或者文本域中的文字被选中时,触发onselect事件,同时调用的程序就会被执行. 如下代码,当选中用户文本框内的文字时,触发onselect 事件, ...

  4. element 配置全局样式 例如:为项目中所有el-dialog弹窗添加分割线

    先看问题,设计图的el-dialog弹窗在提示文字下面都有一条横线, element框架自带的el-dialog弹窗缺少提示文字下面的那个分割横线. 为了避免重复编写样式,我们为el-dialog添加 ...

  5. ppt转html并编辑文字,ppt转视频加字幕和音乐 制作的ppt添加精美的文字说明 然后添加温和背景音乐...

    新年假期已经结束啦,转眼今天又到了开始奋斗的日子,首先祝大家开工大吉哈.个人是感觉过了个假年的,总感觉假期还没开始就已经结束了,真是开工日黑眼圈+胖三圈,不想上班+不敢上班+上不动班....可恨的是还 ...

  6. css3 骨架屏样式_在我们的骨架页面构建中添加样式

    css3 骨架屏样式 在我们正在进行的构建过程的这一部分中,我们将专注于样式化HTML. 本教程将分为三个主要部分: 添加图像 隐藏字体到Web字体 样式各部分 让我们快速提醒一下自己到目前为止所构建 ...

  7. 学习整理fabric.js更换画布文字元素字体样式

    学习整理fabric.js更换画布文字元素字体样式 原图 效果图 实现代码 index.html script.js 生成本地图 原图 效果图 实现代码 index.html <!DOCTYPE ...

  8. R语言ggplot2可视化:ggplot2可视化使用labs函数为可视化图像添加(caption)图片说明文字、theme_bw中指定参数base_size来改变图片说明文字、轴标签等的大小

    R语言ggplot2可视化:ggplot2可视化使用labs函数为可视化图像添加(caption)图片说明文字.theme_bw中指定参数base_size来改变图片说明文字.轴标签等的大小 目录

  9. 不让复制是不可能的----js获取选中文字

    在360百科.知乎上经常会遇见禁止复制文本的情形,这能挡住一部分人复制,却挡不住程序员的复制. HTML都给我了,难道一小段文本我都拿不下来吗? F12打开控制台,然后选中文本,在控制台下粘贴以下代码 ...

最新文章

  1. 全面升级!星环科技基础软件再升级,赋能数字中国建设
  2. MySQL太细碎了,我硬生生捋出了一条核心大主线!
  3. vue结合Promise及async实现高效开发。
  4. 7-1 叶节点求和 (30 分)
  5. 数据结构排序1-冒泡,选择,插入排序
  6. ubuntu下go插件delve下载安装
  7. 【病毒分析】——熊猫烧香 专杀工具C源码
  8. windows64位首次安装git
  9. 论文格式修改之英文摘要
  10. 三阶魔方大中小魔公式_七步玩转三阶魔方还原公式图解 + 视频
  11. 对话现实版“谢耳朵”:多重宇宙和引力、超弦理论、暗能量
  12. oppo开启系统更新服务器,oppo手机系统升级开不了机怎么办
  13. 黑客技术入门|“知已知彼,百战不殆”——踩点
  14. 【转】PCB布线技巧
  15. 人月神话是神话嘛?嗯!
  16. spring中依赖注入方式总结
  17. 一个简单的小游戏——“数字炸弹”的实现
  18. Verilog基础知识(异步FIFO)
  19. oracle oats 工具讲解,Oracle 表空间基本操作
  20. 计算机课电子商务那一题,[计算机]电子商务试题.pdf

热门文章

  1. lucene-solr源码编译导入eclipse--转
  2. 数据挖掘导论读书笔记7 Apriori算法
  3. 使用Skywalking实现全链路监
  4. Linux文件查找工具之find “大宝剑”--转载
  5. redis 控制调用频率
  6. Linux shell的和||--转载
  7. 中台创业潮起,你中台创业了吗?
  8. 深入理解分布式技术 - ServiceMesh 服务网格
  9. Spring Cache-缓存注解(二)
  10. 实战SSM_O2O商铺_29【商品】商品添加之Service层的实现及重构