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 === -1 && endRet === 1;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 != 1) {return false;}if (parentEl.contains ) {return parentEl.contains(el);}if ( parentEl.compareDocumentPosition ) {return !!(parentEl.compareDocumentPosition(el) & 16);}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 === 9 ? a.documentElement : a,bup = b && b.parentNode;return a === bup || !!( bup && bup.nodeType === 1 && (adown.contains ?adown.contains( bup ) :a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16));} :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 === 9 ? 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) & 16); 
} ||
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

如果您觉得本文的内容有趣就扫一下吧!捐赠互勉!

分类: JavaScript
好文要顶 关注我 收藏该文
^_^肥仔John
关注 - 85
粉丝 - 707
+加关注
1
0
« 上一篇:JS魔法堂:函数重载 之 获取变量的数据类型
» 下一篇:CSS布局:水平居中
posted @ 2016-03-02 11:29 ^_^肥仔John 阅读(751) 评论(0) 编辑 收藏
刷新评论刷新页面返回顶部
【推荐】超50万VC++源码: 大型工控、组态\仿真、建模CAD源码2018!
【推荐】加入腾讯云自媒体扶持计划,免费领取域名&服务器
ketang0108
最新IT新闻:
· 苏宁宣布重磅福利:“7天无理由退货”线上线下统一标准
· 兴趣降温 安卓厂商无意模仿iPhone X推出3D识别
· 乐视云更名新乐视云联:仍未获云服务牌照
· 微软搜索引擎Bing改进航班 电影和比赛结果查询
· 彭博商业周刊:硅谷悄然掀起中国技术人员回国潮
» 更多新闻...
阿里云C2-1208
最新知识库文章:
· 步入云计算
· 以操作系统的角度述说线程与进程
· 软件测试转型之路
· 门内门外看招聘
· 大道至简,职场上做人做事做管理
» 更多知识库文章...

公告

肥仔John@github
作品:
iScheme—Scheme解释器
本文转自
^_^肥仔John博客园博客,原文链接:http://www.cnblogs.com/fsjohnhuang/p/3931818.html,如需转载请自行联系原作者

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/283043.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ChartCtrl源码剖析之——CChartAxis类

CChartAxis类用来绘制波形控件的坐标轴&#xff0c;这个源码相对较复杂&#xff0c;当初阅读的时候耗费了不少精力来理解源码中的一些实现细节。 CChartAxis类的头文件。 #if !defined(AFX_CHARTAXIS_H__063D695C_43CF_4A46_8AA0_C7E00268E0D3__INCLUDED_) #define AFX_CHARTA…

基于.net开发的自助餐饮系统

本文系 EMQ&Intel 联合举办的首届“中国物联网数据基础设施最佳案例评选大赛“个人开发者赛道一等奖作品。项目简介智能餐饮自助结算系统是一个由称重系统、显示屏、自助扫码盒和 Intel CPU 组成的智能自助结算终端&#xff0c;将装有菜品的托盘放到秤盘上结算&#xff0c;…

java打包维护_java打包详解

from yahh2008的blog: http://www.matrix.org.cn/blog/yahh2008/兄弟&#xff0c;对java着迷吗&#xff0c;或者是为了自己的生计&#xff0c;不论怎样都欢迎你进入精彩java世界&#xff0c;welcome&#xff01;可能你刚刚对每个人说&#xff1a;Hello World&#xff01;也或者…

Linux高级文本处理之sed(三)

sed高级命令sed允许将多行内容读取到模式空间&#xff0c;这样你就可以匹配跨越多行的内容。本篇笔记主要介绍这些命令&#xff0c;它们能够创建多行模式空间并且处理之。其中&#xff0c;N/D/P这三个多行命令分别对应于小写的n/d/p命令&#xff0c;后者我们在上一篇已经介绍。…

如何在 C# 程序中注入恶意 DLL ?

一&#xff1a;背景 前段时间在训练营上课的时候就有朋友提到一个问题&#xff0c;为什么 Windbg 附加到 C# 程序后&#xff0c;程序就处于中断状态了&#xff1f;它到底是如何实现的&#xff1f;其实简而言之就是线程的远程注入&#xff0c;这一篇就展开说一下。二&#xff1a…

练习题|网络编程-socket开发

原文&#xff1a;https://www.cnblogs.com/shengyang17/p/8822745.html 1、什么是C/S架构&#xff1f; C指的是client&#xff08;客户端软件&#xff09;&#xff0c;S指的是Server&#xff08;服务端软件&#xff09;&#xff0c;C/S架构的软件&#xff0c;实现服务端软件与客…

ABP vNext微服务架构详细教程(补充篇)——单层模板(上)

简介在之前的《ABP vNext微服务架构详细教程》系列中&#xff0c;我们已经构建了完整的微服务架构实例&#xff0c;但是在开发过程中&#xff0c;我们会发现每个基础服务都包含10个类库&#xff0c;这是给予DDD四层架构下ABP的实现方案&#xff0c;但是实际使用中我们会发现&am…

