天天躁日日躁狠狠躁AV麻豆-天天躁人人躁人人躁狂躁-天天澡夜夜澡人人澡-天天影视香色欲综合网-国产成人女人在线视频观看-国产成人女人视频在线观看

Prototype Selector對(duì)象學(xué)習(xí)

復(fù)制代碼 代碼如下:
function $$() {
return Selector.findChildElements(document, $A(arguments));
}

這個(gè)類可以分成三個(gè)部分:第一個(gè)部分就是根據(jù)不同的瀏覽器,判斷使用什么DOM操作方法。其中操作IE就是用普通的getElementBy* 系列方法;FF是document.evaluate;Opera和Safari是selectorsAPI。第二部分是對(duì)外提供的基本函數(shù),像findElements,match等,Element對(duì)象里面的很多方法就是直接調(diào)用這個(gè)對(duì)象里面的方法。第三部分就是XPath等一些查詢DOM的匹配標(biāo)準(zhǔn),比如什么的字符串代表的意思是查找first-child,什么的字符串代表的是查詢nth-child。

由于這個(gè)對(duì)象里面的方法很多,就不給出所有的源碼了,其實(shí)我自己也僅僅看懂了一些方法的代碼而已。這里根據(jù)瀏覽器的不同用一個(gè)簡單的例子走一遍進(jìn)行DOM選擇的流程。在這個(gè)過程中給出需要的源代碼,并加以說明。

具體的例子如下:
復(fù)制代碼 代碼如下:
<div id="parent2">
<div id="navbar">
<a id="n1"></a>
<a></a>
</div>
<div id="sidebar">
<a id="s1"></a>
<a></a>
</div>
</div>

<script type="text/Javascript"><!--
        $$('#navbar a', '#sidebar a')
// --></script>

下面以FF為例進(jìn)行說明,流程如下:
復(fù)制代碼 代碼如下:
/*先找到$$方法,上面已經(jīng)給出了,在這個(gè)方法里面將調(diào)用Selector的findChildElements方法,并且第一個(gè)參數(shù)為document,剩下參數(shù)為DOM查詢字符串的數(shù)組*/

findChildElements: function(element, expressions) {
//這里先調(diào)用split處理了一下字符串?dāng)?shù)組,判斷是否合法,并且刪除了空格
expressions = Selector.split(expressions.join(','));
//handlers里面包含了對(duì)DOM節(jié)點(diǎn)處理的一些方法,像concat,unique等
var results = [], h = Selector.handlers;
//逐個(gè)處理查詢表達(dá)式
for (var i = 0, l = expressions.length, selector; i < l; i++) {
//新建Selector
selector = new Selector(expressions[i].strip());
//把查詢到的節(jié)點(diǎn)連接到results里面
h.concat(results, selector.findElements(element));
}
//如果找到的節(jié)點(diǎn)數(shù)大于一,把重復(fù)節(jié)點(diǎn)過濾掉
return (l > 1) ? h.unique(results) : results;
}

//===================================================

