我希望获得用户选择中包含的所有元素(如DOM 2范围/ MS TextRanges中所示)。
/** @return {Array.<Element>} */
function getSelectedElements() {
var elements = [];
// get elements in the user selection somehow
return elements;
}
我试过这样做是通过跟随Tim Down的优秀solution to a similar question,以及一些Moz和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
:
从elementRange
设置element
。
如果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分支),请告诉我。
答案 0 :(得分:1)
在睡了一觉并再次阅读compareBoundaryPoints后,我想我有答案。
if (elementRange.compareBoundaryPoints(Range.START_TO_START, selectedRange) > -1 &&
elementRange.compareBoundaryPoints(Range.END_TO_END, selectedRange) < 1) {
对于完全在用户选择范围内的元素,这似乎只评估true
。