mybatis源码学习(三):MappedStatement的解析过程

我们之前介绍过MappedStatement表示的是XML中的一个SQL。类当中的很多字段都是SQL中对应的属性。我们先来了解一下这个类的属性&#xff1a; public final class MappedStatement {private String resource;private Configuration configuration;//sql的IDprivate String id;//…

C# 二十年语法变迁之 C# 8参考

C# 二十年语法变迁之 C# 8参考自从 C# 于 2000 年推出以来&#xff0c;该语言的规模已经大大增加&#xff0c;我不确定任何人是否有可能在任何时候都对每一种语言特性都有深入的了解。因此&#xff0c;我想写一系列快速参考文章&#xff0c;总结自 C# 2.0 以来所有主要的新语言…

windows 提权 cve-2018-8897

windows 提权 cve-2018-8897影响范围&#xff1a;基本上是全版本具体影响范围看详情&#xff1a;https://portal.msrc.microsoft.co … isory/CVE-2018-8897http://www.o2oxy.cn/wp-content/uploads/2018/06/cve-2018-8897.rar转载于:https://blog.51cto.com/9861015/2126608

java servlet练习测试

步骤&#xff1a; 0、首先创建web project&#xff0c;工程名&#xff1a;test_servlet 1、编写Servlet&#xff0c;TestServlet.java文件内容&#xff1a; package com.ouyang.servlet;import java.io.IOException; import java.sql.Connection; import java.sql.DriverManage…

《ASP.NET Core 6框架揭秘》实例演示[19]:数据加解密与哈希

数据保护&#xff08;Data Protection&#xff09;框架旨在解决数据在传输与持久化存储过程中的一致性&#xff08;Integrity&#xff09;和机密性&#xff08;confidentiality&#xff09;问题&#xff0c;前者用于检验接收到的数据是否经过篡改&#xff0c;后者通过对原始的数…

如何在ABAP Netweaver和CloudFoundry里记录并查看日志

Netweaver 要记录日志需要有一个checkpoint group&#xff0c;可以自行创建也可以使用标准的。这里我重用标准的group&#xff1a;DEMO_CHECKPOINT_GROUP。 tcode SAAB&#xff0c;点Display <->Activate进入编辑模式&#xff0c;将Logpoints设置为"Log"&#…

如何成为有效学习的高手(许岑)——思维导图

总结自许岑精品课《如何成为有效学习的高手》&#xff0c;图片看不清的可以看下面。 最后有彩蛋&#xff01;最后有彩蛋&#xff01;最后有彩蛋&#xff01; 定义 高效学习的定义&#xff1a;找到最适合自己的学习手法&#xff0c;在相对短的时间内集中注意力&#xff0c;以解决…

WPF Canvas 平滑笔迹

WPF Canvas 平滑笔迹控件名&#xff1a;CanvasHandWriting作者&#xff1a;小封&#xff08;邝攀升&#xff09;原文链接&#xff1a; https://github.com/WPFDevelopersOrg/WPFDevelopers编辑&#xff1a;驚鏵完整的思路如下收集路径点集。平均采样路径点集。将路径点集转为…

NetSpeed

NetSpeed公司提供的NOC包括三部分&#xff0c;可以通过NocStudio进行配置生成。 1)NetSpeed Orion&#xff0c;面向快速SoC design的可综合平台。 2)Linley NetSpeed NoC面向复杂的interconnect实现&#xff0c;同时优化内部physical implementation和timing closure. NoC是基于…

js ajax java传参_ajax参数传递与后台接收

ajax参数传递与后台接收Servlet中读取http参数的方法Enumeration getParameterNames() 返回一个 String 对象的枚举&#xff0c;包含在该请求中包含的参数的名称String getParameter(String name) 以字符串形式返回请求参数的值&#xff0c;或者如果参数不存在则返回 null。Str…

init 访问器只能初始化时赋值,是真的吗?

前言C# 提供的 init 关键字用于在属性中定义访问器方法&#xff0c;可以让属性仅能在对象初始化的时候被赋值&#xff0c;其他时候只能为只读属性的形式。例如下面代码可以正常执行&#xff1a;public class Demo {public string Name { get; init; } }var demo new Demo { Na…

eclipse实现代码块折叠-类似于VS中的#region……#endregion

背 景 刚才在写代码的时候&#xff0c;写了十几行可以说是重复的代码&#xff1a; 如果整个方法或类中代码多了&#xff0c;感觉它们太TM占地方了&#xff0c;给读者在阅读代码上造成很大的困难&#xff0c;于是想到能不能把他们“浓缩”成一行&#xff0c;脑子里第一个闪现出的…

java定义基础变量语句_java语言基础-变量

一丶变量的基本概念1.什么是变量(1).内存中的一个存储区域(2).该区域有自己的名称(变量名),和类型(数据类型)(3.)该区域的数据可以在同一类型范围内不断变化(定义变量的主要目的是因为数据的不确定性)2.为什么要定义变量用来不断存放同一类型的常量&#xff0c;并可以重复使用3…