搜索
首页web前端js教程Prototype Selector对象学习_prototype

复制代码 代码如下:

function $$() {
return Selector.findChildElements(document, $A(arguments));
}

这个类可以分成三个部分:第一个部分就是根据不同的浏览器,判断使用什么DOM操作方法。其中操作IE就是用普通的getElementBy* 系列方法;FF是document.evaluate;Opera和Safari是selectorsAPI。第二部分是对外提供的基本函数,像findElements,match等,Element对象里面的很多方法就是直接调用这个对象里面的方法。第三部分就是XPath等一些查询DOM的匹配标准,比如什么的字符串代表的意思是查找first-child,什么的字符串代表的是查询nth-child。

由于这个对象里面的方法很多,就不给出所有的源码了,其实我自己也仅仅看懂了一些方法的代码而已。这里根据浏览器的不同用一个简单的例子走一遍进行DOM选择的流程。在这个过程中给出需要的源代码,并加以说明。

具体的例子如下:
复制代码 代码如下:








下面以FF为例进行说明,流程如下:
复制代码 代码如下:

/*先找到$$方法,上面已经给出了,在这个方法里面将调用Selector的findChildElements方法,并且第一个参数为document,剩下参数为DOM查询字符串的数组*/

findChildElements: function(element, expressions) {
//这里先调用split处理了一下字符串数组,判断是否合法,并且删除了空格
expressions = Selector.split(expressions.join(','));
//handlers里面包含了对DOM节点处理的一些方法,像concat,unique等
var results = [], h = Selector.handlers;
//逐个处理查询表达式
for (var i = 0, l = expressions.length, selector; i //新建Selector
selector = new Selector(expressions[i].strip());
//把查询到的节点连接到results里面
h.concat(results, selector.findElements(element));
}
//如果找到的节点数大于一,把重复节点过滤掉
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对象
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 if (typeof (n = nodes[i])._countedByPrototype == 'undefined') {
n._countedByPrototype = Prototype.emptyFunction;
results.push(Element.extend(n));
}
return Selector.handlers.unmark(results);
},

//下面转向新建Selector对象过程!!

复制代码 代码如下:

//先看Selector的初始化部分
//可以看出初始化部分就是判断要用什么方法操作DOM,下面看一个这几个方法
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,FF支持此种方法
shouldUseXPath: (function() {

//下面检查浏览器是否有BUG,具体这个BUG是怎么回事,我在网上也没搜到。大概意思就是检查一下能否正确找到某个节点的个数
var IS_DESCENDANT_SELECTOR_BUGGY = (function(){
var isBuggy = false;
if (document.evaluate && window.XPathResult) {
var el = document.createElement('div');
el.innerHTML = '
';
//这里的local-name()的意思就是去掉命名空间进行查找
var xpath = ".//*[local-name()='ul' or local-name()='UL']"
"//*[local-name()='li' or local-name()='LI']";
//document.evaluate是核心的DOM查询方法,具体的使用可以到网上搜
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表达式和empty表达式的操作
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里面进行查询是否会抛出异常
try {
Selector._div.querySelector(this.expression);
} catch(e) {
return false;
}

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

//Selector.CASE_INSENSITIVE_CLASS_NAMES属性
/*document.compatMode用来判断当前浏览器采用的渲染方式。
当document.compatMode等于BackCompat时,浏览器客户区宽度是document.body.clientWidth;
当document.compatMode等于CSS1Compat时,浏览器客户区宽度是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;
},

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

//如果这两个都不是就用document.getElement(s)By*系列方法进行处理,貌似IE8开始支持SelectorAPI了,其余版本IE就只能用普通的方法进行DOM查询了

//下面转向FF支持的shouldUseXPath方法!!!

复制代码 代码如下:

//当判断要用XPath进行查询时,就开始调用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;
}
// './/*'表示在当前节点下查询所有节点 不懂得可以去网上看一下XPath的表示方法
this.matcher = ['.//*'];
//这里的le防止无限循环查找,那个正则表达式匹配除单个空格符之外的所有字符
while (e && le != e && (/S/).test(e)) {
le = e;
//逐个查找pattern
for (var i = 0; i//这里的name就是pattern里面对象的name属性
name = ps[i].name;
//这里查看表达式是否匹配这个pattern的正则表达式        
if (m = e.match(ps[i].re)) {
/*
注意这里,下面的xpath里面有的是方法,有的是字符串,所以这里需要判断一下,字符串的话,需要调用Template的evaluate方法,替换里面的#{...}字符串;是方法的话,那就传入正确的参数调用方法
*/
this.matcher.push(Object.isFunction(x[name]) ? x[name](m) :
new Template(x[name]).evaluate(m));
//把匹配的部分去掉,继续下面的字符串匹配
e = e.replace(m[0], '');

break;
}
}

}
//把所有的匹配的xpath表达式连接起来,组成最终的xpath查询字符串
this.xpath = this.matcher.join('');
//放到缓存中
Selector._cache[this.expression] = this.xpath;
},
//==============================================

//这些patterns就是判断查询字符串到底是要查找什么,根据相应的整个表达式来判断,譬如字符串'#navbar'根据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|([^'"][^
]]*?)))?]/ }
],

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

