Dojo 的css 2.0 lite 选择器源代码

来源:互联网 发布:守望者罗夏的面具淘宝 编辑:程序博客网 时间:2024/06/05 18:40

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";//testvar testDiv = document.createElement("div");var matchesSelector = testDiv.matches || testDiv.webkitMatchesSelector || testDiv.mozMatchesSelector || testDiv.msMatchesSelector || testDiv.oMatchesSelector;  //IE9以下才支持,testDiv.matches('div') 如果每个元素跟css选择器相同,返回ture, 否则返回falsevar 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 fastvar 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", "div", undefined", ".", "class", undefined]            query(".red")            返回 [".red", undefined, undefined, ".", "red", undefined]            query("div#id .red") 查找div#id 的 .red子元素                querySelectorAl ? null :  div#clock1 .red,div,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", undefined, undefined, undefined, "div > ipnut > ", "a", ":visited"]                返回的数组格式是                ['匹配到的字符串', 'div', 'id', '.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 existsif(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 matchesreturn [];}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 ,i,[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 resultsreturn 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 elementsreturn root.querySelectorAll(selector);}}else if(!found){  // 对于没有querySelectorAll的浏览器// search all children and then filterfound = root.getElementsByTagName("*");}// now we filter the nodes that were found using the matchesSelectorvar results = [];for(var i = 0, l = found.length; i < l; i++){var node = found[i];if(node.nodeType == 1 && jsMatchesSelector(node, selector, root)){// keep the nodes that match the selectorresults.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 sizzlevar 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对像都是有parentNodereturn [];}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 handlersvar 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 quotesvalue = 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, selector, 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 selectorif(!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'],,,,#clock\'1 .aa ,i,[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, 而有一个元素的类名是 aaSuffix, 对过添加左右空格,就能排除这种情况                 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, value, 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 nodereturn 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 resultsfor(var i = 0; i < selectors.length; i++){selector = new String(selectors[i].replace(/\s*$/,''));selector.indexOf = escape; // keep it from recursively entering combinevar 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, selector, root){if(root && root.nodeType != 9){// doesn't support three args, use rooted id trickreturn useRoot(root, selector, function(query){return matchesSelector.call(node, query);});}// we have a native matchesSelector, use thatreturn matchesSelector.call(node, selector);} : jsMatchesSelector; // otherwise use the JS matches implreturn liteEngine;});


0 0
原创粉丝点击