Range是JavaScript的内置對象,一般來講用到的地方不是很多,主要是一些互動性比較強的場景可能會用到,比如高亮标注,用不到還好說,如果用到了查資料确實也是比較少的, 是以這裡總結一下筆記,不會太深入。
需要注意的是這裡很多方法都屬于實驗性功能, 是以生産環境使用的使用需要謹慎,具體可以參考MDN。這裡不贅述了。
range的應用場景
這類相對比較生僻的api應用常見并不是很多, 這裡我們先了解一下range的應用場景.
- 就是常見的高亮标注電子書之類的
- 人工标注機器學習所需的基礎文檔(我所做的)
當然應該也有很多其他場景, 我也沒怎麼接觸. 有興趣的可以自行了解
Range是什麼
顧名思義,Range其實可以認為是一個選中的文字範圍, 但是Range又不依賴于滑鼠選中, 我們可以自行構造或者克隆。不過在細說Range之前我們先了解一下Selection。
如圖當我們選中一段文字時, 我們就以通過
window.getSelection
來擷取Selection對象

Selection可以
window.getSelection().toString()
直接擷取選中的文字, 但是很多時候我們并不是要擷取選中的文字,而是要得到選中文字所在位置并将其存儲起來。這時候就是Range發揮作用的地方了。
window.getSelection().getRangeAt(0)
可以将一個Selection對象轉化為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如需轉載請聯系原作者