阅读 203

文本标记 - 用户从网页上选择文本高亮

在上一篇文章 文本标记 - 高亮标记多个指定位置的文本 中, 说明了如何高亮后台返回的指定位置的多个数据. 现在解释下一个需求: 用户在高亮后台返回的数据的基础上, 从页面上选择文本高亮, 并且在表格中显示文本

上篇文章说到高亮的效果: 一般都是给指定的文本包裹一个标签 ( 如  ), 在给这个标签设置一个样式 ( 比如添加背景色 ), 这是实现这个需求的基础.

ps: 如果只有用户从网页上选择文本的需求, 可以考虑用 web-highlighter 插件, 比起自己手写方法来要简便一些.

首先介绍一下这个需求的逻辑:

  1. 点这个知识抽取的按钮, 然后左边文本高亮, 右面的表格填充 (实体这一列为只读状态), (有多个表格, 采取了不同的高亮样式)

在这里插入图片描述

  1. 点击这个按钮, 对应的高亮消失, 表格变空

在这里插入图片描述 3. 用户从左边文本选择一段, 像这样; 再点击这个对钩, 就会填充, 然后获取它对应的下标起始位置, 保存下来, 并且将选中的文本高亮 在这里插入图片描述

这里的难点就是, 因为已经标记了后台返回的那些文本, 然后这些高亮文本都被包裹了span标签. 这时候获取的下标就不是从整个文本对应的下标0开始的了, 因为整个文本被span标签分成了很多部分, 用户标记的时候, 获取的这个下标只是这个分段里的下标, 但是我们要传给后端相对于整篇文本的下标, 这就是难点.

通过对不同位置的selection对象的研究 , 发现了以下几个不同点:

  1. 普通文本的

a) parentNode是 <div>...</div> b) previousSibling是前一个已经标记的元素节点, 元素节点的nodeType=1, className可以看到该元素节点的类名;  nextSibling也是已标记的元素节点

  1. 已标记文本的

a) parentNode是当前标签, eg. <span>纯电动汽车</span> b) previousSibling 和 nextSibling都是null

  1. 判断是否是开头的普通文本:

a) parentNode是整个div

后台返回的某个高亮本文对象: 在这里插入图片描述

思路

  1. 首先通过baseNode的parentNode是div还是span判断是普通文本还是标记文本,

  2. 如果是普通文本, 通过previousSibling寻找前面的元素节点:

a) 如果返回null => 说明是开头的元素, 直接用 开始位置: baseOffset 结束位置: baseOffset+length-1 b) 如果返回某个元素节点 => 通过className判断是table1还是table2里的元素 (项目中不只有右边一个表格, 这几个表格中使用了不同的高亮样式), 然后 前兄弟节点的end+1+baseOffset 就是用户选择的selection相对于整个文本的开始位置, 结束位置可以是: 开始位置+选中文本.length-1 3) 如果是标记文本, 根据它的className判断是table1还是table2中的元素, 获取对应的start. 开始位置: start+baseOffset 结束位置: 开始位置+选中文本.length-1

代码

// 知识抽取按钮     knowExtract() {       if (this.isEdit) {         this.$alert("请先保存文本");       } else {         const _key = this.extract_key;         console.log(_key);         knowiExtract(_key).then((res) => {           console.log(res);           if (res.status == 0) {             // 知识抽取成功             this.$message.success(res.message);             // 表1数据渲染             this.tableData1 = res.entity;             console.log(this.tableData1);             // 表2数据渲染             // this.tableData2 = res.attribute             // 表1文本高亮             this.highlightEntity();           } else {             this.$message.error("知识抽取失败");           }         });       }     },     // 表1 文本高亮     highlightEntity() {       console.log("文本高亮");       let text = this.fixedText       let textList = text.split("");       // 表1中的实体高亮       this.tableData1.forEach((item) => {         let startNum = item.start;         let endNum = item.end + 1;         console.log(startNum,endNum)         if (startNum == endNum - 1) {           textList[startNum] = `<span>${textList[startNum]}</span>`;         } else {           textList[startNum] = `<span>${textList[startNum]}`;           textList[endNum - 1] = `${textList[endNum - 1]}</span>`;         }       });       // 表2 中的文本高亮       this.text = textList.join("");     },     // 表1 实体高亮初始化 (编辑按钮)     initEntiExtract(i) {       this.$message.warning("请从前向后选择文本内容")       console.log("实体高亮初始化" + i);       this.tableData1[i].entity = ""       this.tableData1[i].start = ""       this.tableData1[i].end = ""       console.log(this.tableData1)       this.highlightEntity()       this.selection = window.getSelection();     },     // 表1 实体抽取  (√按钮)     entityExtract(i) {       console.log(this.selection);       let newEntiry = this.selection.toString();       // 保证起始位置小于结束位置 (用户倒选的操作)(未完成)              // 获取截取开头的baseNode       let startNode = this.selection.baseNode       // 根据parentNode是div还是span判断是普通文本还是标记文本       let startParent = startNode.parentNode.nodeName       console.log("parentNode 父节点")       console.log(startParent)       if (startParent == "DIV") {         // 普通文本, 通过previousSibling寻找前面的元素节点         let preSib = startNode.previousSibling         console.log("前兄弟节点")         console.log(preSib)         if (!preSib) {           console.log("前兄弟节点不存在")           // 如果返回null => 说明是开头的元素, 直接用开始位置: baseOffset,结束位置: baseOffset+length-1           this.tableData1[i].start = this.selection.baseOffset           this.tableData1[i].end = this.selection.baseOffset + newEntiry.length - 1           console.log("起始位置")           console.log(this.tableData1[i].start, this.tableData1[i].end)         } else {           let preSibClass = preSib.className           console.log("前兄弟节点的className")           console.log(preSibClass)           if (preSibClass == "EntityHighlight") {             // 表1元素             // 寻找前兄弟节点的end位置             let preSibEnd = 0             for(let k=0;k<this.tableData1.length;k++){               if(this.tableData1[k].entity == preSib.innerText){                 console.log(preSib.innerText)                 console.log(this.tableData1[k].end)                 preSibEnd = this.tableData1[k].end               }             }             console.log("前兄弟节点的end位置")             console.log(preSibEnd)             this.tableData1[i].start = preSibEnd + 1 + this.selection.baseOffset             this.tableData1[i].end = this.tableData1[i].start + newEntiry.length - 1             console.log("起始位置")             console.log(this.tableData1[i].start, this.tableData1[i].end)           }         }       } else if (startParent == "SPAN") {         // 标记文本, 根据它的className判断是table1还是table2中的元素         let nodeClass = startNode.className         console.log(nodeClass)         if (nodeClass == "EntityHighlight"){           // 表1元素           // 获取起始位置           for(let j=0;j<this.tableData1.length;j++) {             if( this.tableData1[j].entity = startNode.nodeValue) {               this.tableData1[i].start = this.tableData1[j].start + this.selection.baseOffset               this.tableData1[i].end = this.tableData1[i].start + this.selection.length - 1             }           }         }       }              // 给table1中相应的entity赋值       this.tableData1[i].entity = newEntiry;       console.log(this.tableData1)       this.highlightEntity()     },


作者:Charonmomo
链接:https://juejin.cn/post/7032194743366860813

文章分类
代码人生
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