JS开发人员经常都需要确定某个节点是否包含另一个节点,由此IE率先引入了contains()函数,随后,Safari3及其更高版本,Opera 8及其更高版本,Chrome都支持了这个方法(Safari 2.x中虽然也支持这个方法,但无法正常使用)。FireFox不支持这个方法,但在DOM3中提供了一个替代方法compareDocuemntPosition()。由于此种原因,要想跨平台确定某个节点是否包含另一个节点,就必须提供一个通用的contains()函数来解决这种差异,通常的解法方法如下:
var Contains = function(a, b){ return a.contains ? a != b && a.contains(b) : !!(a.compareDocumentPosition(b) & 16); }
以上代码中,通过三目运算符确定浏览器是否支持contains()方法,如果支持该方法,则对a != b && a.contains(b)进行判断,并返回一个布林值,否则对
!!(a.compareDocumentPosition(b) & 16)进行判断,我的疑惑正在于此,我们知道,逻辑非操作符由一个叹号(!)表示,可以用于ECMAscript中的任何值,无论这个值是什么数据类型,这个操作符都会返回一个布尔值。同时使用两个逻辑非(!)操作符,就会模拟Boolean()转型函数的行为,于是就得到了这个值真正对应的布尔值。也就是说实际判断的是a.compareDocumentPosition(b) & 16。在使用compareDocumentPosiotn()方法时会返回一个表示该关系的位掩码,下表列出了这个位掩码的可能值:
掩码 | 节点关系 |
1 | 无关(给定的节点不在当前文档中) |
2 | 居前(给点的节点在DOM树种位于给点的节点之前) |
4 | 居后(给点的节点在DOM树种位于给点的节点之后) |
8 | 包含(给定的节点是参考节点的祖先) |
16 | 被包含(给定的节点是参考节点的后代) |
为模仿contanins(),应该关注的是16。如果a.compareDocumentPosition(b)得出的值是1,2,4,8,16,那么!!(1&16),!!(2&16),!!(4&16)... ...怎么确认呢?讲到这里我们必须注意”&“操作符,它叫作”按位与“,从本质上讲,按位与就是将两个数值的每一位对齐,然后根据下表中的规则,对相同位置上的两个数进行操作:
第一个数值的位 | 第二个数值的位 | 结果 |
1 | 1 | 1 |
1 | 0 | 0 |
0 | 1 | 0 |
0 | 0 | 0 |
以16&16为例,先将16转换为二进制数,16=0000 0000 0000 0000 0000 0000 0001 0000,那么16&16=0000 0000 0000 0000 0000 0000 0001 0000(具体操作过程可参考位操作符的原则),那么!!(16&16)=true(对于其它几个掩码,大家不妨也试一下,其与16进行按位与操作的结果都是0)。因此通过以上方法,便能通过!!(a.compareDocumentPosition(b) & 16)模拟出a.contanins(b)。