替换HTML,然后将文档片段添加回HTML。

3
我正在尝试创建一个类似以下方式工作的荧光笔工具:
- 用户首先选择一段文本。 - 然后单击其中一个颜色按钮,所选文本的背景色将被突出显示。 - 然后他们选择另一段文本... - 现在当他们单击按钮时,所有HTML都将替换为HTML的缓存版本(没有高亮显示)。 - 然后新选择的已突出显示的文本将附加回新的HTML中。
这样只有一段文本可以被突出显示。
问题:
我很难理解 Range Selection Node API。
目前,我无法将突出显示的文本添加回新的HTML中...我只将其附加到了document.body中。
我目前的进展是:

https://jsfiddle.net/4mb39jd6/

(function(){

  var highlighter = {

    /**
     *
     */
    init: function(){
      this.cacheDOM();
      this.bindEvents();
    },

    /**
     *
     */
    cacheDOM: function(){
      this.$html           = $('.content').html();
      this.$content        = $('.content');
      this.$highlighter    = $('.highlighter');
    },

    /**
     *
     */
    bindEvents: function(){
      this.$highlighter.on('mousedown', this.highlightSelection.bind(this));
    },

    /**
     *
     */
    highlightSelection: function(e){

      var selection = window.getSelection();          // get selection
      var text      = selection.toString();           // get selected text
      var newNode   = document.createElement('span'); // create node

      newNode.style.backgroundColor = $(e.target).css('backgroundColor'); // set node properties
      newNode.appendChild(document.createTextNode(text));                 // append selection text to node

      var range = selection.getRangeAt(0);      // 2 - get the selected range
      range.deleteContents();                   // delete the contents
      range.insertNode(newNode);                // insert the new node with the replacement text
      documentFragment = range.cloneContents(); // clone the node

      this.$content.html(this.$html);              // refresh the content
      document.body.appendChild(documentFragment); // add the new highlighted text
    },
  };
  highlighter.init();

})();

问题:

如何将我高亮的节点添加回一个新的HTML文档中,它看起来像这样<span style="background-color: rgb(255, 255, 131);">一些随机文本</span>,以便它在相同的位置。


你想克隆整个内容有什么特别的原因吗?在highlightSection中注释掉最后三行似乎可以产生所需的行为... https://jsfiddle.net/wxrxf6r1/ - user3297291
是的,原因是我只想在任何给定时刻选择一个选定的文本部分...因为现在我可以随处突出显示...我只想能够一次选择一个范围。 - Jethro Hazelhurst
1
你看过mark.js了吗? - dude
我看到...出于教育目的,试图以最基本的方式实现这个功能...但如果我无法找到可行的解决方案,我会更加深入地研究这个问题。 - Jethro Hazelhurst
1个回答

1
如果目标是每次只有一个突出显示,我会选择一种不那么复杂的方法:
  • 添加突出显示时,
  • 检查之前的突出显示所在的html,
  • 找到后删除它。
为此,请使用属性或类(或更好地,存储引用)标记您的突出显示
newNode.classList.add("js-highlight");

添加一个方法以移除这样的元素:
clearHighlight: function() {
  var selection = document.querySelector(".js-highlight");

  if (selection) {
    selection.parentElement.replaceChild(
      document.createTextNode(selection.innerText),
      selection  
    );
  }
}

然后,在用高亮元素替换您的range之前,请调用clearHighlight
例如:https://jsfiddle.net/2tqdLfb1/ 另一种方法: 我还尝试了另一种方法,它遵循了您的“缓存HTML”逻辑,但发现它过于复杂。该方法的基本原理如下:
  • 检查选择区域的父元素的查询路径
  • 存储选择的起始索引和结束索引
  • 用缓存的HTML字符串替换HTML
  • 通过存储的查询路径找到新注入的选择区域的父元素
  • 根据选择区域的起始和结束索引将其innerText分成1、2或3个textNodes
  • 用高亮的<span>替换表示选择的textNode
以下是一个示例,显示了如何存储范围祖先的查询路径:
function getPathUpToSelector(selector, element, path) {
  var parent = element.parentElement;

  if (parent && !element.matches(selector)) {
    var index = Array.from(parent.children).indexOf(element) + 1;
    path.push("*:nth-child(" + index + ")");

    return getPathUpToSelector(selector, parent, path);
  }

  return [selector].concat(path).join(" > ");
}

谢谢!这太棒了。我一直在过度复杂化事情,而这个方法很好用。现在唯一的问题是,如果我选择跨越两个节点的文本,则HTML标记会被破坏...我已经在这里发起了一个新的问题:http://stackoverflow.com/questions/42656501/serializing-html-placing-span-elements-around-selected-text-while-preserving-th - Jethro Hazelhurst

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接