在文本选择中获取元素 [英] Get elements in text selection
问题描述
我想获取用户选择中包含的所有元素(如在DOM 2范围/ MS TextRanges中)
/ ** @return {Array。< Element>} * /
function getSelectedElements(){
var elements = [];
//在用户选择中获取元素
返回元素;
}
我尝试这样做, 解决类似问题,以及一些Moz和MS文档以及一些PPK内容。
方法基本上是:
-
定义SelectionLikeObject作为DOM选择或IE选择。
-
将RangeLikeObject定义为DOM范围或IE TextRange。
-
让
containerNode
成为一个节点。 -
让
containerElement
成为一个元素。 -
让
containsElements
成为一个NodeList。 / p> -
让
elementRange
成为RangeLikeObject。 -
让
selectedRange
成为RangeLikeObject。 -
让
selectedElements
是一个元素数组。 -
让
元素
成为一个元素。 / p> -
让
选择
成为SelectionLikeObject。 -
设置
选择
-
将
selectedElements
设置为新的数组。 -
对于
选择
中的每个selectedRange
:
-
将
containerNode
设置为selectedRange的共同祖先容器
-
将
containerElement
设置到最接近的元素祖先containerNode
。
-
将
containedElements
设置为containerElement
。 -
对于
元素
> containsElements :
-
设置
elementRange
从元素
。 -
如果
elementRange
落在selectedRange
的
边界内:
- 推
元素
$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;
//没有getRangeAt的浏览器的黑客
//参见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);
返回范围;
};
selection.rangeCount = 1;
}
elementRange = doc.createRange(); (i = 0; i< rangeCount; ++ i)
{
selectedRange = selection.getRangeAt(i);
containerNode = selectedRange.commonAncestorContainer;
while(containerNode&& containerNode.nodeType!= 1){
containerNode = containerNode.parentNode;
}
if(!containerNode){
return selectedElements; //发生错误...
}
containerElement = / ** @type {Element} * / containerNode;
containedElements = containerElement.getElementsByTagName('*');
elementCount = containedElements.length; (var i = 0; i< elementCount; ++ i)
{
element = containedElements [i];
elementRange.selectNodeContents(element);
if(elementRange.compareBoundaryPoints(selectedRange.END_TO_START,selectedRange)< 1&&b $ b 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){
//选择 - 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 = vs85).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:这是一个ControlRange,放弃。
}
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;
};
现在,我想解决的问题是这样的:如果一个元素中只有部分文本是选择它,它出现在返回的数组中,即使它只是部分选择。
我想添加一个将行为更改为仅包括完全选择的参数元素。我有一种感觉,compareBoundaryPoints的答案就是这样,我只是不明白这一点,可以弄清楚。
此外,IE代码迄今未经测试,但是如果有什么问题(或DOM分支),请让我知道。
睡觉后阅读 compareBoundaryPoints ,我想我有答案。
if(elementRange.compareBoundaryPoints(Range.START_TO_START,selectedRange)> -1&&
elementRange.compareBoundaryPoints(Range.END_TO_END,selectedRange)< 1){
对于完全落在用户选择范围内的元素,这似乎只能评估 true
p>
I want to get all of the elements contained in a user selection (as in DOM 2 ranges / MS TextRanges).
/** @return {Array.<Element>} */
function getSelectedElements() {
var elements = [];
// get elements in the user selection somehow
return elements;
}
I've tried to do this by following Tim Down's excellent solution to a similar question, and some Moz and MS docs, and some PPK stuff.
The approach is basically:
Define SelectionLikeObject as a DOM Selection or an IE Selection.
Define RangeLikeObject as a DOM Range or an IE TextRange.
Let
containerNode
be a Node.Let
containerElement
be an Element.Let
containedElements
be a NodeList.Let
elementRange
be a RangeLikeObject.Let
selectedRange
be a RangeLikeObject.Let
selectedElements
be an Array of Elements.Let
element
be an Element.Let
selection
be a SelectionLikeObject.Set
selection
from the user's selection.Set
selectedElements
to a new Array.For each
selectedRange
inselection
:Set
containerNode
to the common ancestor container ofselectedRange
.Set
containerElement
to the closest Element ancestor tocontainerNode
.Set
containedElements
to a list of descendants ofcontainerElement
.For each
element
incontainedElements
:Set
elementRange
fromelement
.If the boundaries of
elementRange
fall within the boundaries ofselectedRange
:- Push
element
ontoselectedElements
.
- Push
The DOM branch looks like this:
/**
@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;
};
The IE branch looks like this:
/**
@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;
};
Now, the issue I want to solve is this: if only part of the text in an element is selected, it appears in the returned array, even though it is only partly selected.
I'd like to add a parameter that changes the behavior to only include fully-selected elements. I have a feeling the answer lies with compareBoundaryPoints, I just don't understand it well enough to figure it out yet.
Also, the IE code is untested so far, but please let me know if anything looks wrong with it (or the DOM branch).
After getting some sleep and reading up on compareBoundaryPoints again, I think I have the answer.
if (elementRange.compareBoundaryPoints(Range.START_TO_START, selectedRange) > -1 &&
elementRange.compareBoundaryPoints(Range.END_TO_END, selectedRange) < 1) {
This seems to only evaluate to true
for elements that fall completely within the user selection.
这篇关于在文本选择中获取元素的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!