vue 选中文字添加标注,在线标注window.getSelection()

实现效果:通过 鼠标松开事件highlight监听选中文本,文本选中后可以通过window.getSelection()拿到选中的文本数据;同时可以通过鼠标松开时的位置,调用this.setBoxPosition(e.pageX, e.pageY)事件计算想要弹出的标签弹窗的位置。标签弹窗中给每一个标签添加点击事件labelmenuchose(id, item)(标签弹窗的html代码没有贴出,可以根据自身需要自行定义),点击选择标签后触发事件labelmenuchose(id, item),[id是我在点击标签时传入的时间戳,准备用来给标注span标签和i标签的id选择器命名用的,保证他们id的一致性,为了后面的删除]。点击事件触发后,在点击事件内部,对选中文本右边添加标注,整个标注内容用span标签包裹,选中文本背景色变成标签背景色的百分之二十五,单击标签,可以对标签进行修改,点击删除按钮实现删除功能

html部分 后端传入文本,前端拿到文本通过v-html进行渲染

<template>
        <div
                id="annotateContent"
                style="text-align: left; font-size: 16px; padding: 20px"
                type="textarea"
                v-html="textMgs.content"
                @mouseup.stop="highlight($event)"
                v-if="textrender"
              ></div>
</template>

js部分

扫描二维码关注公众号,回复: 17280897 查看本文章
<script>
import $ from "jquery";

export default {
  data() {
   return {
   textMgs: {
        content: "",
        detailId: null,
      },
   textrender: true,
   sel: null,
   range: null,
   labeldialog: false,
   editbutton: false,//已标注在文本的标签单击后会把editbutton变成true,然后再次弹出标签选择弹框,可以重新选择标签把旧的标签替换掉,这个代码忘写了,算了,但那不重要,没有标签修改功能的直接走else就可以了
          }
           },
methods:{
// 选中标注文本
    highlight(e) {
      if (!window.getSelection().toString() || this.labels.length == 0) {
        this.labeldialog = false; //选中文本后出现标注列表弹框
        return;
      }

      this.sel = window.getSelection();
      this.range = this.sel.getRangeAt(0);
      // 判断选中文本中是否包含子元素  如果需要阻止重叠标注可使用
      // let span = document.querySelectorAll(".highlight ");
      // for (let i = 0; i < span.length; i++) {
      //   if (this.sel.containsNode(span[i])) {
      //     this.labeldialog = false;
      //     return;
      //   }
      // }

      //计算选中文本在屏幕中的位置,然后弹出标签列表弹窗
        this.setBoxPosition(e.pageX, e.pageY);
    },

// 计算标注标签栏弹出的位置
    setBoxPosition(X, Y) {
      var labellist = document.querySelector(".labelbutton2");
      const maxHeight = document.body.clientHeight;
      let height = Y + 10;
      if (maxHeight < height + 248) {
        height = height - 268;
      }
      labellist.style.cssText = `height:248px;overflow:auto;width:200px;position:fixed;left:${X + 10}px;top:${height}px;background:#fff;box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);border-radius: 4px`;
      //弹出标签列表
      this.labeldialog = true;
    },

  // 选择标注标签
    labelmenuchose(id, item) {
      // 修改标签
      if (this.editbutton) {  //这个是标注后单击标签,实现修改标签,不需要可以直接走else
        let changeid = this.editbutton_spanid;
        let annotateContent =
          document.getElementById(changeid).parentNode.parentNode.parentNode;
        let span = document.getElementById(changeid).parentNode.parentNode;
        span.removeChild(document.getElementById(changeid).parentNode);
        const newspan = document.createElement("span");
        newspan.className = "highlight";
        let delicon = document.createElement("i");
        delicon.setAttribute("id", item.tagId + "-" + new Date().getTime());
        // // 删除标签
        delicon.addEventListener("click", (a) => {
          this.deleteById(a.currentTarget.id);
          this.delMarking.visible = false;        });
        delicon.setAttribute("class", "el-icon-close");
        delicon.setAttribute("style", this.deliconStyle());
        let color = item.tagColor + "40";
        newspan.innerHTML = `<em style="background:${color};font-style: normal;">${span.innerText.trim()}</em><button style="border:1px solid #ccc;position:relative;vertical-align: super;background:${
          item.tagColor
        };color:#fff">${item.tagName}</button>`;
        newspan.lastChild.appendChild(delicon);
        annotateContent.insertBefore(newspan, span);
        annotateContent.removeChild(span);
        this.labeldialog = false;
        this.editbutton = false;
        this.editbutton_spanid = null;
      } else {
        // 添加标签
        if (!this.textchose) { //如果选中文本为空,直接返回
          return;
        }
        this.addMarking(id, item, this.sel, this.range);//标注时间
        var labellist = document.querySelector(".labelbutton2");
        
        labellist.style.cssText = `position:fixed;left:-999px;top:-999px;background:#fff`;
        this.labeldialog = false;
        this.highLightTableMsg(
          $("#annotateContent").html(),
        );
      }
    },

// 在文本中生成标注
    addMarking(id, item, sel, range) {
      // 限制标注内嵌套标注
      let startContainer = range.startContainer;
     
      const span = document.createElement("span");
      span.className = "highlight";
      let delicon = document.createElement("i");

      delicon.setAttribute("id", id);
      // 删除标签
      delicon.addEventListener("click", (a) => {
        // deleteById(id);
        this.labeldialog = false;
        this.deleteById(id);
        this.delMarking.visible = false;
        a.stopPropagation();
      });

      delicon.setAttribute("class", "el-icon-close");
      delicon.setAttribute("style", this.deliconStyle());
      try{
      range.surroundContents(span); //把指定节点插入选区的起始位置,然后把指定节点的内容替换为选区的内容。
      }catch(e){
       window.getSelection().removeAllRanges();
      this.sel = null;
      this.range = null;
      this.textchose = null;
      this.labeldialog = false;
      return;
      }
      let color = item.tagColor + "40";
      span.innerHTML = `<em style="background:${color};font-style: normal;">${span.innerHTML}</em><button style="border:1px solid #fff;background:#fff;padding: 0 2px;border-radius: 2px;position:relative;vertical-align: super;background:${item.tagColor}!important;color:#fff">${item.tagName}</button>`;
      span.lastChild.appendChild(delicon);
      window.getSelection().removeAllRanges();
      this.sel = null;
      this.range = null;
      this.textchose = null;
    },

}
               }
