我想獲取用戶選擇中包含的所有元素(如在DOM 2範圍/ MS TextRanges中)。獲取文本選擇中的元素
/** @return {Array.<Element>} */
function getSelectedElements() {
var elements = [];
// get elements in the user selection somehow
return elements;
}
我試着按照蒂姆唐氏優秀solution to a similar question,有的萬盎司和MS文檔,有些PPK東西做到這一點。
該方法基本上是:
定義SelectionLikeObject作爲DOM選擇或IE選擇。
將RangeLikeObject定義爲DOM範圍或IE TextRange。
讓
containerNode
成爲節點。讓
containerElement
成爲元素。讓
containedElements
成爲NodeList。讓
elementRange
成爲RangeLikeObject。讓
selectedRange
成爲RangeLikeObject。讓
selectedElements
成爲一個元素數組。讓
element
成爲元素。讓
selection
成爲SelectionLikeObject。從用戶的選擇中設置
selection
。將
selectedElements
設置爲新的數組。對於每個
selectedRange
在selection
:設置
containerNode
到的selectedRange
共同祖先容器。將
containerElement
設置爲最接近的元素祖先到containerNode
。將
containedElements
設置爲containerElement
的後代列表。對於每個
element
在containedElements
:從
element
設置elementRange
。如果
elementRange
下降selectedRange
的 邊界內的邊界:- 推
element
到selectedElements
。
- 推
的DOM分支看起來是這樣的:
/**
@param {Document} doc
@return {Array.<Element>}
*/
getSelectedElements.fromDOM = function (doc) {
/** @type {Range} */
var selectedRange;
/** @type {Array.<Element>} */
var selectedElements = [];
/** @type {Node} */
var containerNode;
/** @type {Element} */
var containerElement;
/** @type {NodeList} */
var containedElements;
/** @type {Range} */
var elementRange;
/** @type {Element} */
var element;
/** @type {Selection} */
var selection = doc.defaultView.getSelection();
/** @type {number} */
var rangeCount = selection.rangeCount;
/** @type {number} */
var elementCount;
/** @type {number} */
var i;
// hack for browsers without getRangeAt
// see http://www.quirksmode.org/dom/range_intro.html
if (!selection.getRangeAt) {
selection.getRangeAt = function (i) {
/** @type {Range} */
var range = doc.createRange();
if (i || !selection.anchorNode) {
return range;
}
range.setStart(selection.anchorNode, selection.anchorOffset);
range.setEnd(selection.focusNode, selection.focusOffset);
return range;
};
selection.rangeCount = 1;
}
elementRange = doc.createRange();
for (i = 0; i < rangeCount; ++i) {
selectedRange = selection.getRangeAt(i);
containerNode = selectedRange.commonAncestorContainer;
while (containerNode && containerNode.nodeType != 1) {
containerNode = containerNode.parentNode;
}
if (!containerNode) {
return selectedElements; // something went wrong...
}
containerElement = /** @type {Element} */ containerNode;
containedElements = containerElement.getElementsByTagName('*');
elementCount = containedElements.length;
for (var i = 0; i < elementCount; ++i) {
element = containedElements[i];
elementRange.selectNodeContents(element);
if (elementRange.compareBoundaryPoints(selectedRange.END_TO_START, selectedRange) < 1 &&
elementRange.compareBoundaryPoints(selectedRange.START_TO_END, selectedRange) > -1) {
selectedElements.push(element);
}
}
}
elementRange.detach();
return selectedElements;
};
的IE分支看起來是這樣的:
/**
@param {Document} doc
@return {Array.<Element>}
*/
getSelectedElements.fromIE = function (doc) {
// Selection - http://msdn.microsoft.com/en-us/library/ie/dd347133(v=vs.85).aspx
// TextRange - http://msdn.microsoft.com/en-us/library/dd347140(v=vs.85).aspx
// ControlRange - http://msdn.microsoft.com/en-us/library/ie/ms537447(v=vs.85).aspx
/** @type {TextRange|ControlRange} */
var ieRange = doc.selection && doc.selection.createRange();
/** @type {Array.<Element>} */
var selectedElements = [];
/** @type {TextRange} */
var selectedRange;
/** @type {Element} */
var containerElement;
/** @type {NodeList} */
var containedElements;
/** @type {TextRange} */
var elementRange;
/** @type {Element} */
var element;
/** @type {Selection} */
var selection;
/** @type {number} */
var i = -1;
if (ieRange.text === void 0) {
return []; // FIXME: It's a ControlRange, give up.
}
selectedRange = /** @type {TextRange} */ ieRange;
containerElement = selectedRange.parentElement();
containedElements = containerElement.getElementsByTagName('*');
elementRange = doc.body.createTextRange();
while ((element = containedElements[++i])) {
elementRange.moveToElementText(element);
if (elementRange.compareEndPoints("StartToEnd", selectedRange) > -1 &&
elementRange.compareEndPoints("EndToStart", selectedRange) < 1) {
selectedElements.push(element);
}
}
return /** @type {Array.<Element>} */ selectedElements;
};
現在,我想要解決的問題是:如果只選擇元素中的部分文本,它將出現在返回的數組中,即使它僅爲partly selected。
我想添加一個參數,改變行爲只包含完全選定的元素。我有一種感覺,答案在於compareBoundaryPoints,我現在還不明白它的意義。另外,IE代碼目前尚未測試,但請讓我知道是否有任何東西看起來有問題(或DOM分支)。
您可能難以定義「包含」。一種方法可能是從所選文本中刪除所有空格,然後獲取文本內容以獲取極端元素的文本內容,並獲取其空格縮小的文本內容,並查看它是否包含在選擇內容中(測試,匹配或索引應該做的伎倆)。如果是,則選擇整個元素。如果不是,那不是。 – RobG 2012-04-18 03:43:55
@RobG也許可以通過檢查'anchorOffset'和'focusOffset'完成嗎?如果選擇了一個元素的全部內容,我想將其視爲「完全選定」,否則只是「部分選定」。在大多數情況下,開始和結束元素將被部分選中。事實上,也許我總是可以把它們當作部分選擇的...... – 2012-04-18 03:54:57
@RobG:沒有必要:你可以在所有主流瀏覽器中獲得合理精確的界限。 – 2012-04-18 08:28:03