//Selector.split方法:
split: function(expression) {
var expressions = [];
expression.scan(/(([/w#:.~>+()/s-]+|/*|/[.*?/])+)/s*(,|$)/, function(m) {
        //alert(m[1]);
expressions.push(m[1].strip());
});
return expressions;
}

//===================================================

//Selector.handlers對(duì)象
handlers: {
concat: function(a, b) {
for (var i = 0, node; node = b[i]; i++)
a.push(node);
return a;
},
//...省略一些方法
unique: function(nodes) {
if (nodes.length == 0) return nodes;
var results = [], n;
for (var i = 0, l = nodes.length; i < l; i++)
if (typeof (n = nodes[i])._countedByPrototype == 'undefined') {
n._countedByPrototype = Prototype.emptyFunction;
results.push(Element.extend(n));
}
return Selector.handlers.unmark(results);
},

//下面轉(zhuǎn)向新建Selector對(duì)象過程!!

復(fù)制代碼 代碼如下:
//先看Selector的初始化部分
//可以看出初始化部分就是判斷要用什么方法操作DOM,下面看一個(gè)這幾個(gè)方法
var Selector = Class.create({
initialize: function(expression) {
this.expression = expression.strip();

if (this.shouldUseSelectorsAPI()) {
this.mode = 'selectorsAPI';
} else if (this.shouldUseXPath()) {
this.mode = 'xpath';
this.compileXPathMatcher();
} else {
this.mode = "normal";
this.compileMatcher();
}

}

//===================================================

//XPath,F(xiàn)F支持此種方法
shouldUseXPath: (function() {

//下面檢查瀏覽器是否有BUG,具體這個(gè)BUG是怎么回事,我在網(wǎng)上也沒搜到。大概意思就是檢查一下能否正確找到某個(gè)節(jié)點(diǎn)的個(gè)數(shù)
var IS_DESCENDANT_SELECTOR_BUGGY = (function(){
var isBuggy = false;
if (document.evaluate && window.XPathResult) {
var el = document.createElement('div');
el.innerHTML = '<ul><li></li></ul><div><ul><li></li></ul></div>';
//這里的local-name()的意思就是去掉命名空間進(jìn)行查找
var xpath = ".//*[local-name()='ul' or local-name()='UL']" +
"http://*[local-name()='li' or local-name()='LI']";
//document.evaluate是核心的DOM查詢方法,具體的使用可以到網(wǎng)上搜
var result = document.evaluate(xpath, el, null,
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);

isBuggy = (result.snapshotLength !== 2);
el = null;
}
return isBuggy;
})();

return function() {
//返回的方法中判斷是否支持此種DOM操作。
if (!Prototype.BrowserFeatures.XPath) return false;

var e = this.expression;
//這里可以看到Safari不支持-of-type表達(dá)式和empty表達(dá)式的操作
if (Prototype.Browser.WebKit &&
(e.include("-of-type") || e.include(":empty")))
return false;

if ((/(/[[/w-]*?:|:checked)/).test(e))
return false;

if (IS_DESCENDANT_SELECTOR_BUGGY) return false;

return true;
}

})(),

//===================================================

//Sarafi和opera支持此種方法
shouldUseSelectorsAPI: function() {
if (!Prototype.BrowserFeatures.SelectorsAPI) return false;
//這里判斷是否支持大小寫敏感查找
if (Selector.CASE_INSENSITIVE_CLASS_NAMES) return false;

if (!Selector._div) Selector._div = new Element('div');
//檢查一下在空div里面進(jìn)行查詢是否會(huì)拋出異常
try {
Selector._div.querySelector(this.expression);
} catch(e) {
return false;
}

//===================================================

//Selector.CASE_INSENSITIVE_CLASS_NAMES屬性
/*document.compatMode用來判斷當(dāng)前瀏覽器采用的渲染方式。
當(dāng)document.compatMode等于BackCompat時(shí),瀏覽器客戶區(qū)寬度是document.body.clientWidth;
當(dāng)document.compatMode等于CSS1Compat時(shí),瀏覽器客戶區(qū)寬度是document.documentElement.clientWidth。*/

if (Prototype.BrowserFeatures.SelectorsAPI &&
document.compatMode === 'BackCompat') {
Selector.CASE_INSENSITIVE_CLASS_NAMES = (function(){
var div = document.createElement('div'),
span = document.createElement('span');

div.id = "prototype_test_id";
span.className = 'Test';
div.appendChild(span);
var isIgnored = (div.querySelector('#prototype_test_id .test') !== null);
div = span = null;
return isIgnored;
})();
}

return true;
},

//===================================================

//如果這兩個(gè)都不是就用document.getElement(s)By*系列方法進(jìn)行處理,貌似IE8開始支持SelectorAPI了,其余版本IE就只能用普通的方法進(jìn)行DOM查詢了

//下面轉(zhuǎn)向FF支持的shouldUseXPath方法!!!

復(fù)制代碼 代碼如下:
//當(dāng)判斷要用XPath進(jìn)行查詢時(shí),就開始調(diào)用compileXPathMatcher方法了

compileXPathMatcher: function() {
//底下給出patterns,和xpath
var e = this.expression, ps = Selector.patterns,
x = Selector.xpath, le, m, len = ps.length, name;

//判斷是否緩存了查詢字符串e
if (Selector._cache[e]) {
this.xpath = Selector._cache[e]; return;
}
// './/*'表示在當(dāng)前節(jié)點(diǎn)下查詢所有節(jié)點(diǎn) 不懂得可以去網(wǎng)上看一下XPath的表示方法
this.matcher = ['.//*'];
//這里的le防止無限循環(huán)查找,那個(gè)正則表達(dá)式匹配除單個(gè)空格符之外的所有字符
while (e && le != e && (//S/).test(e)) {
le = e;
//逐個(gè)查找pattern
for (var i = 0; i<len; i++) {
//這里的name就是pattern里面對(duì)象的name屬性
name = ps[i].name;
//這里查看表達(dá)式是否匹配這個(gè)pattern的正則表達(dá)式        
if (m = e.match(ps[i].re)) {
/*
注意這里,下面的xpath里面有的是方法,有的是字符串,所以這里需要判斷一下,字符串的話,需要調(diào)用Template的evaluate方法,替換里面的#{...}字符串;是方法的話,那就傳入正確的參數(shù)調(diào)用方法
*/
this.matcher.push(Object.isFunction(x[name]) ? x[name](m) :
new Template(x[name]).evaluate(m));
//把匹配的部分去掉,繼續(xù)下面的字符串匹配
e = e.replace(m[0], '');

break;
}
}

}
//把所有的匹配的xpath表達(dá)式連接起來,組成最終的xpath查詢字符串
this.xpath = this.matcher.join('');
//放到緩存中
Selector._cache[this.expression] = this.xpath;
},
//==============================================

//這些patterns就是判斷查詢字符串到底是要查找什么,根據(jù)相應(yīng)的整個(gè)表達(dá)式來判斷,譬如字符串'#navbar'根據(jù)patterns匹配,那么就是id
patterns: [
{ name: 'laterSibling', re: /^/s*~/s*/ },
{ name: 'child', re: /^/s*>/s*/ },
{ name: 'adjacent', re: /^/s*/+/s*/ },
{ name: 'descendant', re: /^/s/ },
{ name: 'tagName', re: /^/s*(/*|[/w/-]+)(/b|$)?/ },
{ name: 'id', re: /^#([/w/-/*]+)(/b|$)/ },
{ name: 'className', re: /^/.([/w/-/*]+)(/b|$)/ },
{ name: 'pseudo', re:
/^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|d
is)abled|not)(/((.*?)/))?(/b|$|(?=/s|[:+~>]))/ },
{ name: 'attrPresence', re: /^/[((?:[/w-]+:)?[/w-]+)/]/ },
{ name: 'attr', re:
//[((?:[/w-]*:)?[/w-]+)/s*(?:([!^$*~|]?=)/s*((['"])([^/4]*?)/4|([^'"][^
/]]*?)))?/]/ }
],

//==============================================

/*當(dāng)找到pattern之后,在用對(duì)應(yīng)的name找到相應(yīng)的查詢字符串的xpath表示形式。比如上面的id,對(duì)應(yīng)的就是id字符串,在compileXPathMatcher里面會(huì)判斷xpath是字符串還是方法,是方法則會(huì)傳進(jìn)來相應(yīng)的參數(shù)進(jìn)行調(diào)用*/
xpath: {
descendant: "http://*",
child: "/*",
adjacent: "/following-sibling::*[1]",
laterSibling: '/following-sibling::*',
tagName: function(m) {
if (m[1] == '*') return '';
return "[local-name()='" + m[1].toLowerCase() +
"' or local-name()='" + m[1].toUpperCase() + "']";
},
className: "[contains(concat(' ', @class, ' '), ' #{1} ')]",
id: "[@id='#{1}']",
//...省略一些方法

//==============================================

//下面進(jìn)入Selector的findElements方法!!

復(fù)制代碼 代碼如下:
findElements: function(root) {
//判斷root是否null,為null則設(shè)置成document
root = root || document;
var e = this.expression, results;
//判斷是用哪種模式操作DOM,在FF下是xpath
switch (this.mode) {
case 'selectorsAPI':

if (root !== document) {
var oldId = root.id, id = $(root).identify();
id = id.replace(/[/.:]/g, "http://$0");
e = "#" + id + " " + e;

}
results = $A(root.querySelectorAll(e)).map(Element.extend);
root.id = oldId;

return results;
case 'xpath':
//下面看一下_getElementsByXPath方法
return document._getElementsByXPath(this.xpath, root);
default:
return this.matcher(root);
}
},

//===========================================

//這個(gè)方法其實(shí)就是把查找到的節(jié)點(diǎn)放到results里,并且返回,這里用到了document.evaluate,下面給出了這個(gè)方法詳細(xì)解釋的網(wǎng)址
if (Prototype.BrowserFeatures.XPath) {
document._getElementsByXPath = function(expression, parentElement) {
var results = [];
var query = document.evaluate(expression, $(parentElement) || document,
null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
for (var i = 0, length = query.snapshotLength; i < length; i++)
results.push(Element.extend(query.snapshotItem(i)));
return results;
};
}

/*
下面這個(gè)網(wǎng)址是document.evaluate的方法解釋:https://developer.mozilla.org/cn/DOM/document.evaluate
*/

下面使用給出的例子連續(xù)起來解釋一下:

首先$$里面調(diào)用findChildElements方法,expressions被設(shè)置為['#navbar a','#siderbar a']

下面調(diào)用:selector = new Selector(expressions[i].strip());新建一個(gè)Selector對(duì)象,調(diào)用initialize方法,也就是判斷用什么DOM API,由于是FF,所以是this.shouldUseXPath(),然后調(diào)用compileXPathMatcher()

然后compileXPathMatcher()里面的 var e = this.expression,把e設(shè)置成'#navbar a',然后進(jìn)入while循環(huán),遍歷patterns,檢查查詢字符串的匹配模式,這里根據(jù)pattern的正則表達(dá)式,找到{ name: 'id', re: /^#([/w/-/*]+)(/b|$)/ },,所以name為id,當(dāng)m = e.match(ps[i].re)匹配之后,m被設(shè)置成一個(gè)數(shù)組,其中m[0]就是整個(gè)匹配的字符串'#navbar',m[1]就是匹配的第一個(gè)分組字符串'navbar'

接下來判斷Object.isFunction(x[name]),由于id對(duì)應(yīng)的是字符串,所以執(zhí)行new Template(x[name]).evaluate(m)),字符串:id: "[@id='#{1}']",中的#{1}被替換成m[1],即'navbar',最后把結(jié)果放到this.matcher中

然后通過把第一個(gè)匹配的字符串刪除,e變成了' a',這里有一個(gè)空格!接下來繼續(xù)進(jìn)行匹配

這次匹配到的是:{ name: 'descendant', re: /^/s/ },然后找到xpath中對(duì)應(yīng)的descendant項(xiàng):descendant: "http://*",然后把這個(gè)字符串放到this.matcher中,去掉空格e只剩下字符'a'了,繼續(xù)匹配

這詞匹配到的是:{ name: 'tagName', re: /^/s*(/*|[/w/-]+)(/b|$)?/ },然后找到tagName對(duì)應(yīng)的xpath項(xiàng),

tagName: function(m) {
if (m[1] == '*') return '';
return "[local-name()='" + m[1].toLowerCase() +
"' or local-name()='" + m[1].toUpperCase() + "']";
}

是個(gè)方法,所以會(huì)調(diào)用x[name](m),而m[1]='a',返回下面的那串字符,然后在放到this.matcher里,這次e為空串,while的第一個(gè)條件不滿足,退出循環(huán),把this.matcher數(shù)組連接成一個(gè)xpath字符串: .//*[@id='navbar']//*[local-name()='a' or local-name()='A']

在初始化完Selector后,執(zhí)行Selector的實(shí)例方法findElements,這里直接調(diào)用:document._getElementsByXPath(this.xpath, root);

在_getElementsByXPath方法里執(zhí)行真正的DOM查詢方法document.evaluate,最后返回結(jié)果

以上就是整個(gè)查詢DOM在FF下的流程!

在IE下和Opera,safari下流程是一樣的,只不過執(zhí)行的具體方法略有不同,有興趣可以自己研究研究,那些復(fù)雜的DOM選擇操作就不舉例子了。這里構(gòu)造的流程是非常值得學(xué)習(xí)的,包括通過pattern模式匹配進(jìn)行xpath的生成,把那些patterns,xpath等提出來。

可以看出來,寫一個(gè)兼容所有瀏覽器的框架真是不容易!學(xué)習(xí)學(xué)習(xí)!

JavaScript技術(shù)Prototype Selector對(duì)象學(xué)習(xí),轉(zhuǎn)載需保留來源!

鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請(qǐng)第一時(shí)間聯(lián)系我們修改或刪除,多謝。

主站蜘蛛池模板: 亚洲视频精品在线观看 | 国产成人免费在线观看 | 99久久精品免费看国产一区二区 | 久久成人免费观看全部免费 | 久久亚洲AV成人无码动态图 | 国产精品久久久久久免费播放 | 涩涩视频在线看 | 强壮的公次次弄得我高潮韩国电影 | 极品内射少妇精品无码视频 | 性女传奇 快播 | 99re久久免费热在线视频手机 | 精品夜夜澡人妻无码AV蜜桃 | 娇小XXXXX第一次出血 | 后入式狂顶免费视频 | 久久精品国产亚洲AV天美18 | 久久性综合亚洲精品电影网 | yellow视频免费观看高清在线 | 国产高清国内精品福利色噜噜 | 日本亚洲欧洲免费旡码 | 热久久国产欧美一区二区精品 | 亚洲精品伊人久久久久 | 啊好大好厉害好爽真骚 | 欧美久久综合网 | 久久中文电影 | 耻辱诊察室1一4集动漫在线观看 | 极品内射少妇精品无码视频 | 国产亚洲精品久久久久久国 | 中文字幕亚洲男人的天堂网络 | 超碰在线97久久视频观看 | 宝贝你骚死哥了好爽 | 97在线看视频福利免费 | 一区二区三区国产亚洲网站 | 97国产蝌蚪视频在线观看 | 手机在线成人精品视频网 | 麻豆精品2021最新 | 24小时日本在线观看片免费 | 亚洲欧美强伦一区二区另类 | 伊人久久大香线蕉综合电影 | 无码人妻精品国产婷婷 | 柠檬福利精品视频导航 | 高清国产激情视频在线观看 |