元素的查询Ext.DomQuery
该类结合css选择器可以提供高效的查询。首先说一下DOM中对文档元素的查询,主要有以下方法
- document.getElementById(id)
- element.getElementsByTagName(tagName)
DOM对应的6个属性来获取其父、子及兄弟节点的引用
- parentNode 指向其父节点的引用
- previousSibling 指向前一个兄弟节点的引用
- nextSibling 指向后一个兄弟节点的引用
- firstChild 指向其第一个节点的引用
- lastChild 指向其最后一个节点的引用
- childNodes 返回所有子节点的引用的集合
下面看该类中几个主要方法。先看combination过滤器的实现,共有四种模式,分别为EF,E/F(E>F),E+F,E~F,格模式详见程序中的注释和实现。
- /**
- * 实现combination选择器的查询方式
- * @param {} ns 要查询的元素或元素集合
- * @param {} mode 查询模式
- * @param {} tagName 标签名
- */
- function getNodes(ns, mode, tagName){
- var result = [], ri = -1, cs;
- if(!ns){
- return result;
- }
- tagName = tagName || "*";//没有指定tagName,默认为所有的元素,注意这里用*来查询所有
- //ns参数值为element时,先封装成数组,后续统一处理
- if(typeof ns.getElementsByTagName != "undefined"){
- ns = [ns];
- }
- if(!mode){//后代选择器,符号值为 " " E F模式,查询E标签下所有标签名为F的后代元素
- for(var i = 0, ni; ni = ns[i]; i++){//找到结果集中所有满足的元素
- cs = ni.getElementsByTagName(tagName);
- for(var j = 0, ci; ci = cs[j]; j++){//找到所有满足tagName的后代元素
- result[++ri] = ci;
- }
- }
- } else if(mode == "/" || mode == ">"){// E/F 或E>F 模式,查询E标签下所有标签名为F的子元素,只有一层,而EF模式为所有后代元素,会有好几层的查询
- var utag = tagName.toUpperCase();
- for(var i = 0, ni, cn; ni = ns[i]; i++){
- cn = ni.childNodes;
- for(var j = 0, cj; cj = cn[j]; j++){
- if(cj.nodeName == utag || cj.nodeName == tagName || tagName == '*'){
- result[++ri] = cj;
- }
- }
- }
- }else if(mode == "+"){// E+F模式,在E标签下的所有元素集合中,查找每个元素的相邻的后续兄弟元素(只找第一个后续兄弟)中标签为F的元素
- var utag = tagName.toUpperCase();
- for(var i = 0, n; n = ns[i]; i++){
- while((n = n.nextSibling) && n.nodeType != 1);
- if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
- result[++ri] = n;
- }
- }
- }else if(mode == "~"){// E~F模式,在E标签下的所有元素集合中,查找每个元素的相邻的后续兄弟元素中标签为F的元素
- var utag = tagName.toUpperCase();
- for(var i = 0, n; n = ns[i]; i++){
- while((n = n.nextSibling)){
- if (n.nodeName == utag || n.nodeName == tagName || tagName == '*'){
- result[++ri] = n;
- }
- }
- }
- }
- return result;
- }
接下来看过滤器的实现。Ext.DomQuery中提供了5中过滤方式:id、class、tagName、attribute、pseudo(伪操作根据函数),为此也相应的提供了byId、byClassName、byTag、byAttribute、byPseudo 5个函数来实现,下面主要看byAttribute
- /**
- *
- * @param {} cs 要查询的元素结合
- * @param {} attr 属性名
- * @param {} value 属性对应的值
- * @param {} op 操作符
- * @param {} custom 对于元素属性,采用[],对于css样式则采用{}
- */
- function byAttribute(cs, attr, value, op, custom){
- var result = [],
- ri = -1,
- useGetStyle = custom == "{",
- fn = Ext.DomQuery.operators[op],//DOM操作符
- a,//根据判断的属性来取得元素对应的属性值或css样式属性值
- xml,
- hasXml;
- for(var i = 0, ci; ci = cs[i]; i++){
- // skip non-element nodes.
- if(ci.nodeType != 1){//忽略不是元素节点
- continue;
- }
- // only need to do this for the first node
- if(!hasXml){
- xml = Ext.DomQuery.isXml(ci);
- hasXml = true;
- }
- // we only need to change the property names if we're dealing with html nodes, not XML
- // html节点的处理
- if(!xml){
- if(useGetStyle){
- a = Ext.DomQuery.getStyle(ci, attr);
- } else if (attr == "class" || attr == "className"){
- a = ci.className;
- } else if (attr == "for"){
- a = ci.htmlFor;
- } else if (attr == "href"){
- // getAttribute href bug
- // http://www.glennjones.net/Post/809/getAttributehrefbug.htm
- a = ci.getAttribute("href", 2);
- } else{
- a = ci.getAttribute(attr);
- }
- }else{
- a = ci.getAttribute(attr);
- }
- if((fn && fn(a, value)) || (!fn && a)){
- result[++ri] = ci;
- }
- }
- return result;
- }
下面看select方法:选择一组元素
- select : document.querySelectorAll ? function(path, root, type) {
- root = root || document;
- if (!Ext.DomQuery.isXml(root)) {
- try {
- var cs = root.querySelectorAll(path);
- return Ext.toArray(cs);
- } catch (ex) {
- }
- }
- return Ext.DomQuery.jsSelect.call(this, path, root, type);
- } : function(path, root, type) {
- return Ext.DomQuery.jsSelect.call(this, path, root, type);
- },
该函数分为两种情况,如果支持document.querySelectorAll,优先调用该方法返回。调用传入的参数path为选择器,root为开始查询的节点,默认为document。该方法的别名为"Ext.query = Ext.DomQuery.select;"。另外该类也是单例模式,代码实现比较复杂,内部用到了缓存机制,代码中有三个缓存变量
- var cache = {},
- simpleCache = {},
- valueCache = {},
- cache缓存了选择器的编译(compile)结果
- simpleCache缓存了选择器的简单(simple)编译结果
- valueCache 缓存了选择器查询元素的值