Safari中Range setEndAfter函数的有效节点类型是什么?

时间:2012-09-06 08:37:56

标签: javascript dom

我正在编写一个模块,允许用户选择HTML文档的一部分。要使内部工作正常,我将所选内容的Range扩展为有效的HTML代码段。

对于B是A的后代的情况,我找到B的祖先,它是A的子节点,并且想要使用setEndAfter将范围设置为在该节点之后结束。这就是我现在所拥有的:

var closestChild = function (node, descendant) {
    var parent;
    if (descendant.parentElement) {
        parent = descendant.parentElement;
        if ( node === parent ) {
            return descendant;
        }
        return closestChild(node, parent);
    }
    return false;
}

var legalRange = function (range) {
        var newRange = range.cloneRange(),
            child;
        if (range.startContainer === range.endContainer) {
            return newRange;
        }
        child = closestChild(range.startContainer.parentElement, range.endContainer.parentElement);
        if (child) {
            newRange.setEndAfter(child);
            return newRange;
        } 
        return null;
};

但是当我尝试设置终点时,这会抛出INVALID_NODE_TYPE_ERR: DOM Range Exception 2。我也尝试使用parentNode而不是parentElement,抛出相同的异常。如果我使用setEnd(),这不是问题。我应该通过哪些类型的节点来实现这一目标。

PS:事实证明代码在FireFox中有效,所以现在我的问题出在Safari和Chrome上。

2 个答案:

答案 0 :(得分:1)

我找到了解决方案。 当我设置我的测试用例时,我没有将元素添加到文档中。如果节点不是文档的一部分,那么当使用setEndAfter时,Chrome和Safari似乎将节点视为无效。

答案 1 :(得分:0)

正如Eivind所回答的,问题是用于设置范围位置的节点未附加到文档。因此,一种解决方案是在设置范围位置之前附加它。

另一种解决方案是,如果由于某种原因无法将节点附加到DOM,则可以使用setStart()setEnd()

// Instead of `range.setStartBefore(node)`
var parent = node.parent;
range.setStart(parent, Array.from(parent.childNodes).indexOf(node))

// Instead of `range.setStartAfter(node)`
var parent = node.parent;
range.setStart(parent, Array.from(parent.childNodes).indexOf(node) + 1)

// Instead of `range.setEndBefore(node)`
var parent = node.parent;
range.setEnd(parent, Array.from(parent.childNodes).indexOf(node))

// Instead of `range.setEndAfter(node)`
var parent = node.parent;
range.setEnd(parent, Array.from(parent.childNodes).indexOf(node) + 1)

注意:Internet Explorer中不支持Array.from(arrayLike)< = 11.使用Array.prototype.slice.call(arrayLike)代替您需要IE支持。