在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。
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的标签片断的修正。这也是上面的代码处理的重点。
文章来自:精灵部落
作者:prk(彭仁夔) QQ:546711211 email:sjkjs155@126.com 水平有限,难免有错误,请多多请教
如对本文有疑问,请提交到交流论坛,广大热心网友会为你解答!! 点击进入论坛