Dojo 的css 2.0 lite 选择器源代码

Dojo 包含两个选择器 lite.js 和 acme.js,lite只是一个基础的选择器,包含id,className,tagName,以及css2.0的属性选择器, 文件库相对于acme.js来说小很多,常用的选择方法都已经覆盖, 而acme.js 比较全面,支持全部的css3.0的要求, 但文件很大


Dojo中还支持 sizzle,可以通过selectorEngine 来自定义选择器, 关于选择器的使用可以参考http://dojotoolkit.org/reference-guide/1.10/dojo/query.html


以下是对 lite.js 源码注释


/*
    选择器分为两部分:
        拥有 querySelectorAll的浏览器
                     正则表达式判断出 #id,.class,input等,不是 css格式的选择符
                        分别调用 getElementById,getElementsByClassName,getElementsByTagName
                            对于没有getElementsByClassName的浏览器IE8,则调用querySelectorAll
                     没有符合正则表达式, css 的选择器字符串 "div#id .a"
                        直接调用 querySelectorAll 函数
        没有 querySelectorAll的浏览器 IE6,IE7
                    如果查询字符串中包含逗号分隔 "div,input"
                        调用combine方法,分隔成单独的查询字符串,在执行以下的步骤

                    正则表达式会匹配 div#clock .aa i[id='test'],div,clock,.aa i[id='test'],先查找 #clock元素,然后设用 .aa i[id='test'],#clock,变为 .aa i[id='test'],.aa,i,[id='test']
                    正则表达式匹配 .aa i[id='test']
                        先在root里找getElementsByTagName('i'),构建新的selector, .aa [id='test']
                            调用 jsMatchesSelector,先匹配getElementsByTagName中的元素 其属性是否为[id='test'],然后他的父元素是否有 .aa类

    其它
        构造 match 类方法
        调整 querySelectorAll 方法



 */
