`
jljlpch
  • 浏览: 319787 次
  • 性别: Icon_minigender_1
  • 来自: 南昌
社区版块
存档分类
最新评论

如何找到Dom元素

阅读更多
3、如何找到Dom元素
                                                    prk/彭仁夔       08-08-19
在jQuery.fn.init函数中,最终的结果是把Dom元素放到jQuery对象的集合,我们可以传入单个Dom元素或Dom元素集合直接把其存到jQuery对象的集合。但是如果第一个参数是string类型的话,如#id就要把Dom文档树去查找。对于html的片断就得生成Dom元素。我们再进一步,传入的单个Dom元素或Dom元素集合参数又是从那里来的?我们可以通过Dom元素的直接或间接的查找元素的方式。
这一部分首先分析如何从html的片断就得生成Dom元素,然后分析jQuery是如何通过直接或间接的方式在在Dom树中找到dom元素,第三就是分析基于CSS1~CSS3的CSS selector。

3.1生成Dom元素
Init方法中通过jQuery.clean([match[1]], context);来实现把html片断转换成Dom元素,这是一个静态方法:
// 把html转换成Dom元素,elems多个html string 的数组
clean : function(elems, context) {
	  var ret = [];
	  context = context || document;//默认的上下文是document
	  //在IE中!context.createElement行不通,因为它返回对象类型
	  if (typeof context.createElement == 'undefined')
			//这里支持context为jQuery对象,取第一个元素。
		context = context.ownerDocument || context[0]
						&& context[0].ownerDocument || document;

	jQuery.each(elems, function(i, elem) {
// 把int 转换成string的最高效的方法
		if (typeof elem == 'number')elem += '';
		if (!elem)	return;// 为'',undefined,false等时返回
		if (typeof elem == "string") {// 转换html为Dom元素
		// 修正 "XHTML"-style 标签,对于如<div/>的形式修改为<div></div>
		    //但是对于(abbr|br|col|img|input|link|meta|param|hr|area|embed)
//不修改	 。front=(<(\w+)[^>]*?)
elem = elem.replace(/(<(\w+)[^>]*?)\/>/g,				     function(all, front, tag) {	return            tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)? all: front + "></" + tag+ ">";}	);
		// 去空格,否则indexof可能会出不能正常工作
		var tags = jQuery.trim(elem).toLowerCase(), 
		div = context.createElement("div");//在上下文中创建了一个元素<div>
		// 有些标签必须是有一些约束的,比如<option>必须在<select></select>中间
		// 下面的代码在大部分是对<table>中子元素进行修正。数组中第一个元素为深度			var wrap = 
//<opt在开始的位置上(index=0)就返回&&后面的数组,这是对<option>的约束
!tags.indexOf("<opt")&& [1, "<select 
multiple='multiple'>","</select>"]
	//<leg 必须在<fieldset>内部
	|| !tags.indexOf("<leg")&& [1, "<fieldset>", "</fieldset>"]
	//thead|tbody|tfoot|colg|cap必须在<table>内部
	||  tags.match(/^<(thead|tbody|tfoot|colg|cap)/)
&& [1, "<table>", "</table>"]
     //<tr在<tbody>中间
	|| !tags.indexOf("<tr")&& [2, "<table><tbody>", "</tbody></table>"]
	 //td在tr中间
	 (!tags.indexOf("<td") || !tags.indexOf("<th"))&& [3, 
"<table><tbody><tr>","</tr></tbody></table>"]
	 //col在<colgroup>中间
	|| !tags.indexOf("<col")&& [2, 
"<table><tbody></tbody><colgroup>","</colgroup></table>"]
	 //IE中 link script不能串行化 ?
	|| jQuery.browser.msie&& [1, "div<div>", "</div>"]
	 //默认不修正
	|| [0, "", ""];

	// 包裹html之后,采用innerHTML转换成Dom
	div.innerHTML = wrap[1] + elem + wrap[2];

	while (wrap[0]--)
	// 转到正确的深度,对于[1, "<table>","</table>"],div=<table>
		div = div.lastChild;

	// fragments去掉IE对<table>自动插入的<tbody>
	if (jQuery.browser.msie) {								
	// 第一种情况:tags以<table>开头但没有<tbody>。在IE中生成的元素中可能会自动
// 加的<tbody> 第二种情况:thead|tbody|tfoot|colg|cap为tags,
// 那wrap[1] == "<table>" .tbody不一定是tbody,也有可能是thead等等
	var tbody = !tags.indexOf("<table")&& tags.indexOf("<tbody") < 0
				 ? div.firstChild&& div.firstChild.childNodes 
				 : wrap[1] == "<table>"&& tags.indexOf("<tbody") < 0
					   ? div.childNodes: [];
	// 除去<tbody>
	 for (var j = tbody.length - 1;j >= 0; --j)
		     if (jQuery.nodeName(tbody[j],
 "tbody")&&!tbody[j].childNodes.length)						    tbody[j].parentNode.removeChild(tbody[j]);

	//使用innerHTML,IE会去开头的空格节点的,加上去掉的空格节点
	if (/^\s/.test(elem))									 div.insertBefore(context.createTextNode
(elem.match(/^\s*/)[0]),div.firstChild);
	}
                     
	elem = jQuery.makeArray(div.childNodes);//elem从字符转换成了数组
}
      //采用===0,因为form,select都有length属性。这里主要是为了form,select进
//行下面的if else 处理。对于其它的length === 0的,也根本就不要加入到ret中。
if (elem.length === 0&& (!jQuery.nodeName(elem, "form")
				    && !jQuery.nodeName(elem, "select")))
						return;
   //不是(类)数组的形式的元素,或是form元素或是select元素(这两个可以看作类数组)
if (elem[0] == undefined|| jQuery.nodeName(elem, "form")|| elem.options)
		ret.push(elem);
else// 对于elems是array-like的集合
	ret = jQuery.merge(ret, elem);
});
   //上面的each中把有效的元素都加入到ret,现在只要返回就得到转换的Dom元素数组
return ret;
},
在上面的代码中,我们可以看出对于elems, context的参数的支持是多种形式的,elems可以为(类)数组的形式,还可以采用对象的形式。数组中的元素或对象的属性可以是混合形的,如string,ojbect,甚至(类)数组的形式。对于数字类型,会转换在string形,除string形之外的都放入返回的数组中,当然对于集合的形式,那就会取集合中每个元素。
对于string的形式就转换成Dom元素的形式,之后存到返回的数组中。这是这个函数的主要任务。对于把html转换成Dom元素,这里采用innerHTML把html挂到Dom文档树中。这样就转换成了Dom元素。
有些html标签片断是有约束的,比如<td>xx</td>,它必须存在table的tr中,也就是说在要进行html的标签片断的修正。这也是上面的代码处理的重点。
3.2间接引用Dom节点
 在init的参数中,我们可以直接输入Dom元素(集)的参数来构建jQuery对象,那么这些Dom元素(集)从那里来的呢?我们可以通过直接引用节点的方式如:document.getElementById,或getElementsByTag来引用Dom元素节点。我们还可以通过dom元素的我们可以通过元素的childNodes firstChild、lastChild、nextSibling、parentNode、previousSibling等间接来取得,等类似的属性来引用元素。因为这个不是直接在Dom中查找的,称为间接引用。
stChild、parentNode这些都是Dom元素的属性,jquery对象是Dom元素的集合。它也应该把这些间接引用的属性整合到jquery对象中来,这样就可以通过jquery对象来获得其集合中所有元素的各自的间接引用节点。把这些间接引用 的节点组合起来构成新的集合。集合中元素是jquery对象所有的元素的子节点或父节点之类的间接引用节点。
有了这些节点集合,可以作为jQuery.fn.init的参数传入。有人会问连这jQuery.fn.init都没有完成,怎么可能通过jquery对象而得到间接引用节点(集)呢? 
第一、因为jQuery.fn.init的参数可以有四个类型,我们完全可以通过String类型来先构建Jquery对象(html生成Dom对象,#id的getElementById直接引用,CSS Selector)。然后就可以把这个对象的间接引用到的元素节点作为参数传给下一个要构建的jquery对象。很多调用pushStack函数的用法就是这样的。
第二、如果硬是要不脱离jQuery来进行间接引用的话,jQuery提供几个静态方法:jQuery.dir、jQuery.nth、jQuery.sibling来完成间接引用。既然是间接引用,当然得先有一个dom元素的主体。直接引用也有主体,但是我们可以采用document来做为默认的。因为直接引用是查找的方式。
jQuery.each( {// 一组对元素的相关节点的操作,如父,子,兄节点等
	   parent : function(elem) {// 父亲节点
			return elem.parentNode;
		},
		parents : function(elem) {// elem的所有parentNode
			return jQuery.dir(elem, "parentNode");
		},
		next : function(elem) {// 元素的下一个兄弟
			return jQuery.nth(elem, 2, "nextSibling");
		},
		prev : function(elem) {// 前一个兄弟
			return jQuery.nth(elem, 2, "previousSibling");
		},
		nextAll : function(elem) {// 所有后继兄弟
			return jQuery.dir(elem, "nextSibling");
		},
		prevAll : function(elem) {// 所有前继兄弟
			return jQuery.dir(elem, "previousSibling");
		},
		siblings : function(elem) {// 所有兄弟
			return jQuery.sibling(elem.parentNode.firstChild, elem);
		},
		children : function(elem) {// 所有孩子
			return jQuery.sibling(elem.firstChild);
		},.. .. ..	},
 function(name, fn) {// 注册到jQuery对象中去,可以调用同名方法
			jQuery.fn[name] = function(selector) {
				var ret = jQuery.map(this, fn);// 每个元素都执行同名方法
				if (selector && typeof selector == "string")
					ret = jQuery.multiFilter(selector, ret);// 过滤元素集
			return this.pushStack(jQuery.unique(ret));// 构建jQuery对象
			};
		});
这段代码就是jQuery对象的间接引用节点的方法。它提供了父节点、子节点,兄弟节点三个方法的操作。对于父节点,可以可得到其当前的父亲节点,还可以得到所有父亲节点(包括祖先节点)。对于子节点,就是所有的直接的子节点(不包括其它的后代节点)。对于兄弟节点,有大(前面)兄弟节点(们)和小(后面)兄弟节点(们)之分。
接下就是把这八个节点注册到jQuery.fn(即jQuery对象)中的同名方法中。因为jQuery对象的Dom元素是集合,那么就得对集合中每个Dom元素都要进行单个元素相同的操作(上面8种的间接引用节点的Fn)。在组合中,有可能会有重复的dom元素,也还有可能用户会对这些间接引用节点进行其它的自定义的过滤。这里支持的过滤方式下一小节要分析的selector的过滤。最后构建一个新的jQuery对象。
其调用的方式如:$(‘p’). parents()。这个例子是所有p标签的所有父辈(包含祖先)节点的所组合而成的集合。这个集合有重复的元素可能性是很大。所以要jQuery.unique(ret)来进行过滤。
再举一个例子:$(‘p’). parents(“div”)。这个就是取所有所有父辈(包含祖先)节点中的Tagname为div的元素集合。这个就采用了CSS的过滤。最后还是要进行unique处理。
在上面的代码,我们也看到了几个静态间接引用节点的函数调用:
//从一个元素出发,取得一个方向上的所有元素,如parentNode、nextSibling、//previousSibling、lastChild,firstChild这样的元素的属性作为方向(dir).
dir : function(elem, dir) {
	var matched = [], cur = elem[dir];
	while (cur && cur != document) {
		if (cur.nodeType == 1)
			matched.push(cur);
		cur = cur[dir];
	}
	return matched;
},
取名dir,我们会想到Dos的Dir操作。其实我们解释为direction(方向)更好理解。它一直会朝着方向走到尽头。如parentNode的方向。它会把取的父节点作为当前节点再取其父节点。就是所有的父辈节点(们)。对于nextSibling、previousSibling、lastChild,firstChild都一样。只要取到的元素有dir这个方向(属性)的话,就一直取下去。每一步都会把取到的元素保存起来。这个函数是对于要取‘一条线’形式的Dom文档树的所有元素是有很有用的。从一个元素出发,其parentNode可以画一条线,lastChild也可以画一条线。当然nextSibling不也类外。
Dir是取某一个方向上所有的元素,有的时候,我们要取的是某一个方向上的第几个元素,如我们找到其爷爷节点。那就是某一个方向上的第二个元素。下面的nth就是实现这个功能。
// 取某个方向上的第几个元素。Result是第几个
nth : function(cur, result, dir, elem) {
	result = result || 1;
	var num = 0;
	for (;cur; cur = cur[dir])
		if (cur.nodeType == 1 && ++num == result)
			break;
	return cur;
},
Nth与dir在一个方向有点不同的地方,就是dir不包含自身,而nth包含自身,如果result是1的话,那是自身的元素。如果没有找到就返回空(有可能是undifined,或null)。
接下来的sibling就比较简单,但是用途也会少很多。它实现从一个元素(包括自身)找到其所有后续兄弟(们),然后从这个后续的兄弟(们)排除一个指的元素。
// 从包含n元素开始的所有后续兄弟,但不包含elem。
sibling : function(n, elem) {
	var r = [];
	for (;n; n = n.nextSibling) {
		if (n.nodeType == 1 && n != elem)
			r.push(n);
	}
	return r;
}
仔细想一下,没有什么好适用的场合,但是jquery.fn.siblings中的用法倒是最合适的用法。找到自己的所有兄弟。找自己的所有兄弟最好的方法当然是找到父亲节点,然后排除自身节点。采用childNodes再查找当前元素并去掉和这个操作差不多,效率应该会高点。

 

分享到:
评论
1 楼 lipeng88213 2010-10-19  
引用
context = context.ownerDocument || context[0] 
                        && context[0].ownerDocument || document;


楼主能解释一下这个的用处吗 谢谢

相关推荐

    在vue中获取dom元素内容的方法

    本篇文章主要介绍了在vue中获取dom元素内容的方法,可以通过给标签加ref属性,这里整理了详细的代码,有兴趣的可以了解一下

    elementPresence:仅在页面中找到元素时才运行代码

    元素存在是一个简单的函数,用于在页面中找到DOM元素时解决承诺。 该函数不仅查找作为参数提供的特定选择器,还侦听DOM中的任何后续更改,这些更改可能导致注入我们在页面中寻找的DOM元素。 它还可以捕获DOM元素...

    准确找到DOM树的元素

    NULL 博文链接:https://sunny-xhh.iteye.com/blog/2236271

    jQuery中dom元素上绑定的事件详解

    所谓原生就是通过addEventListener方法绑定到dom元素上。这个好办: 审查绑定事件的元素(对着它右键-&gt;审查元素)在弹出的Elements视图的控制台,右侧点击“Event Listeners”页签然后就能看到所有绑定在该元素的...

    Angular.JS中的指令与参数详解

    JQuery通过选择器找到DOM元素,再赋予元素的行为; 而AngularJS则是,将指令与DOM绑定在一起,再扩展指令的行为。 所以AngularJS开发最理想的结果就是,在页面HTML与CSS的设计时,设计工程师只需要关注指令的使用...

    getselector:获取选择器浏览器扩展

    getselector 从Chrome网上应用店... 该扩展可帮助您在任何页面上找到DOM元素的唯一选择器。 只需单击图标启用扩展,将鼠标悬停在元素上,然后通过上下文菜单复制唯一选择器即可! 它将被复制到剪贴板。 那样简单! 基于

    ElementsCollector:返回 DOM 元素的 Javascript 选择引擎

    元素收集器 返回 DOM 元素的 Javascript 选择引擎。 按标签名称、id、类或这些的组合查找元素。 如何使用 克隆 repo 并在 chrome 中打开 test.html。 可以在控制台中找到通过测试的详细信息。

    JS获取动态添加元素的方法详解

    在页面的渲染中,我们经常用遇到动态添加的DOM元素,那么在获取这些元素的时候或则为这些DOM元素的时候,我们有下面三种处理方法: 1. 在模板引擎里面添加行内事件 2. 利用事件的委托获取(常用) 3. 在ajax的监听...

    ember-cli-dom-stats:在开发时将DOM元素数量快速添加到余烬应用程序

    ember-cli-dom-stats 一种快速技巧,可在小窗口中显示DOM元素总数和增量,类似于(完全基于) 和 我强烈建议您与此同时使用ember-browsery-stats ,因为我已将litle stats框编码为位于浏览器状态的左侧。演示版您...

    解决给dom元素绑定click等事件无效问题的方法

    之前一直遇到js插件生成的元素,click事件无效的问题,重新绑定也不行,终于找到解决方法,在此记下来。 同时加深对js事件处理机制的理解。 1. 事件被解绑 这种情况下一般不会导致click失效,但以下情况,click事件...

    js实现创建删除html元素小结

    首先需要找到被删元素的父元素,通过父元素将其需要删除的子元素删除。 var el = document.getElementById('div2js'); el[removed].removeChild(el); 2.使用JQuery 直接找到并删除。 $(‘#div

    element-to-source:提供使用(某些)框架提供的调试元数据将DOM元素映射到源信息的实用程序

    元素到源提供使用(某些)框架提供的调试元数据将DOM元素映射到源信息的实用程序。 API: import { resolve } from 'element-to-source' ; var domElement = document . getElementById ( '#main' ) ; var ...

    pluton:将JS类链接到dom元素JavaScript调度程序

    将JS类链接到dom元素JavaScript调度程序。 这是JS工作流的主要部分。 安装 NPM npm i @whitecube/pluton 纱 yarn add @whitecube/pluton 然后在您的代码中将其导入: import Pluton from '@whitecube/pluton' ; ...

    xtal-decorator:将属性方法添加到其他DOM(自定义)元素

    xtal-decorator是一个Web组件,能够在其他DOM元素(自定义或其他)上创建强大的深色魔术咒语。 xtal-decorator与和一起是组件的一部分。 xtal-deco和xtal-decor努力。 他们坚持选择加入的原则-要求DOM元素通过...

    ready:在初始页面加载和动态附加到DOM时检测元素可用性

    准备好 在初始页面加载和动态附加到DOM时检测元素可用性 ... 如果找到多个元素,则按文档顺序为每个元素连续调用回调。 或者,仅提供回调函数作为添加通用DOM ready事件侦听器的唯一参数: ready ( ( doc ) =&gt;

    JavaScript(四):JavaScript版的DOM总结

    属性 : value,style ,width2、DOM操作1) 单个HTML DOM节点的操作a、找到DOM节点b、DOM修改2)DOM结构的改变a、添加节点b、移除节点c、插入节点3)显示和隐藏 DOM总结 跟着老师从HTML入门,对CSS有了初步了解,再...

    restaurant:一个简单的页面,可通过Javascript呈现所有DOM元素

    该网站是使用Javascript创建的,用于呈现页面上的所有DOM元素。 这也是我第一次使用webpack,这非常令人满意(尽管我在部署Github页面时遇到了麻烦)。任务原始作业可在 找到。 以与开始webpack教程项目相同的方式...

    virtual-dom-docs-wip:虚拟 dom 文档编写时的临时位置

    概述virtual-dom 由四个主要部分组成: - 负责两个虚拟表示 DOM 节点 - 负责获取由的并使用它来修改渲染的 DOM - dom 元素的虚拟表示 - 从简单数据结构生成 VNode 的接口新手应该从阅读 VNode 和 VText 文档开始,...

    swipeit:用于处理 DOM 元素上的滑动事件的 Javascript 模块 - 在滑动时发出事件

    swipeit 是一个简单的触摸事件管理器,用于检测 DOM 元素上的滑动。 注册后,它会在滑动时发出事件; 我决定使用基数事件,因为它看起来比左、右更直接。 所以事件是东、西、北和南。 swipeit 是一个 Javascript ...

    dcblocks:Brackets 扩展,增加了在 HTMLXML 代码中轻松创建 DOM 元素注释块的能力

    这是 Brackets 的扩展,它增加了为 HTML/XML 代码中的 DOM 元素轻松创建注释块的能力。 有时我们需要编写 HTML 文档,然后将其发送给程序员。 他们希望能够轻松找到主要代码块(如果文档很大)——我们必须为此...

Global site tag (gtag.js) - Google Analytics