天天看點

JavaScript之Range--或許會有點用

Range是JavaScript的内置對象,一般來講用到的地方不是很多,主要是一些互動性比較強的場景可能會用到,比如高亮标注,用不到還好說,如果用到了查資料确實也是比較少的, 是以這裡總結一下筆記,不會太深入。

需要注意的是這裡很多方法都屬于實驗性功能, 是以生産環境使用的使用需要謹慎,具體可以參考MDN。這裡不贅述了。

range的應用場景

這類相對比較生僻的api應用常見并不是很多, 這裡我們先了解一下range的應用場景.

  1. 就是常見的高亮标注電子書之類的
  2. 人工标注機器學習所需的基礎文檔(我所做的)

當然應該也有很多其他場景, 我也沒怎麼接觸. 有興趣的可以自行了解

Range是什麼

顧名思義,Range其實可以認為是一個選中的文字範圍, 但是Range又不依賴于滑鼠選中, 我們可以自行構造或者克隆。不過在細說Range之前我們先了解一下Selection。

如圖當我們選中一段文字時, 我們就以通過

window.getSelection

來擷取Selection對象

JavaScript之Range--或許會有點用
JavaScript之Range--或許會有點用

Selection可以

window.getSelection().toString()

直接擷取選中的文字, 但是很多時候我們并不是要擷取選中的文字,而是要得到選中文字所在位置并将其存儲起來。這時候就是Range發揮作用的地方了。

window.getSelection().getRangeAt(0)

可以将一個Selection對象轉化為Range對象。

JavaScript之Range--或許會有點用

如上圖,我們知道Dom元素排列是一段一段的, 這裡的container就是指的每個段,offset就是選中的位置。Range肯定是連續的,這樣我們就可以定位一段完整的Range。

Range的存儲

如果作為高亮, Range必然是要存到伺服器上的, 但是作為js對象, Range不可以直接存到資料庫裡,這時候就要對Range進行一定的處理了。

上面提到過Range是可以手動建立的:

document.createRange

var range = document.createRange();

range.setStart(startNode, startOffset);
range.setEnd(endNode, endOffset);           

這裡

startNode

指startContainer, 自然就是指dom元素了,看到這裡其實大家心裡也該有點方案了,不好存儲的無非就是dom元素了,那我們将dom元素轉為選擇器存起來就好了, 到時再通過選擇器擷取dom元素。

當然我們也有其他選擇: xpath, 主要是我接手項目的時候就是利用的xpath, 将dom轉為xpath的代碼如下:

// 擷取一個元素的xpath
function getElementXPath (element) {
  if (!element) return null

  if (element.id) {
    return `//*[@id=${element.id}]`
  } else if (element.tagName === 'BODY') {
    return '/html/body'
  } else {
    const sameTagSiblings = Array.from(element.parentNode.childNodes)
      .filter(e => e.nodeName === element.nodeName)
    const idx = sameTagSiblings.indexOf(element)

    return getElementXPath(element.parentNode) +
      '/' +
      element.tagName.toLowerCase() +
      (sameTagSiblings.length > 1 ? `[${idx + 1}]` : '')
  }
}
           

将xpath轉化為Range:

function createRangeFromXPathRange (xpathRange) {
    var startContainer,
      endContainer,
      endOffset,
      evaluator = new XPathEvaluator()

    // must have legal start and end container nodes
    startContainer = evaluator.evaluate(
      xpathRange.startContainerPath,
      document.documentElement,
      null,
      XPathResult.FIRST_ORDERED_NODE_TYPE,
      null
    )
    if (!startContainer.singleNodeValue) {
      return null
    }

    if (xpathRange.collapsed || !xpathRange.endContainerPath) {
      endContainer = startContainer
      endOffset = xpathRange.startOffset
    } else {
      endContainer = evaluator.evaluate(
        xpathRange.endContainerPath,
        document.documentElement,
        null,
        XPathResult.FIRST_ORDERED_NODE_TYPE,
        null
      )
      if (!endContainer.singleNodeValue) {
        return null
      }

      endOffset = xpathRange.endOffset
    }

    // map to range object
    var range = document.createRange()
    range.setStart(startContainer.singleNodeValue, xpathRange.startOffset)
    range.setEnd(endContainer.singleNodeValue, endOffset)
    return range
  }           

原文釋出時間為:2018年06月02日

原文作者:_xiadd_

本文來源: 

掘金 https://juejin.im/entry/5b3a29f95188256228041f46

如需轉載請聯系原作者

繼續閱讀