define(["../has","../_base/kernel"],function(has,dojo){
"use strict";
//test

var testDiv = document.createElement("div");
var matchesSelector = testDiv.matches || testDiv.webkitMatchesSelector || testDiv.mozMatchesSelector || testDiv.msMatchesSelector || testDiv.oMatchesSelector;  //IE9以下才支持,testDiv.matches('div') 如果每个元素跟css选择器相同,返回ture,否则返回false
var querySelectorAll = testDiv.querySelectorAll;
var unionSplit = /([^\s,](?:"(?:\\.|[^"])+"|'(?:\\.|[^'])+'|[^,])*)/g;
has.add("dom-matches-selector",!!matchesSelector);
has.add("dom-qsa",!!querySelectorAll); 

// this is a simple query engine. It has handles basic selectors,and for simple
// common selectors is extremely fast
var liteEngine = function(selector,root){
	// summary:
	//		A small lightweight query selector engine that implements CSS2.1 selectors
	//		minus pseudo-classes and the sibling combinator,plus CSS3 attribute selectors
    // 当 query('div,p") 以及 IE7及以下,才调用combine方法
	if(combine && selector.indexOf(',') > -1){
		return combine(selector,root);
	}
	// use the root's ownerDocument if provided,otherwise try to use dojo.doc. Note 
	// that we don't use dojo/_base/window's doc to reduce dependencies,and 
	// fallback to plain document if dojo.doc hasn't been defined (by dojo/_base/window).
	// presumably we will have a better way to do this in 2.0
    /*
        如果浏览器提供了node.ownerDocument属性(IE6.0引进,其它未知),doc为拥有这个元素的document对像.
     */
	var doc = root ? root.ownerDocument || root : dojo.doc || document,/*
            针对现在浏览器:可以匹配div#id,div.class,input,但对于 span i等, match 返回空


            div#id
            返回["div.id","div","id"]
            div.class
            返回["div.class",undefined",".","class",undefined]
            query(".red")
            返回 [".red",undefined,"red",undefined]
            query("div#id .red") 查找div#id 的 .red子元素
                querySelectorAl ? null :  div#clock1 .red,clock1,.red,数组的格式是 ['匹配到的字符串','div','id','.','className','element']


         */
		match = (querySelectorAll ? 
			/^([\w]*)#([\w\-]+$)|^(\.)([\w\-\*]+$)|^(\w+$)/ : // 简单的查询, div#id,class, 元素input
            /*
                每一个匹配部分, 都会用于查询的手动过滤
                ^([\w]*)#([\w\-]+)(?:\s+(.*))?$  用于匹配 "div#id .red" 或者 "div#id input"
                (?:^|(>|.+\s+))([\w\-\*]+)(\S*$)    div > ipnut > a:visit  result: ["div > ipnut > a:visited","div > ipnut > ","a",":visited"]
                返回的数组格式是
                ['匹配到的字符串','.class elementName','div>input>','a',":visited']
             */
			/^([\w]*)#([\w\-]+)(?:\s+(.*))?$|(?:^|(>|.+\s+))([\w\-\*]+)(\S*$)/) // this one matches parts of the query that we can use to speed up manual filtering
			.exec(selector);
	root = root || doc;

	if(match){

		// fast path regardless of whether or not querySelectorAll exists
		if(match[2]){

			// an #id
			// use dojo.byId if available as it fixes the id retrieval in IE,note that we can't use the dojo namespace in 2.0,but if there is a conditional module use,we will use that
            // IE 的getElementById bug是, name 和 id不分
			var found = dojo.byId ? dojo.byId(match[2],doc) : doc.getElementById(match[2]);
			if(!found || (match[1] && match[1] != found.tagName.toLowerCase())){
				// if there is a tag qualifer and it doesn't match,no matches
				return [];
			}
			if(root != doc){
				// there is a root element,make sure we are a child of it 如果指定了root, 那么确保root是这个元素的父元素
				var parent = found;
				while(parent != root){
					parent = parent.parentNode;
					if(!parent){
						return [];
					}
				}
			}

            // 对于没有querySelectAll的浏览器, “div#id .red”, 现在只是找到了div#id元素, 还需要在次查找.red元素, 并在找查.red元素时,设置root为div#id;
			return match[3] ?
					liteEngine(match[3],found) 
					: [found];
		}

		if(match[3] && root.getElementsByClassName){
			// a .class   IE8,IE7 不支持

			return root.getElementsByClassName(match[4]);
		}
		var found;

		if(match[5]){
			// a tag   现代化浏览器,直接根据tagName查找,并返回
			found = root.getElementsByTagName(match[5]);

            // IE7及以下的浏览器,选查找到元素,并构造新的选择器字符串
            /*
             #clock\'1 .aa i[id='test'],#clock\'1 .aa,[id='test']
             #clock\'1 .aa [id='test']

             */
			if(match[4] || match[6]){
				selector = (match[4] || "") + match[6];

			}else{
				// that was the entirety of the query,return results
				return found;
			}
		}

	}
	if(querySelectorAll){

		// qSA works strangely on Element-rooted queries
		// We can work around this by specifying an extra ID on the root
		// and working up from there (Thanks to Andrew Dupont for the technique)
		// IE 8 doesn't work on object elements 不支持在<object> 标签调用querySelectorAll  <object width="400" height="400" data="helloworld.swf"></object>

		if (root.nodeType === 1 && root.nodeName.toLowerCase() !== "object"){
			return useRoot(root,selector,root.querySelectorAll);
		}else{
			// we can use the native qSA
            // 应用于document
            // IE8 不能应用此方法 object elements
			return root.querySelectorAll(selector);
		}
	}else if(!found){  // 对于没有querySelectorAll的浏览器
		// search all children and then filter

		found = root.getElementsByTagName("*");
	}
	// now we filter the nodes that were found using the matchesSelector
	var results = [];
	for(var i = 0,l = found.length; i < l; i++){
		var node = found[i];
		if(node.nodeType == 1 && jsMatchesSelector(node,root)){
			// keep the nodes that match the selector
			results.push(node);
		}
	}
	return results;
};
var useRoot = function(context,query,method){

	// this function creates a temporary id so we can do rooted qSA queries,this is taken from sizzle
	var oldContext = context,old = context.getAttribute("id"),nid = old || "__dojo__",hasParent = context.parentNode,relativeHierarchySelector = /^\s*[+~]/.test(query);  // query('+input',#form1); 相对于context的元素

		/*
		 prev + next,返回找到的next元素,next是prev的后一个兄弟元素,prev ~ siblings, 返回prev之后的兄弟元素
		 <label>
		 <input>
		 <span>
		 <input>
		  label + input 返回 第一个input
		  label ~ input 返回 两个input,span不返回

		*/

	if(relativeHierarchySelector && !hasParent){
        //这个表达式应该没有用,因为传递给useRoot不可能为Document对像,只可能是Element, 而Element对像都是有parentNode
		return [];
	}
	if(!old){
		context.setAttribute("id",nid);
	}else{
		nid = nid.replace(/'/g,"\\$&"); // $&引用与 regexp 相匹配的子串, $` '左侧的文本 $' '右侧的文本
	}
	if(relativeHierarchySelector && hasParent){
		context = context.parentNode;
	}
    //对query中的每个元素定义id, query('+ span,~span",context);
	var selectors = query.match(unionSplit);
	for(var i = 0; i < selectors.length; i++){
		selectors[i] = "[id='" + nid + "'] " + selectors[i];
	}
    /*
     <div id="tes't"><span><i></i></span></div>
     那么: query('span i',document.getElementById("test'1")) 会返回 [id='tes\'t'] span i

     在div.parentNode上调用 querySelectorAll("[id='tes\'t'] span i") 查找对应的元素
     如果你在元素上直接调用 div.querySelectorAll('body div span i") 这个也是会返回 i 元素,因为querySelectorAll是从整个文档查询 div span i, 然后在过滤出 div#test't内的 span i.
     useRoot 会让 div.querySelectAll("body div span i") 返回 [],相等于查询 [id='tes\'t'] body div span i
     */
	query = selectors.join(",");

	try{
		return method.call(context,query);
	}finally{
		if(!old){
			oldContext.removeAttribute("id");
		}
	}
};

if(!has("dom-matches-selector")){
	var jsMatchesSelector = (function(){
		// a JS implementation of CSS selector matching,first we start with the various handlers
		var caseFix = testDiv.tagName == "div" ? "toLowerCase" : "toUpperCase";  //xml 或者 xhtml 会保留原始的大小写, 而html中,都是大小的

		var selectorTypes = {
			"": function(tagName){
				tagName = tagName[caseFix]();
				return function(node){
					return node.tagName == tagName;
				};
			},".": function(className){
				var classNameSpaced = ' ' + className + ' ';  //如果要查询,.aa,而有一个元素的类名是 aaSuffix,对过添加左右空格,就能排除这种情况
				return function(node){
					return node.className.indexOf(className) > -1 && (' ' + node.className + ' ').indexOf(classNameSpaced) > -1;
				};
			},"#": function(id){
				return function(node){
					return node.id == id;
				};
			}
		};
		var attrComparators = {
			"^=": function(attrValue,value){
				return attrValue.indexOf(value) == 0;
			},"*=": function(attrValue,value){
				return attrValue.indexOf(value) > -1;
			},"$=": function(attrValue,value){
				return attrValue.substring(attrValue.length - value.length,attrValue.length) == value;
			},"~=": function(attrValue,value){
				return (' ' + attrValue + ' ').indexOf(' ' + value + ' ') > -1;
			},"|=": function(attrValue,value){
				return (attrValue + '-').indexOf(value + '-') == 0;
			},"=": function(attrValue,value){
				return attrValue == value;
			},"": function(attrValue,value){
				return true;
			}
		};
		function attr(name,value,type){
			var firstChar = value.charAt(0);
			if(firstChar == '"' || firstChar == "'"){
				// it is quoted,remove the quotes
				value = value.slice(1,-1);
			}
			value = value.replace(/\\/g,'');
			var comparator = attrComparators[type || ""];
			return function(node){
				var attrValue = node.getAttribute(name);
				return attrValue && comparator(attrValue,value);
			};
		}
		function ancestor(matcher){
			return function(node,root){
				while((node = node.parentNode) != root){
					if(matcher(node,root)){
						return true;
					}
				}
			};
		}
		function parent(matcher){
			return function(node,root){
				node = node.parentNode;
				return matcher ? 
					node != root && matcher(node,root)
					: node == root;
			};
		}
		var cache = {};
		function and(matcher,next){

			return matcher ?
				function(node,root){

					return next(node) && matcher(node,root);
				}
				: next;
		}
		return function(node,root){
			// this returns true or false based on if the node matches the selector (optionally within the given root)
			var matcher = cache[selector]; // check to see if we have created a matcher function for the given selector
			if(!matcher){

				// create a matcher function for the given selector
				// parse the selectors
                /*
                    正则表达式翻译
                    1. 检测字符串中的 >或者空格,并记录到 &1
                    2. 检测字符串中是否有 #或者. ,并记录到 &2, 并记录它们后面的字符 &3 (转义字符,eg. id="first.last", 查找时#first\\.last",其它可包含的字符为 [\w-]
                    3. 检测字符串中是否有 [],并记录  attrName 到 &4,attrType(*=,|=,=,^=)到&5,对于 attrValue有三种类型 "aa",aa,aa ([^\]]) 检测非"]" 结束的字符
                 */
                /*
                    e.g: query("#clock\\'1 .aa i[id='test']")
                    match: #clock\'1 .aa i[id='test'],[id='test']
                    selector: #clock\'1 .aa [id='test']
                    执行 selector.replace
                    1st matcher = function(node){
                             return node.id == id;
                             };
                    2st
                    matcher = ancestor(matcher) // 返回一个函数 function(node,root){},返回的这个函数会调用 1st中的函数

                    3st
                    matcher = function(className){
                 var classNameSpaced = ' ' + className + ' ';  //如果要查询,.aa,对过添加左右空格,就能排除这种情况
                 return function(node){
                 return node.className.indexOf(className) > -1 && (' ' + node.className + ' ').indexOf(classNameSpaced) > -1;
                 };
                 }  && matcher(node,root) // 调用第二步的matcher
                    4st 同2st
                    5st matcher = attr() && matcher (4st)

                    6 当调用 matcher时, 先调用执行 attr() -> matcher (4st) ->  matcher(3st) -> matcher(2st) -> matcher(1st)
                 */

				if(selector.replace(/(?:\s*([> ])\s*)|(#|\.)?((?:\\.|[\w-])+)|\[\s*([\w-]+)\s*(.?=)?\s*("(?:\\.|[^"])+"|'(?:\\.|[^'])+'|(?:\\.|[^\]])*)\s*\]/g,function(t,combinator,type,attrName,attrType,attrValue){

					if(value){

						matcher = and(matcher,selectorTypes[type || ""](value.replace(/\\/g,'')));
					}
					else if(combinator){
						matcher = (combinator == " " ? ancestor : parent)(matcher);
					}
					else if(attrName){
						matcher = and(matcher,attr(attrName,attrValue,attrType));
					}
					return "";
				})){
					throw new Error("Syntax error in query");
				}
				if(!matcher){
					return true;
				}

				cache[selector] = matcher;
			}
			// now run the matcher function on the node

			return matcher(node,root);
		};
	})();
}
/*
    IE7及以下的浏览器,没有定义querySelectorAll方法
    lite 支持的css2 选择器用法 http://dojotoolkit.org/reference-guide/1.10/dojo/query.html
 */

if(!has("dom-qsa")){

	var combine = function(selector,root){
		// combined queries
        //var unionSplit = /([^\s,])*)/g;
        /*
            1. "div,a" 或才 " div,a",[^\s,]以非空格,逗号开头,|[^,]* 当遇到逗号时,停止这次匹配
            2. \\.|[^"] 用来匹配转义后的字符,如"aaa\\"bbbbccc",我要匹配整个字任串时,如果只用[^"]只能匹配到"aaa\\" , 为了提升正则表达式的效率,可以改成以下方式
                "(?: [^"\\]|\\.)+" 因为 正常字符的出现,要比转义出现的概率大, 所以把 [^"\]提前
            3. /([^\s,]([^,])*)/g  但是必须要考虑双引号或者单引号之间有的逗号
            4. 如果遇到双引号,就应当一次性的读取“”之间的内容,内容的形式为 [^"] 和 \\. 转义字符,querySelectAll(".class:test") //错误
            e.g:
                query("div,a");
                query("div[id='my\\:name']")  <div id="my:name"></div>
                query("[data-name='hello,world']")
         */
		var selectors = selector.match(unionSplit);
		var indexed = [];

		// add all results and keep unique ones,this only runs in IE,so we take advantage 
		// of known IE features,particularly sourceIndex which is unique and allows us to 
		// order the results

		for(var i = 0; i < selectors.length; i++){
			selector = new String(selectors[i].replace(/\s*$/,''));

			selector.indexOf = escape; // keep it from recursively entering combine
			var results = liteEngine(selector,root);
			for(var j = 0,l = results.length; j < l; j++){
				var node = results[j];
				indexed[node.sourceIndex] = node;

			}
		}
		// now convert from a sparse array to a dense array 将一个稀疏数组变为稠密数组
		var totalResults = [];
		for(i in indexed){
			totalResults.push(indexed[i]);
		}

		return totalResults;
	};
}

liteEngine.match = matchesSelector ? function(node,root){
	if(root && root.nodeType != 9){
		// doesn't support three args,use rooted id trick
		return useRoot(root,function(query){
			return matchesSelector.call(node,query);
		});
	}
	// we have a native matchesSelector,use that
	return matchesSelector.call(node,selector);
} : jsMatchesSelector; // otherwise use the JS matches impl

return liteEngine;
});

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


我有一个网格,可以根据更大的树结构编辑小块数据.为了更容易知道用户保存了什么,我希望当用户第一次看到网格时,网格处于不可编辑状态.当用户准备好后,他们可以单击编辑按钮,这将使网格的某些部分可编辑.然后,有一个保存或取消按钮可以保存更改或还原.在大多数情况下它是有效的.但
我即将开始开发一款教育性的视频游戏.我已经决定以一种我可以轻松打包为Web,Mobiles和可能的Standalone版本的方式来实现这一目标.我不想使用Flash.因此,我确信(无论如何我会听取建议)使用JavaScript和SVG.我正在对这个问题进行大量研究,但我很难把各个部分放在一起.我知道Raphae
我正在使用带有Grails2.3.9的Dojo1.9.DojoNumberTextBox小部件–我在表单中使用–将固定格式(JavaScript基本格式)的实数值(例如:12.56)设置为HTML表单输入字段(但根据浏览器区域设置显示/编辑它们,所以用户总是看到格式正确的数字).另一方面,Grails期望输入字段根据浏览器
1.引言鉴于个人需求的转变,本系列将记录自学arcgisapiforjavaScript的学习历程,本篇将从最开始的arcgisapiforjavaScript部署开始,个人声明:博文不在传道受业解惑,旨在方便日后复习查阅。由于是自学,文章中可能会出现一些纰漏,请留言指出,不必留有情面哦!2.下载ArcGISforDe
我正在阅读使用dojo’sdeclare进行类创建的语法.描述令人困惑:Thedeclarefunctionisdefinedinthedojo/_base/declaremodule.declareacceptsthreearguments:className,superClass,andproperties.ClassNameTheclassNameargumentrepresentsthenameofthec
我的团队由更多的java人员和JavaScript经验丰富组成.我知道这个问题曾多次被问到,但为了弄清楚我的事实,我需要澄清一些事情,因为我在客户端技术方面的经验非常有限.我们决定使用GWT而不是纯JavaScript框架构建我们的解决方案(假设有更多的Java经验).这些是支持我的决定的事实.>
路由dojo/framework/srcouting/README.mdcommitb682b06ace25eea86d190e56dd81042565b35ed1Dojo应用程序的路由路由FeaturesRoute配置路径参数RouterHistoryManagersHashHistoryStateHistoryMemoryHistoryOutletEventRouterContextInjectionOutl
请原谅我的无知,因为我对jquery并不熟悉.是否有dojo.connect()的等价物?我找到了这个解决方案:http:/hink-robot.com/2009/06/hitch-object-oriented-event-handlers-with-jquery/但是没有断开功能!你知道jquery的其他解决方案吗?有jquery.connect但这个插件在我的测试中不起作用.
与java类一样,在dojo里也可以定义constructor 构造函数,在创建一个实例时可以对需要的属性进行初始化。//定义一个类mqsy_yjvar mqsy_yj=declare(null,{     //thedefaultusername    username: "yanjun",          //theconstructor   
我一直在寻找一些最佳实践,并想知道Dojo是否具有框架特定的最佳实践,还是最好只使用通用的Javascript标准?特别是我主要是寻找一些功能和类评论的指导方针?解决方法:对于初学者来说,这是项目的风格指南:DojoStyleGuide
我有’05/17/2010’的价值我想通过使用dojo.date.locale将其作为“2010年5月17日”.我尝试过使用dojo.date.locale.parse,如下所示:x='05/17/2010'varx=dojo.date.locale.parse(x,{datePattern:"MM/dd/yyyy",selector:"date"});alert(x)这并没有给我所需的日期
我正在尝试创建一个包含函数的dojo类,这些函数又调用此类中的其他函数,如下所示:dojo.provide("my.drawing");dojo.declare("my.drawing",null,{constructor:function(/*Object*/args){dojo.safeMixin(this,args);this.container=args[0];
我知道你可以使用jQuery.noConflict为jQuery做这件事.有没有办法与Dojo做类似的事情?解决方法:我相信你可以.有关在页面上运行多个版本的Dojo,请参阅thispage.它很繁琐,但似乎是你正在寻找的东西.一般来说,Dojo和jQuery都非常小心,不会破坏彼此或其他任何人的变量名.
我有一个EnhancedGrid,用户经常使用复杂的过滤器.有没有办法允许用户保存或标记过滤器,以便将来可以轻松地重新应用它?我知道我可以通过编程方式设置过滤器,但我无法预测用户想要的过滤器.谢谢!编辑:自己做了一些进展…使用grid.getFilter()返回过滤器的JSON表示,然后使用json.strin
我有这个代码:dojo.declare("City",null,{constructor:function(cityid,cityinfo){}});dojo.declare("TPolyline",GPolyline,{constructor:function(points,color){},initialize:function(map){});应该是什
我遇到的问题是我的所有javascript错误似乎来自dojo.xd.js或子模块.我正在使用chrome调试器和许多dijit功能,如dijit.declaration和dojo.parser.这有点烦人,因为它很难找到简单的错误或滑倒.我希望我可以添加一个选项,允许我的调试器在我的非dojo代码中显示选项会发生的位置.我是
我正在使用DojoToolkit数字/解析函数来处理格式化和使用ICU模式语法解析字符串.有没有人知道有可能采取任意ICU模式字符串并以某种方式使用Dojo(或其他)库将其分解为它的部分(例如,对于数字模式,它可以被分解为小数位数,数千个分组等…).我希望这样做,而不需要让我的代码密切了
我有两个看似相关的问题,访问在不同的地方定义的javascript函数.我遇到的第一个问题是调用我在firgbug或safari控制台中定义的函数.我定义了一个名为getRed的函数,如下所示:functiongetRed(row,col){//dosomethingstuffandreturntheredvalueasa
我想添加一个在Ajax调用中指定的外部样式表.我已经找到了一种方法来使用jQuery(参见下面的示例),但是我需要使该方法适应dojoJavaScript框架.JQuery示例$('head').append('<linkrel="stylesheet"type="text/css"href="lightbox_stylesheet.css">');谢谢.解决方法:一旦你
我正在尝试使用dojo.connect将onMouseDown事件连接到图像,如:dojo.connect(dojo.byId("workpic"),"onMouseDown",workpicDown);functionworkpicDown(){alert("mousedown");}类似的代码几行后,我将onMouse*事件连接到dojo.body确实完全正常工作.但是当我点击图像时