当前位置:   article > 正文

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

vue标注插件

实现效果:通过 鼠标松开事件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进行渲染

  1. <template>
  2. <div
  3. id="annotateContent"
  4. style="text-align: left; font-size: 16px; padding: 20px"
  5. type="textarea"
  6. v-html="textMgs.content"
  7. @mouseup.stop="highlight($event)"
  8. v-if="textrender"
  9. ></div>
  10. </template>

js部分

  1. <script>
  2. import $ from "jquery";
  3. export default {
  4. data() {
  5. return {
  6. textMgs: {
  7. content: "",
  8. detailId: null,
  9. },
  10. textrender: true,
  11. sel: null,
  12. range: null,
  13. labeldialog: false,
  14. editbutton: false,//已标注在文本的标签单击后会把editbutton变成true,然后再次弹出标签选择弹框,可以重新选择标签把旧的标签替换掉,这个代码忘写了,算了,但那不重要,没有标签修改功能的直接走else就可以了
  15. }
  16. },
  17. methods:{
  18. // 选中标注文本
  19. highlight(e) {
  20. if (!window.getSelection().toString() || this.labels.length == 0) {
  21. this.labeldialog = false; //选中文本后出现标注列表弹框
  22. return;
  23. }
  24. this.sel = window.getSelection();
  25. this.range = this.sel.getRangeAt(0);
  26. // 判断选中文本中是否包含子元素 如果需要阻止重叠标注可使用
  27. // let span = document.querySelectorAll(".highlight ");
  28. // for (let i = 0; i < span.length; i++) {
  29. // if (this.sel.containsNode(span[i])) {
  30. // this.labeldialog = false;
  31. // return;
  32. // }
  33. // }
  34. //计算选中文本在屏幕中的位置,然后弹出标签列表弹窗
  35. this.setBoxPosition(e.pageX, e.pageY);
  36. },
  37. // 计算标注标签栏弹出的位置
  38. setBoxPosition(X, Y) {
  39. var labellist = document.querySelector(".labelbutton2");
  40. const maxHeight = document.body.clientHeight;
  41. let height = Y + 10;
  42. if (maxHeight < height + 248) {
  43. height = height - 268;
  44. }
  45. 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`;
  46. //弹出标签列表
  47. this.labeldialog = true;
  48. },
  49. // 选择标注标签
  50. labelmenuchose(id, item) {
  51. // 修改标签
  52. if (this.editbutton) { //这个是标注后单击标签,实现修改标签,不需要可以直接走else
  53. let changeid = this.editbutton_spanid;
  54. let annotateContent =
  55. document.getElementById(changeid).parentNode.parentNode.parentNode;
  56. let span = document.getElementById(changeid).parentNode.parentNode;
  57. span.removeChild(document.getElementById(changeid).parentNode);
  58. const newspan = document.createElement("span");
  59. newspan.className = "highlight";
  60. let delicon = document.createElement("i");
  61. delicon.setAttribute("id", item.tagId + "-" + new Date().getTime());
  62. // // 删除标签
  63. delicon.addEventListener("click", (a) => {
  64. this.deleteById(a.currentTarget.id);
  65. this.delMarking.visible = false; });
  66. delicon.setAttribute("class", "el-icon-close");
  67. delicon.setAttribute("style", this.deliconStyle());
  68. let color = item.tagColor + "40";
  69. 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:${
  70. item.tagColor
  71. };color:#fff">${item.tagName}</button>`;
  72. newspan.lastChild.appendChild(delicon);
  73. annotateContent.insertBefore(newspan, span);
  74. annotateContent.removeChild(span);
  75. this.labeldialog = false;
  76. this.editbutton = false;
  77. this.editbutton_spanid = null;
  78. } else {
  79. // 添加标签
  80. if (!this.textchose) { //如果选中文本为空,直接返回
  81. return;
  82. }
  83. this.addMarking(id, item, this.sel, this.range);//标注时间
  84. var labellist = document.querySelector(".labelbutton2");
  85. labellist.style.cssText = `position:fixed;left:-999px;top:-999px;background:#fff`;
  86. this.labeldialog = false;
  87. this.highLightTableMsg(
  88. $("#annotateContent").html(),
  89. );
  90. }
  91. },
  92. // 在文本中生成标注
  93. addMarking(id, item, sel, range) {
  94. // 限制标注内嵌套标注
  95. let startContainer = range.startContainer;
  96. const span = document.createElement("span");
  97. span.className = "highlight";
  98. let delicon = document.createElement("i");
  99. delicon.setAttribute("id", id);
  100. // 删除标签
  101. delicon.addEventListener("click", (a) => {
  102. // deleteById(id);
  103. this.labeldialog = false;
  104. this.deleteById(id);
  105. this.delMarking.visible = false;
  106. a.stopPropagation();
  107. });
  108. delicon.setAttribute("class", "el-icon-close");
  109. delicon.setAttribute("style", this.deliconStyle());
  110. try{
  111. range.surroundContents(span); //把指定节点插入选区的起始位置,然后把指定节点的内容替换为选区的内容。
  112. }catch(e){
  113. window.getSelection().removeAllRanges();
  114. this.sel = null;
  115. this.range = null;
  116. this.textchose = null;
  117. this.labeldialog = false;
  118. return;
  119. }
  120. let color = item.tagColor + "40";
  121. 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>`;
  122. span.lastChild.appendChild(delicon);
  123. window.getSelection().removeAllRanges();
  124. this.sel = null;
  125. this.range = null;
  126. this.textchose = null;
  127. },
  128. }
  129. }
  130. </script>

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

  1. // 删除标记
  2. deleteById(id) {
  3. let annotateContent =
  4. document.getElementById(id).parentNode.parentNode.parentNode;
  5. let span = document.getElementById(id).parentNode.parentNode;
  6. let button = document.getElementById(id).parentNode;
  7. let emtext = "";
  8. if (span.firstChild.getElementsByTagName("span").length > 0) { //判断标注内是否还嵌套有标注,有走if,没有走else(其实这里只需要if就可以了,写的时候可以把代码简化一下)
  9. let kk = span.firstChild.innerHTML
  10. let newSpan = document.createElement('span');
  11. newSpan.className = "tagReplace";
  12. span.removeChild(button);
  13. annotateContent.insertBefore(newSpan, span);
  14. annotateContent.removeChild(span);
  15. annotateContent.innerHTML = annotateContent.innerHTML.replace('<span class="tagReplace"></span>', kk);
  16. } else {
  17. emtext = span.firstChild.innerText.trim();
  18. span.removeChild(button);
  19. let textNode = document.createTextNode(emtext);
  20. annotateContent.insertBefore(textNode, span);
  21. annotateContent.removeChild(span);
  22. }
  23. window.getSelection().removeAllRanges();
  24. this.labeldialog = false;
  25. },

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

  1. // 给已标注文件添加点击事件
  2. addLabelDel() {
  3. let del = document.querySelectorAll("#annotateContent span button i");
  4. // let button = document.querySelectorAll("#annotateContent span button")
  5. $("#annotateContent").on("click", "span button", (e) => {
  6. //通过事件委托完成,有效
  7. if (this.labels.length > 0) {
  8. this.editbutton = true;
  9. this.setBoxPosition(e.pageX, e.pageY);
  10. this.editbutton_spanid = e.currentTarget.lastElementChild.id;
  11. //操作this
  12. // ...
  13. }
  14. });
  15. for (var i = 0; i < del.length; i++) {
  16. del[i].addEventListener("click", (a) => {
  17. this.labeldialog = false;
  18. this.deleteById(a.currentTarget.id);
  19. a.stopPropagation();
  20. });
  21. }
  22. },

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/人工智能uu/article/detail/903210
推荐阅读
相关标签
  

闽ICP备14008679号