天天看点

contains和compareDocumentPosition 的用法及区别

  1、DOMElement.contains(DOMNode)

    这个方法起先用在 IE ,用来确定 DOM Node 是否包含在另一个 DOM Element 中。

    注意点:如果 DOM Node 和 DOM Element 相一致,.contains() 将返回 true ,虽然,一个元素不能包含自己。

    这里有一个简单的执行包装,可以运行在:Internet Explorer, Firefox, Opera, and Safari。

  function contains(a, b) {

       return a.contains ? a != b && a.contains(b) : !!(a.compareDocumentPosition(arg) & 16);

  }

  2、NodeA.compareDocumentPosition(NodeB)

    这个方法是 DOM Level 3 specification 的一部分,允许你确定 2 个 DOM Node 之间的相互位置。这个方法比 .contains() 强大。这个方法的一个可能应用是排序 DOM Node 成一个详细精确的顺序,该方法IE6~8是不支持的

    使用这个方法你可以确定关于一个元素位置的一连串的信息。所有的这些信息将返回一个比特码(Bit,比特,亦称二进制位)。    对于那些,人们知之甚少。比特码是将多重数据存储为一个简单的数字(译者注:0 或 1)。你最终打开 / 关闭个别数目(译者注:打开/关闭对应 0 /1),将给你一个最终的结果。

    这里是从 NodeA.compareDocumentPosition(NodeB) 返回的结果,包含你可以得到的信息。

Bits          Number        Meaning 

000000         0              元素一致 

000001         1              节点在不同的文档(或者一个在文档之外) 

000010         2              节点 B 在节点 A 之前 

000100         4              节点 A 在节点 B 之前 

001000         8              节点 B 包含节点 A 

010000         16             节点 A 包含节点 B 

100000         32             浏览器的私有使用

现在,这意味着一个可能的结果类似于:

 <div id="a">

 <div id="b"></div>

</div>

<script>

 alert( document.getElementById("a").compareDocumentPosition(document.getElementById("b")) == 20);

</script>

   一旦一个节点 A 包含另一个节点 B,包含 B(+16) 且在 B 之前(+4),则最后的结果是数字 20 。如果你查看比特发生的变化,将增加你的理解。

000100 (4) + 010000 (16) = 010100 (20)

    这个,毫无疑问,有助于理解单个最混乱的 DOM API 方法。当然,他的价值当之无愧的。

    现在,DOMNode.compareDocumentPosition 在 Firefox 和 Opera 中是可用的。然而,有一些技巧,我们可以用来在 IE 中执行他。

function comparePosition(a, b){

 return a.compareDocumentPosition ?

 a.compareDocumentPosition(b) :

 a.contains ?

  ( a != b && a.contains(b) && 16 ) +

  ( a != b && b.contains(a) && 8 ) +

  ( a.sourceIndex >= 0 && b.sourceIndex >= 0 ?

   (a.sourceIndex < b.sourceIndex && 4 ) +

   (a.sourceIndex > b.sourceIndex && 2 ) :

   1 ) :

  0;

}

    IE 提供给我们一些可以使用的方法和属性。开始,使用 .contains() 方法(如我们前面所讨论的),以便给我们包含(+16)或者被包含(+8)的结果。IE 还有一个 .sourceIndex 属性在所有的 DOM Element 对应着元素在文档中的位置,例如:document.documentElement.sourceIndex == 0。因为我们有这个信息,我们可以完成两个 compareDocumentPosition 难题:在前面(+2)和在后面(+4)。另外,如果一个元素不在当前的文档,.sourceIndex 将等于 -1,这个给我们另外一个回答(+1)。最后,通过这个过程的推断,我们可以确定如果一个元素等于他本身,返回一个空的比特码(+0)。

    这个函数可以在 Internet Explorer、Firefox 和 Opera 中运行。但在 Safari 中却有残缺功能(因为他只有 contains() 方法,而没有 .sourceIndex 属性。我们只能得到 包含(+16),被包含(+8),其他的所有结果都将返回(+1)代表一个断开)。

    PPK 提供了一个关于通过创建一个 getElementsByTagNames 方法使新功能可以被使用的很棒的例子。让我们改编他到我们的新方法中:

// Original by PPK quirksmode.org

function getElementsByTagNames(list, elem) {

        elem = elem || document;         

        var tagNames = list.split(’,’), results = [];         

        for ( var i = 0; i < tagNames.length; i++ ) {

                var tags = elem.getElementsByTagName( tagNames[i] );

                for ( var j = 0; j < tags.length; j++ )

                        results.push( tags[j] );

        }         

        return results.sort(function(a, b){

                return 3 - (comparePosition(a, b) & 6);

        });

}

    我们现在可以使用他来按次序构建一个站点的目录:

