JS魔法堂:判断节点位置关系
一、前言
在polyfill querySelectorAll 和写弹出窗时都需要判断两个节点间的位置关系,通过jQuery我们可以轻松搞定,但原生JS呢?下面我将整理各种判断方法,以供日后查阅。
二、祖孙关系
html
<div id="ancestor">
<div id="parent">
<div id="son">son</div>
</div>
</div>
<div id="other">other</div>
common.js
var ancestor = document.getElementById('ancestor');
var parent = document.getElementById('parent');
var son = document.getElementById('son');
var other = document.getElementById('other');
方法一:通过Selection对象
/** 定义判断祖孙关系函数
* @param {HTMLElement} parentNode
* @param {HTMLElement} sonNode
*/
var has = function(parentNode, sonNode){
if (parentNode === sonNode) return true;
var selection = window.getSelection();
selection.selectAllChildren(parentNode);
var ret = selection.containsNode(sonNode, false); return ret;
}; // 调用
console.log(has(ancestor, son)); // 显示true
console.log(has(ancestor, other)); // 显示false
缺点:仅仅FF支持,其他浏览器一律无效
1. 执行 selection.selectAllChildren(parentNode) 时,parentNode的内容会被高亮,并且原来高亮的部分将被取消;
2. chrome下, selection.containsNode()恒返回false ;
3. IE9~11下的Selection类型对象没有containsNode方法;
4. IE5.5~8下没有Selection类型;
关于IE下的[object Selection]和[object MSSelection]类型(详细可浏览《JS魔法堂:细说Selection和MSSelection类型》)
1. IE11仅有[object Selection]类型
获取方式: document.getSelection() 或 window.getSelection()
2. IE9~10有[object MSSelection]和[object Selection]两种类型
获取[object MSSelection]: document.selection
获取[object Selection]: document.getSelection() 和 window.getSelection()
3. IE5.5~IE8仅有[object MSSelection]类型
获取方式: document.selection
注意:document.selection是IE的特有属性。
方法二:通过Range对象
var has = function(parentNode, sonNode){
if (parentNode === sonNode) return true;
var r1 = document.createRange(), r2 = document.createRange();
r1.selectNode(parentNode);
r2.selectNode(sonNode);
var startRet = r1.compareBoundaryPoints(Range.START_TO_START, r2);
var endRet = r1.compareBOundaryPoints(Range.END_TO_END, r2);
var ret = startRet === - && endRet === ; return ret;
};
缺点:不兼容IE5.5~8(IE9+、FF和Chrome均支持)
1. IE5.5~8没有 document.createRange() 方法
关于[object Range]、[object TextRange]和[object ControlRange]类型
首先明确的是[object Range]是符合W3C标准的,而[object TextRange]和[object ControlRange]是IE独有的。
(详细可浏览《JS魔法堂:细说Range、TextRange和ControlRange类型》)
1. 通过document.createRange()创建[object Range]对象
2. 通过window.getSelection().getRangeAt({unsigned int32} index)获取[object Range]对象
3. 通过document.selection.createRange()或document.selection.createRangeCollection()方法获取[object TextRange]对象,并且无法像Range对象内容通过selectNode方法直接绑定到DOM片段中。
方法三:通过contains方法
var has = function(parentNode, sonNode){
return parentNode.contains(sonNode);
}; console.log(has(ancestor, ancestor));// 返回true
console.log(has(ancestor, son));// 返回true
console.log(has(ancestor, other));// 返回false
优点:简单直接
缺点:兼容性问题
支持——chrome、 firefox9+、 ie5+、 opera9.64+(估计从9.0+)、safari5.1.7+
不支持——FF
方法四:通过compareDocumentPosition方法
var has = function(parentNode, sonNode){
if (parentNode === sonNode) return true;
var rawRet = parentNode.compareDocumentPosition(sonNode);
var ret = !!(rawRet & 16); return ret;
};
compareDocumentPosition可以算是W3C标准中比较两节点位置关系的一大利器,不仅可以判断祖孙关系,还可以判断其他关系哦
var ret = A.compareDocumentPosition(B);
返回值ret的意思如下:
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 浏览器的私有使用
方法五:递归遍历
var has = function(parentNode, sonNode){
if (parentNode === sonNode) return true;
var p = sonNode.parentNode;
if (!p.ownerDocument){
return false;
}
else if (p !== parentNode){
return has(parentNode, p);
}
else{
return true;
}
}
优点:所有浏览器均通用
缺点:当节点层级深时,效率较低。
综合方案一,来自司徒正美(http://m.cnblogs.com/57731/1583523.html?full=1):
//2013.1.24 by 司徒正美
function contains(parentEl, el, container) {
// 第一个节点是否包含第二个节点
//contains 方法支持情况:chrome+ firefox9+ ie5+, opera9.64+(估计从9.0+),safari5.1.7+
if (parentEl == el) {
return true;
}
if (!el || !el.nodeType || el.nodeType != ) {
return false;
}
if (parentEl.contains ) {
return parentEl.contains(el);
}
if ( parentEl.compareDocumentPosition ) {
return !!(parentEl.compareDocumentPosition(el) & );
}
var prEl = el.parentNode;
while(prEl && prEl != container) {
if (prEl == parentEl)
return true;
prEl = prEl.parentNode;
}
return false;
}
综合方案二,来自Sizzle(https://github.com/jquery/sizzle/blob/master/src/sizzle.js#L688)
注意:Sizzle的contains版本是contains(ancestor,ancestor)返回false的。
// Element contains another
// Purposefully does not implement inclusive descendent
// As in, an element does not contain itself
contains = hasCompare || rnative.test( docElem.contains ) ?
function( a, b ) {
var adown = a.nodeType === ? a.documentElement : a,
bup = b && b.parentNode;
return a === bup || !!( bup && bup.nodeType === && (
adown.contains ?
adown.contains( bup ) :
a.compareDocumentPosition && a.compareDocumentPosition( bup ) &
));
} :
function( a, b ) {
if ( b ) {
while ( (b = b.parentNode) ) {
if ( b === a ) {
return true;
}
}
}
return false;
};
综合方案三,我那又长又臭的版本^_^
var rNative = /[^{]+\{\s*\[native code\]\s*\}/;
var docEl = document.documentElement;
var contains = rNative.test(docEl.contains) && function(ancestor, descendant){
if (ancestor === descendant) return true; ancestor = ancestor.nodeType === ? ancestor.documentElement : ancestor;
return ancestor.contains(descendant);
} ||
rNative.test(docEl.compareDocumentPosition) &&
function(ancestor, descendant){
if (ancestor === descendant) return true; ancestor = ancestor.documentElement || ancestor;
return !!(ancestor.compareDocumentPosition(descendant) & );
} ||
rNative.test(document.createRange) &&
function(ancestor, descendant){
if (ancestor === descendant) return true; var r1 = document.createRange(), r2 = document.createRange();
r1.selectNode(ancestor.documentElement || ancestor);
r2.selectNode(descendant.documentElement || descendant);
var startRet = r1.compareBoundaryPoints(Range.START_TO_START, r2);
var endRet = r1.compareBOundaryPoints(Range.END_TO_END, r2);
var ret = startRet === -1 && endRet === 1;
try{
r1.detach();
r2.detach();
}catch(e){} return ret;
} ||
function(ancestor, descendant){
if (ancestor === descendant) return true; var a = ancestor.documentElement || ancestor;
var b = (descendant.documentElement || descendant)['parentNode'];
while(!!b){
if (a === b) return true;
b = b.parentNode;
}
return false;
};
三、总结
尊重原创,转载请注明来自:http://www.cnblogs.com/fsjohnhuang/p/3931818.html^_^肥子John
JS魔法堂:判断节点位置关系的更多相关文章
- JS魔法堂:精确判断IE的文档模式by特征嗅探
一.前言 苦逼的前端攻城狮都深受浏览器兼容之苦,再完成每一项功能前都要左顾右盼,生怕浏览器不支持某个API,生怕原生API内含臭虫因此判断浏览器类型和版本号成了不可绕过的一道关卡,而特征嗅探是继浏览器 ...
- JS魔法堂:浏览器模式和文档模式怎么玩?
一.前言 从IE8开始引入了文档兼容模式的概念,作为开发人员的我们可以在开发人员工具中通过“浏览器模式”和“文档模式”(IE11开始改为“浏览器模式”改成更贴切的“用户代理字符串”)品味一番,它的出现 ...
- JS魔法堂:属性、特性,傻傻分不清楚
一.前言 或许你和我一样都曾经被下面的代码所困扰 var el = document.getElementById('dummy'); el.hello = "test"; con ...
- JS魔法堂:追忆那些原始的选择器
一.前言 ...
- JS魔法堂:LINK元素深入详解
一.前言 我们一般使用方式为 <link type="text/css" rel="stylesheet" href="text.css&quo ...
- JS魔法堂:那些困扰你的DOM集合类型
一.前言 大家先看看下面的js,猜猜结果会怎样吧! 可选答案: ①. 获取id属性值为id的节点元素 ②. 抛namedItem is undefined的异常 var nodes = documen ...
- JS魔法堂:不完全国际化&本地化手册 之 实战篇
前言 最近加入到新项目组负责前端技术预研和选型,其中涉及到一个熟悉又陌生的需求--国际化&本地化.熟悉的是之前的项目也玩过,陌生的是之前的实现仅仅停留在"有"的阶段而已. ...
- JS魔法堂:IMG元素加载行为详解
一.前言 在<JS魔法堂:jsDeferred源码剖析>中我们了解到img元素加载失败可以作为函数异步执行的优化方案,本文打算对img元素的加载行为进行更深入的探讨. 二.资源加载的相关属 ...
- JS魔法堂:jsDeferred源码剖析
一.前言 最近在研究Promises/A+规范及实现,而Promise/A+规范的制定则很大程度地参考了由日本geek cho45发起的jsDeferred项目(<JavaScript框架设计& ...
随机推荐
- cefsharp开发实例1
做了几年.NET开发,基本都是搞WEB居多,以前也搞过一个winform项目,虽然很把界面拼接出来了,但是感觉有点痛苦,改动的时候又要改动一大堆代码.最近又要搞个桌面软件,试着搜索了下html做界面方 ...
- UWP中的Direct2D
介绍 DirectX一直是Windows平台中高性能图形的代名词,自Win7开始,微软又推出了Direct2D技术,包装于Direct3D,但专注于2D图形,并且准备取代GDI这样的传统2D图形技术. ...
- 作业2源程序版本管理软件优缺点,及Github注册
目前流行的源程序管理软件和项目管理软件主要有以下一些: 1.Visual Source Safe 优点:如果开发工具是VS.NET,用VSS较合适,方便,安装配置和使用都简单,版本控制简单,打labe ...
- 【OpenGL(SharpGL)】支持任意相机可平移缩放的轨迹球实现
[OpenGL(SharpGL)]支持任意相机可平移缩放的轨迹球 (本文PDF版在这里.) 在3D程序中,轨迹球(ArcBall)可以让你只用鼠标来控制模型(旋转),便于观察.在这里(http://w ...
- 细说.NET中的多线程 (三 使用Task)
上一节我们介绍了线程池相关的概念以及用法.我们可以发现ThreadPool. QueueUserWorkItem是一种起了线程之后就不管了的做法.但是实际应用过程,我们往往会有更多的需求,比如如果更简 ...
- C#学习笔记 -- Attribute
学习参考: http://www.cnblogs.com/dudu/articles/4449.html http://www.cnblogs.com/anytao/archive/2007/04/1 ...
- Git学习笔记(1)——安装,配置,创建库,文件添加到库
初次接触git,为了记忆深刻,把学习的简单流程记录下来. 本文记录了Git在Ubuntu上的安装,配置,以及创建版本库和往库中添加文件的过程. 1.Git的安装:(Ubuntu-Linux非常友好的安 ...
- try catch 与 throw拾遗
今天在微软虚拟学院看到的代码: var b = 0; try{ if (b == 0) { throw('NO NO!!'); } else { alert('OK OK'); }} catch(e) ...
- 移动端基于HTML模板和JSON数据的JavaScript交互
写本文之前,我正在做一个基于Tab页的订单中心: 每点击一个TAB标签,会请求对应状态的订单列表.之前的项目,我会在js里使用 + 连接符连接多个html内容: var html = ''; htm ...
- Java程序员的日常—— POI与JDBC、Mockmvc与单元测试
周日没怎么休息好,周一一天都迷迷糊糊的,不过还算是干了不少的活. 总结一下,大致有以下几点内容: 1 使用poi以及mysql jdbc实现了一个复杂excel的导入 2 基于工程原有的代码,书写sp ...