/*当找到pattern之后,在用对应的name找到相应的查询字符串的xpath表示形式。比如上面的id,对应的就是id字符串,在compileXPathMatcher里面会判断xpath是字符串还是方法,是方法则会传进来相应的参数进行调用*/
xpath: {
descendant: "//*",
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}']",
//...省略一些方法

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

//下面进入Selector的findElements方法!!

复制代码 代码如下:

findElements: function(root) {
//判断root是否null,为null则设置成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, "\$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);
}
},

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

//这个方法其实就是把查找到的节点放到results里,并且返回,这里用到了document.evaluate,下面给出了这个方法详细解释的网址
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 results.push(Element.extend(query.snapshotItem(i)));
return results;
};
}

/*
下面这个网址是document.evaluate的方法解释:https://developer.mozilla.org/cn/DOM/document.evaluate
*/

下面使用给出的例子连续起来解释一下:

首先$$里面调用findChildElements方法,expressions被设置为['#navbar a','#siderbar a']

下面调用:selector = new Selector(expressions[i].strip());新建一个Selector对象,调用initialize方法,也就是判断用什么DOM API,由于是FF,所以是this.shouldUseXPath(),然后调用compileXPathMatcher()

然后compileXPathMatcher()里面的 var e = this.expression,把e设置成'#navbar a',然后进入while循环,遍历patterns,检查查询字符串的匹配模式,这里根据pattern的正则表达式,找到{ name: 'id', re: /^#([w-*] )(b|$)/ },,所以name为id,当m = e.match(ps[i].re)匹配之后,m被设置成一个数组,其中m[0]就是整个匹配的字符串'#navbar',m[1]就是匹配的第一个分组字符串'navbar'

接下来判断Object.isFunction(x[name]),由于id对应的是字符串,所以执行new Template(x[name]).evaluate(m)),字符串:id: "[@id='#{1}']",中的#{1}被替换成m[1],即'navbar',最后把结果放到this.matcher中

然后通过把第一个匹配的字符串删除,e变成了' a',这里有一个空格!接下来继续进行匹配

这次匹配到的是:{ name: 'descendant', re: /^s/ },然后找到xpath中对应的descendant项:descendant: "//*",然后把这个字符串放到this.matcher中,去掉空格e只剩下字符'a'了,继续匹配

这词匹配到的是:{ name: 'tagName', re: /^s*(*|[w-] )(b|$)?/ },然后找到tagName对应的xpath项,

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

是个方法,所以会调用x[name](m),而m[1]='a',返回下面的那串字符,然后在放到this.matcher里,这次e为空串,while的第一个条件不满足,退出循环,把this.matcher数组连接成一个xpath字符串: .//*[@id='navbar']//*[local-name()='a' or local-name()='A']

在初始化完Selector后,执行Selector的实例方法findElements,这里直接调用:document._getElementsByXPath(this.xpath, root);

在_getElementsByXPath方法里执行真正的DOM查询方法document.evaluate,最后返回结果

以上就是整个查询DOM在FF下的流程!

在IE下和Opera,safari下流程是一样的,只不过执行的具体方法略有不同,有兴趣可以自己研究研究,那些复杂的DOM选择操作就不举例子了。这里构造的流程是非常值得学习的,包括通过pattern模式匹配进行xpath的生成,把那些patterns,xpath等提出来。

可以看出来,写一个兼容所有浏览器的框架真是不容易!学习学习!
声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
使用PHP的json_encode()函数将数组或对象转换为JSON字符串使用PHP的json_encode()函数将数组或对象转换为JSON字符串Nov 03, 2023 pm 03:30 PM

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式,已经成为Web应用程序之间数据交换的常用格式。PHP的json_encode()函数可以将数组或对象转换为JSON字符串。本文将介绍如何使用PHP的json_encode()函数,包括语法、参数、返回值以及具体的示例。语法json_encode()函数的语法如下:st

源码探秘:Python 中对象是如何被调用的?源码探秘:Python 中对象是如何被调用的?May 11, 2023 am 11:46 AM

楔子我们知道对象被创建,主要有两种方式,一种是通过Python/CAPI,另一种是通过调用类型对象。对于内置类型的实例对象而言,这两种方式都是支持的,比如列表,我们即可以通过[]创建,也可以通过list(),前者是Python/CAPI,后者是调用类型对象。但对于自定义类的实例对象而言,我们只能通过调用类型对象的方式来创建。而一个对象如果可以被调用,那么这个对象就是callable,否则就不是callable。而决定一个对象是不是callable,就取决于其对应的类型对象中是否定义了某个方法。如