getElementsByTagNames("h1, h2, h3");

    虽然 Firefox 和 Opera 都采取了一些主动落实这一方法。我依然期待看到更多的浏览器进入,以帮助向前推动

——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

__contains =  

    documentElement.compareDocumentPosition ?  

        function (a, b) {  

            return !!(a.compareDocumentPosition(b) & 16);  

        } :  

        documentElement.contains ?  

            function (a, b) {  

                if (a.nodeType == NodeType.DOCUMENT_NODE) {  

                    a = a.documentElement;  

                }  

                // !a.contains => a===document || text  

                // 注意原生 contains 判断时 a===b 也返回 true  

                b = b.parentNode;  

                if (a == b) {  

                    return true;  

                }  

                // when b is document, a.contains(b) 不支持的接口 in ie  

                if (b && b.nodeType == NodeType.ELEMENT_NODE) {  

                    return a.contains && a.contains(b);  

                } else {  

                    return false;  

                }  

            } : 0;  

这段代码的作用是判断一个节点是另一个节点的父节点。代码中主要用到了compareDocumentPosition与contains两个方法。 

下面认识一下这两个方法。 

contains是IE发明的函数,后来也有别的浏览器支持,比如Chrome,opera,有资料说firefox不支持此方法,但我用firefox15测试发现已经支持此方法。用法如下: 

el.contains(el2); 

如果 el包含el2 或 el === el2,返回true;否则返回false。 

这很简单,接着说compareDocumentPosition。 

这个函数来自DOM Level 3 规范,标准浏览器都支持,但IE6-8不支持,IE从9开始支持。这个函数的返回值有点难理解,下面细说: 

<div id="a">  

    <div id="b"></div>  

</div>  

<script type="text/javascript">  

   alert(a.compareDocumentPosition(b));  

</script>  

在Chrome试试这个例子,结果是 20。这里的 20 表示什么呢?下面先看一些常见的返回值: 

  33 => 100001 

  20 => 010100 

  16 => 010000 

  10 => 001010 

    8 => 001000 

    4 => 000100 

    2 => 000010 

    1 => 000001 

    0 => 000000 

很明显,例子的结果是第2个。要说明20的意义,需要先分析右边的二进制数。 

这个二进制数分6位,从左到右依次是(如果符合条件,该位为1,否则为0): 

  第1位:浏览器的私有使用 

  第2位:a是b的祖先节点 

  第3位:a是b的子孙节点 

  第4位:a是b前面的节点 

  第5位:a是b后面的节点 

  第6位:a和b在不同的文档(或者一个在文档之外) 

说到这里,20的意思就明显了,即 a是b的祖先节点,同时 a是b前面的节点。其实通常用到的是第2-5位,我觉得这四位已经足够表示任何位置关系了,也许你会问,a等于b 的情况怎么表示呢? 

逆向思维一下,如果这四位都是0,也就是说,既不是父节点,又不是子节点,既不在前面,又不在后面,那唯一的解释就是a 等于 b,答案就是 000000。 

知道了这些再来看代码 return !!(a.compareDocumentPosition(b) & 16); 

为什么要&16? 我们知道16的2进制为10000 ,我们发现之后与左数第五位为1的数做&运算才会得到非零数,对非零做两次!操作就获得了一个布尔值。那么谜底解开了,这句的作用就是判断一个节点是否是另一个节点的祖先节点。 

继续分析代码: 

Javascript代码  收藏代码

if (a.nodeType == NodeType.DOCUMENT_NODE) {  

    a = a.documentElement;  

}  

// !a.contains => a===document || text  

这段代码考虑到了一种情况,就是当第一个参数是document的情况,document本身不是一个节点,所以contains方法无法对其进行比较,但是在逻辑上我们可以把document当成是最外层节点来处理,似乎更加合理。 

Javascript代码  收藏代码

// 注意原生 contains 判断时 a===b 也返回 true  

 b = b.parentNode;  

 if (a == b) {  

     return true;  

 }  

contrains方法判断a与b对象的关系式还会将a 等于 b这种情况也返回true。上面这段代码将这种情况做了特殊处理。 

最后一段代码就是使用contains方法来判断a与b的关系。 

*注:在比较节点时有一种特殊情况,就是两个节点其中一个或两个还没有被放入DOM树中,这种情况下的比较结果结果在个浏览器中表现不一致,请使用者注意。 

继续阅读