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

Replacing HTML then adding a document fragment back into the html

我正在尝试创建一个像这样工作的荧光笔工具:

这样一次只能突出显示一个范围内的文本。

问题:

我很难理解 Range, Selection and 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();

})();

问:

如何将我突出显示的节点...添加回 <span style="background-color: rgb(255, 255, 131);">some random text</span> 新的 html 文档,使其处于相同位置。

如果目标是一次只有一个亮点,我会选择一种不太复杂的方法:

  • 添加高亮时,
  • 检查 html 上一个突出显示,
  • 找到后删除

为此,请使用属性或 class 标记您的突出显示 <span>(或者更好的是,存储参考):

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/

替代方案:

我还尝试了另一种尊重您的 "cached HTML" 逻辑的方法,但发现它过于复杂。该方法的基础知识:

  • 检查所选 parentElement 的查询路径
  • 存储选择的开始索引和结束索引
  • 用缓存的HTML字符串替换HTML
  • 通过存储的query-path找到selection新注入的父元素
  • 根据选择的开始和结束索引将其 innerText 拆分为 1、2 或 3 textNodes
  • 用您的突出显示替换表示选择的 textNode <span>

显示如何存储范围祖先查询路径的示例:

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(" > ");
}