</script>

如果还需要删除标记功能,需要在上面的methods插入下面的方法

// 删除标记
    deleteById(id) {
     
      let annotateContent =
        document.getElementById(id).parentNode.parentNode.parentNode;
     
      let span = document.getElementById(id).parentNode.parentNode;
      let button = document.getElementById(id).parentNode;
      
      let emtext = "";
     
      if (span.firstChild.getElementsByTagName("span").length > 0) {  //判断标注内是否还嵌套有标注,有走if,没有走else(其实这里只需要if就可以了,写的时候可以把代码简化一下)
       
        let kk = span.firstChild.innerHTML
        let newSpan = document.createElement('span');
        newSpan.className = "tagReplace";
        span.removeChild(button);
        annotateContent.insertBefore(newSpan, span);
        annotateContent.removeChild(span);
        annotateContent.innerHTML = annotateContent.innerHTML.replace('<span class="tagReplace"></span>', kk);
      } else {
        emtext = span.firstChild.innerText.trim();
        span.removeChild(button);
        let textNode = document.createTextNode(emtext);
        annotateContent.insertBefore(textNode, span);
        annotateContent.removeChild(span);
      }
     
      window.getSelection().removeAllRanges();
      this.labeldialog = false;
    },

因为我这边保存是直接把整个文本返回给后端,再次获取已标注的结果的话,也是后端直接把一整个文本返回给我的,所以为了保证渲染完已标注过的文本还能正常实现删除功能、标签修改功能,需要在文本获取成功后再次添加上删除的点击事件

 // 给已标注文件添加点击事件
    addLabelDel() {
      let del = document.querySelectorAll("#annotateContent span button i");
      // let button = document.querySelectorAll("#annotateContent span button")
      $("#annotateContent").on("click", "span button", (e) => {
        //通过事件委托完成,有效
        if (this.labels.length > 0) {
          this.editbutton = true;
          this.setBoxPosition(e.pageX, e.pageY);
          this.editbutton_spanid = e.currentTarget.lastElementChild.id;
          //操作this
          // ...
        }
      });
     for (var i = 0; i < del.length; i++) {
          del[i].addEventListener("click", (a) => {
            this.labeldialog = false;
            this.deleteById(a.currentTarget.id);
            a.stopPropagation();
          });
        }
    },

猜你喜欢

转载自blog.csdn.net/weixin_48433218/article/details/124469166