使用Python的__contains__()函数定义对象的包含操作使用Python的__contains__()函数定义对象的包含操作Aug 22, 2023 pm 04:23 PM

使用Python的__contains__()函数定义对象的包含操作Python是一种简洁而强大的编程语言,提供了许多强大的功能来处理各种类型的数据。其中之一是通过定义__contains__()函数来实现对象的包含操作。本文将介绍如何使用__contains__()函数来定义对象的包含操作,并且给出一些示例代码。__contains__()函数是Pytho

使用Python的__le__()函数定义两个对象的小于等于比较使用Python的__le__()函数定义两个对象的小于等于比较Aug 21, 2023 pm 09:29 PM

标题:使用Python的__le__()函数定义两个对象的小于等于比较在Python中,我们可以通过使用特殊方法来定义对象之间的比较操作。其中之一就是__le__()函数,它用于定义小于等于比较。__le__()函数是Python中的一个魔法方法,并且是一种用于实现“小于等于”操作的特殊函数。当我们使用小于等于运算符(<=)比较两个对象时,Python

如何实现Java底层技术之IO模型与Selector如何实现Java底层技术之IO模型与SelectorNov 08, 2023 pm 06:00 PM

实现Java底层技术之IO模型与Selector在Java编程中,IO(Input-Output)模型以及Selector是非常重要的底层技术,它们对于高效地处理网络通信和文件操作至关重要。在本文中,我们将深入探讨Java中IO模型与Selector的实现原理,并提供具体的代码示例来帮助读者更好地理解这些概念。一、IO模型阻塞IO阻塞IO模型是最基本的一种I

详解Javascript对象的5种循环遍历方法详解Javascript对象的5种循环遍历方法Aug 04, 2022 pm 05:28 PM

Javascript对象如何循环遍历?下面本篇文章给大家详细介绍5种JS对象遍历方法,并浅显对比一下这5种方法,希望对大家有所帮助!

Python中如何使用getattr()函数获取对象的属性值Python中如何使用getattr()函数获取对象的属性值Aug 22, 2023 pm 03:00 PM

Python中如何使用getattr()函数获取对象的属性值在Python编程中,我们经常会遇到需要获取对象属性值的情况。Python提供了一个内置函数getattr()来帮助我们实现这个目标。getattr()函数允许我们通过传递对象和属性名称作为参数来获取该对象的属性值。本文将详细介绍getattr()函数的用法,并提供实际的代码示例,以便更好地理解。g

使用Python的isinstance()函数判断对象是否属于某个类使用Python的isinstance()函数判断对象是否属于某个类Aug 22, 2023 am 11:52 AM

使用Python的isinstance()函数判断对象是否属于某个类在Python中,我们经常需要判断一个对象是否属于某个特定的类。为了方便地进行类别判断,Python提供了一个内置函数isinstance()。本文将介绍isinstance()函数的用法,并提供代码示例。isinstance()函数可以判断一个对象是否属于指定的类或类的派生类。它的语法如下

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
3 周前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
3 周前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
3 周前By尊渡假赌尊渡假赌尊渡假赌

热工具

Atom编辑器mac版下载

Atom编辑器mac版下载

最流行的的开源编辑器

Dreamweaver Mac版

Dreamweaver Mac版

视觉化网页开发工具

安全考试浏览器

安全考试浏览器

Safe Exam Browser是一个安全的浏览器环境,用于安全地进行在线考试。该软件将任何计算机变成一个安全的工作站。它控制对任何实用工具的访问,并防止学生使用未经授权的资源。

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一个PHP/MySQL的Web应用程序,非常容易受到攻击。它的主要目标是成为安全专业人员在合法环境中测试自己的技能和工具的辅助工具,帮助Web开发人员更好地理解保护Web应用程序的过程,并帮助教师/学生在课堂环境中教授/学习Web应用程序安全。DVWA的目标是通过简单直接的界面练习一些最常见的Web漏洞,难度各不相同。请注意,该软件中

mPDF

mPDF

mPDF是一个PHP库,可以从UTF-8编码的HTML生成PDF文件。原作者Ian Back编写mPDF以从他的网站上“即时”输出PDF文件,并处理不同的语言。与原始脚本如HTML2FPDF相比,它的速度较慢,并且在使用Unicode字体时生成的文件较大,但支持CSS样式等,并进行了大量增强。支持几乎所有语言,包括RTL(阿拉伯语和希伯来语)和CJK(中日韩)。支持嵌套的块级元素(如P、DIV),