📄 query.js
字号:
// the query doesn't contain any spaces, so there's only so many // things it could be if(query.id && !query.hasLoops && !query.tag){ // ID-only query. Easy. return _getElementsFuncCache[query.query] = function(root){ // FIXME: if root != document, check for parenting! return [ d.byId(query.id) ]; } } var filterFunc = getSimpleFilterFunc(query); var retFunc; if(query.tag && query.id && !query.hasLoops){ // we got a filtered ID search (e.g., "h4#thinger") retFunc = function(root){ var te = d.byId(query.id); if(filterFunc(te)){ return [ te ]; } } }else{ var tret; if(!query.hasLoops){ // it's just a plain-ol elements-by-tag-name query from the root retFunc = function(root){ var ret = []; var te, x=0, tret = root.getElementsByTagName(query.tag); while(te=tret[x++]){ ret.push(te); } return ret; } }else{ retFunc = function(root){ var ret = []; var te, x=0, tret = root.getElementsByTagName(query.tag); while(te=tret[x++]){ if(filterFunc(te)){ ret.push(te); } } return ret; } } } return _getElementsFuncCache[query.query] = retFunc; } var _partsCache = {}; //////////////////////////////////////////////////////////////////////// // the query runner //////////////////////////////////////////////////////////////////////// // this is the second level of spliting, from full-length queries (e.g., // "div.foo .bar") into simple query expressions (e.g., ["div.foo", // ".bar"]) var _queryFuncCache = { "*": d.isIE ? function(root){ return root.all; } : function(root){ return root.getElementsByTagName("*"); }, "~": _nextSiblings, "+": function(root){ return _nextSiblings(root, true); }, ">": _childElements }; var getStepQueryFunc = function(query){ // if it's trivial, get a fast-path dispatcher var qparts = getQueryParts(d.trim(query)); // if(query[query.length-1] == ">"){ query += " *"; } if(qparts.length == 1){ var tt = getElementsFunc(qparts[0]); tt.nozip = true; return tt; } // otherwise, break it up and return a runner that iterates over the parts recursively var sqf = function(root){ var localQueryParts = qparts.slice(0); // clone the src arr var candidates; if(localQueryParts[0].oper == ">"){ // FIXME: what if it's + or ~? candidates = [ root ]; // root = document; }else{ candidates = getElementsFunc(localQueryParts.shift())(root); } return filterDown(candidates, localQueryParts); } return sqf; } // a specialized method that implements our primoridal "query optimizer". // This allows us to dispatch queries to the fastest subsystem we can get. var _getQueryFunc = ( // NOTE: // XPath on the Webkit nighlies is slower than it's DOM iteration // for most test cases // FIXME: // we should try to capture some runtime speed data for each query // function to determine on the fly if we should stick w/ the // potentially optimized variant or if we should try something // new. (document["evaluate"] && !d.isSafari) ? function(query){ // has xpath support that's faster than DOM var qparts = query.split(" "); // can we handle it? if( (document["evaluate"])&& (query.indexOf(":") == -1)&& (query.indexOf("+") == -1) // skip direct sibling matches. See line ~344 ){ // dojo.debug(query); // should we handle it? // kind of a lame heuristic, but it works if( // a "div div div" style query ((qparts.length > 2)&&(query.indexOf(">") == -1))|| // or something else with moderate complexity. kinda janky (qparts.length > 3)|| (query.indexOf("[")>=0)|| // or if it's a ".thinger" query ((1 == qparts.length)&&(0 <= query.indexOf("."))) ){ // use get and cache a xpath runner for this selector return getXPathFunc(query); } } // fallthrough return getStepQueryFunc(query); } : getStepQueryFunc ); // uncomment to disable XPath for testing and tuning the DOM path // _getQueryFunc = getStepQueryFunc; // FIXME: we've got problems w/ the NodeList query()/filter() functions if we go XPath for everything // uncomment to disable DOM queries for testing and tuning XPath // _getQueryFunc = getXPathFunc; // this is the primary caching for full-query results. The query dispatcher // functions are generated here and then pickled for hash lookup in the // future var getQueryFunc = function(query){ // return a cached version if one is available var qcz = query.charAt(0); if(d.doc["querySelectorAll"] && ( (!d.isSafari) || (d.isSafari > 3.1) ) && // see #5832 // as per CSS 3, we can't currently start w/ combinator: // http://www.w3.org/TR/css3-selectors/#w3cselgrammar (">+~".indexOf(qcz) == -1) ){ return function(root){ var r = root.querySelectorAll(query); r.nozip = true; // skip expensive duplication checks and just wrap in a NodeList return r; }; } if(_queryFuncCache[query]){ return _queryFuncCache[query]; } if(0 > query.indexOf(",")){ // if it's not a compound query (e.g., ".foo, .bar"), cache and return a dispatcher return _queryFuncCache[query] = _getQueryFunc(query); }else{ // if it's a complex query, break it up into it's constituent parts // and return a dispatcher that will merge the parts when run // var parts = query.split(", "); var parts = query.split(/\s*,\s*/); var tf = function(root){ var pindex = 0; // avoid array alloc for every invocation var ret = []; var tp; while(tp = parts[pindex++]){ ret = ret.concat(_getQueryFunc(tp, tp.indexOf(" "))(root)); } return ret; } // ...cache and return return _queryFuncCache[query] = tf; } } // FIXME: // Dean's Base2 uses a system whereby queries themselves note if // they'll need duplicate filtering. We need to get on that plan!! // attempt to efficiently determine if an item in a list is a dupe, // returning a list of "uniques", hopefully in doucment order var _zipIdx = 0; var _zip = function(arr){ if(arr && arr.nozip){ return d.NodeList._wrap(arr); } var ret = new d.NodeList(); if(!arr){ return ret; } if(arr[0]){ ret.push(arr[0]); } if(arr.length < 2){ return ret; } _zipIdx++; arr[0]["_zipIdx"] = _zipIdx; for(var x=1, te; te = arr[x]; x++){ if(arr[x]["_zipIdx"] != _zipIdx){ ret.push(te); } te["_zipIdx"] = _zipIdx; } // FIXME: should we consider stripping these properties? return ret; } // the main executor d.query = function(/*String*/ query, /*String|DOMNode?*/ root){ // summary: // Returns nodes which match the given CSS3 selector, searching the // entire document by default but optionally taking a node to scope // the search by. Returns an instance of dojo.NodeList. // description: // dojo.query() is the swiss army knife of DOM node manipulation in // Dojo. Much like Prototype's "$$" (bling-bling) function or JQuery's // "$" function, dojo.query provides robust, high-performance // CSS-based node selector support with the option of scoping searches // to a particular sub-tree of a document. // // Supported Selectors: // -------------------- // // dojo.query() supports a rich set of CSS3 selectors, including: // // * class selectors (e.g., `.foo`) // * node type selectors like `span` // * ` ` descendant selectors // * `>` child element selectors // * `#foo` style ID selectors // * `*` universal selector // * `~`, the immediately preceeded-by sibling selector // * `+`, the preceeded-by sibling selector // * attribute queries: // | * `[foo]` attribute presence selector // | * `[foo='bar']` attribute value exact match // | * `[foo~='bar']` attribute value list item match // | * `[foo^='bar']` attribute start match // | * `[foo$='bar']` attribute end match // | * `[foo*='bar']` attribute substring match // * `:first-child`, `:last-child` positional selectors // * `:empty` content emtpy selector // * `:empty` content emtpy selector // * `:nth-child(n)`, `:nth-child(2n+1)` style positional calculations // * `:nth-child(even)`, `:nth-child(odd)` positional selectors // * `:not(...)` negation pseudo selectors // // Any legal combination of these selectors will work with // `dojo.query()`, including compound selectors ("," delimited). // Very complex and useful searches can be constructed with this // palette of selectors and when combined with functions for // maniplation presented by dojo.NodeList, many types of DOM // manipulation operations become very straightforward. // // Unsupported Selectors: // ---------------------- // // While dojo.query handles many CSS3 selectors, some fall outside of // what's resaonable for a programmatic node querying engine to // handle. Currently unsupported selectors include: // // * namespace-differentiated selectors of any form // * all `::` pseduo-element selectors // * certain pseduo-selectors which don't get a lot of day-to-day use: // | * `:root`, `:lang()`, `:target`, `:focus` // * all visual and state selectors: // | * `:root`, `:active`, `:hover`, `:visisted`, `:link`, // `:enabled`, `:disabled`, `:checked` // * `:*-of-type` pseudo selectors // // dojo.query and XML Documents: // ----------------------------- // // `dojo.query` currently only supports searching XML documents // whose tags and attributes are 100% lower-case. This is a known // limitation and will [be addressed soon](http://trac.dojotoolkit.org/ticket/3866) // Non-selector Queries: // --------------------- // // If something other than a String is passed for the query, // `dojo.query` will return a new `dojo.NodeList` constructed from // that parameter alone and all further processing will stop. This // means that if you have a reference to a node or NodeList, you // can quickly construct a new NodeList from the original by // calling `dojo.query(node)` or `dojo.query(list)`. // // query: // The CSS3 expression to match against. For details on the syntax of // CSS3 selectors, see <http://www.w3.org/TR/css3-selectors/#selectors> // root: // A DOMNode (or node id) to scope the search from. Optional. // returns: dojo.NodeList // An instance of `dojo.NodeList`. Many methods are available on // NodeLists for searching, iterating, manipulating, and handling // events on the matched nodes in the returned list. // example: // search the entire document for elements with the class "foo": // | dojo.query(".foo"); // these elements will match: // | <span class="foo"></span> // | <span class="foo bar"></span> // | <p class="thud foo"></p> // example: // search the entire document for elements with the classes "foo" *and* "bar": // | dojo.query(".foo.bar"); // these elements will match: // | <span class="foo bar"></span> // while these will not: // | <span class="foo"></span> // | <p class="thud foo"></p> // example: // find `<span>` elements which are descendants of paragraphs and // which have a "highlighted" class: // | dojo.query("p span.highlighted"); // the innermost span in this fragment matches: // | <p class="foo"> // | <span>... // | <span class="highlighted foo bar">...</span> // | </span> // | </p> // example: // set an "odd" class on all odd table rows inside of the table // `#tabular_data`, using the `>` (direct child) selector to avoid // affecting any nested tables: // | dojo.query("#tabular_data > tbody > tr:nth-child(odd)").addClass("odd"); // example: // remove all elements with the class "error" from the document // and store them in a list: // | var errors = dojo.query(".error").orphan(); // example: // add an onclick handler to every submit button in the document // which causes the form to be sent via Ajax instead: // | dojo.query("input[type='submit']").onclick(function(e){ // | dojo.stopEvent(e); // prevent sending the form // | var btn = e.target; // | dojo.xhrPost({ // | form: btn.form, // | load: function(data){ // | // replace the form with the response // | var div = dojo.doc.createElement("div"); // | dojo.place(div, btn.form, "after"); // | div.innerHTML = data; // | dojo.style(btn.form, "display", "none"); // | } // | }); // | }); // NOTE: elementsById is not currently supported // NOTE: ignores xpath-ish queries for now if(query.constructor == d.NodeList){ return query; } if(!d.isString(query)){ return new d.NodeList(query); // dojo.NodeList } if(d.isString(root)){ root = d.byId(root); } return _zip(getQueryFunc(query)(root||d.doc)); // dojo.NodeList } /* // exposing this was a mistake d.query.attrs = attrs; */ // exposing this because new pseudo matches are only executed through the // DOM query path (never through the xpath optimizing branch) d.query.pseudos = pseudos; // one-off function for filtering a NodeList based on a simple selector d._filterQueryResult = function(nodeList, simpleFilter){ var tnl = new d.NodeList(); var ff = (simpleFilter) ? getFilterFunc(getQueryParts(simpleFilter)[0]) : function(){ return true; }; for(var x=0, te; te = nodeList[x]; x++){ if(ff(te)){ tnl.push(te); } } return tnl; }})();}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -