我自行开发、引以为豪的CSS3选择器,jQuery能支持的选择器基本都支持了,除了那个:has伪类外。由于选择器种类繁多,也不太好演示,因此请下载文档回来看吧。
用法基本与jQuery一样,下面是一个简单示例:
dom.ready(function(){
dom(".sample :hidden").each(function(){
if(this.type){
dom.console.log(this.tagName+" "+this.type)
}else{
dom.console.log(this.tagName)
}
});
});
源码:
/*dom Framework version 1.0
Copyright 2010
Dual licensed under the MIT or GPL Version 2 licenses.
author: <ruby> <rb>司徒正美<rp>(zhongqincheng)</rp></rb><rt>しとぅなさみ</rt></ruby>
http://www.cnblogs.com/rubylouvre/
*/
//=========================================
// 选择器模块
//==========================================
;;;(function(dom,window,undefined){
dom.provide("query");
dom.lib.quickTag = false;
dom.lib.isNot = false;
dom.lib.pageOrder = ("sourceIndex" in document.documentElement) ? function (a, b) {
return (a.sourceIndex - b.sourceIndex);
}:function (a, b) {
return (3 - (a.compareDocumentPosition(b) & 6));
};
var getUIDXML = function(node){
var uid = node.getAttribute(dom.expando);
if (!uid){
uid = dom.uuid++
node.setAttribute(dom.expando, uid);
}
return uid;
};
var getUIDHTML = function(node){
return node.uniqueNumber || (node.uniqueNumber = dom.uuid++);
};
//用于获取选择器的类型
var types_ = {
"#":"id", //ID选择器
".":"class", //类选择器
/*"tag"; //标签选择器*/
"[":"attribute", //属性选择器
" ":"descendant",//关系选择器(后代选择器)
">":"child", //关系选择器(亲子选择器)
"+":"adjacent", //关系选择器(相邻选择器)
"~":"general", //关系选择器(兄长选择器)
":":"pseudo", //伪类选择器
",":"combine", //联合选择器
"*":"wildcard" //通配符选择器
}
//用于移除相应的选择器
var regexes_ = {
id: /#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/, //ID选择器
tag: /^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/, //标签选择器
attribute: /\[((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)(['"]*)(.*?)\3|)\]/, //属性选择器
"class": /\.((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,//类选择器
pseudo: /:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/, //伪类选择器
combine: /,/, //联合选择器
child:/^(\>)\s*(\w*|\*)/, //亲子选择器
adjacent:/^(\+)\s*(\w*|\*)/, //相邻选择器
general:/^(\~)\s*(\w*|\*)/, //兄长选择器
descendant: /\s+(\.((?:[\w\u00c0-\uFFFF_-]|\\.)+))?/, //后代选择器
wildcard :/\*/ //通配符选择器
};
var queryPseudoHasExp = function(start_,next_,noCheck_){
return {
curry :function(lastResult,flag,a ,b){
//将外围变量变成本地变量
var start = start_,next = next_, isAll = noCheck_,
result = [],ri = 0, uniqResult = {},
i = 0, n = lastResult.length, el, uid, find;
while (i < n) {
el = lastResult[i++];
uid = flag.uuid(el),
find = uniqResult[uid];
if (find === void 0) {
for (var c = 0, node = el.parentNode[start], nodeName = el.nodeName;node; node = node[next])
if (node.nodeType === 1 && (isAll || nodeName === node.nodeName)) {
++c;
uniqResult[flag.uuid(node)] = a === 0 ? c === b : (c - b) % a === 0 && (c - b) / a >= 0;
}
find = uniqResult[uid];
}
if (find ^ dom.lib.isNot)
result[ri++] = el;
}
return result;
}
}
}
var queryPseudoNoExp = function(_direction,_noCheck){
return {
curry : function(lastResult){
var result = [],ri = 0,els = lastResult,direction = _direction,isAll = _noCheck;
for (var i = 0, el; el = els[i]; i++) {
var tagName = isAll || el.nodeName, find = null
if (find === null && direction <= 0){
for (var node = el.previousSibling; node; node = node.previousSibling)
if (node.nodeType === 1 && (isAll || node.nodeName === tagName)) {
find = false;
break;
}
}
if (find === null && direction >= 0)
for (var node = el.nextSibling; node; node = node.nextSibling)
if (node.nodeType === 1 && (isAll || node.nodeName === tagName)) {
find = false;
break;
}
if (find === null)//如果本身就是first-child或last-child
find = true;
if (find ^ dom.lib.isNot)//参与运算的两个值,如果两个相应bit位相同,则结果为0,否则为1。
result[ri++] = el;
}
return result;
}
}
}
var filters = { //伪类选择器的过滤器
enabled: function(el){//标准
return el.disabled === false && el.type !== "hidden";
},
disabled: function(el){//标准
return el.disabled === true;
},
checked: function(el){//标准
return el.checked === true;
},
indeterminate:function(el){//标准
return el.indeterminate = true && el.type === "checkbox"
},
selected: function(el){
el.parentNode.selectedIndex;
return el.selected === true;
},
empty: function (el) {//标准
return !el.firstChild;
},
lang: function (el, value) {//标准
var reg = new RegExp("^" + value, "i")
while (el && !el.getAttribute("lang"))
el = el.parentNode;
return !!(el && reg.test(el.getAttribute("lang")));
},
header: function(el){
return /h\d/i.test( el.nodeName );
},
button: function(el){
return "button" === el.type || el.nodeName === "BUTTON";
},
input: function(el){
return /input|select|textarea|button/i.test(el.nodeName);
},
hidden : function( el ) {
dom.require("node");
return el.type === "hidden" || dom.getStyle(el,"display") === "none"
},
visible : function( el ) {
dom.require("node");
return el.type !== "hidden" && dom.getStyle(el,"display") !== "none"
},
link:function(el){
return el.nodeName.toLowerCase() === "a";
},
root:function(el,exp,context){//标准
return el === context.documentElement;
},
target:function(el,exp,context){//标准
var id = context.location.hash.slice(1);
return (el.id || el.name) === id;
},
parent : function( el ) {
return !!el.firstChild;
},
contains: function(el, exp) {
return (el.textContent||el.innerText||'').indexOf(exp) !== -1
},
"first-child": queryPseudoNoExp(-1, true),//标准
"last-child": queryPseudoNoExp( 1, true),//标准
"only-child": queryPseudoNoExp( 0, true),//标准
"first-of-type": queryPseudoNoExp(-1, false),//标准
"last-of-type": queryPseudoNoExp( 1, false),//标准
"only-of-type": queryPseudoNoExp( 0 ,false),//标准
"nth-child": queryPseudoHasExp("firstChild", "nextSibling", true),//标准
"nth-last-child": queryPseudoHasExp("lastChild", "previousSibling", true),//标准
"nth-of-type": queryPseudoHasExp("firstChild", "nextSibling", false),//标准
"nth-last-of-type": queryPseudoHasExp("lastChild", "previousSibling", false),//标准
//与位置相关的过滤器
first: function(index){
return index === 0;
},
last: function(index, num){
return index === num;
},
even: function(index){
return index % 2 === 0;
},
odd: function(index){
return index % 2 === 1;
},
lt: function(index, num){
return index < num;
},
gt: function(index, num){
return index > num;
},
eq: function(index, num){
return index === num;
}
};
dom.each(String("text radio checkbox file password submit image reset").match(/\w+/g),
function(name){
filters[name] = function(el){
return el.type === name
}
});
var parseNth = function (exp) {
var match = /(-?)(\d*)n([-+]?\d*)/.exec(exp === "even" && "2n" || exp === "odd" && "2n+1" || !/\D/.test(exp) && "0n+" + exp || exp);
return {
a: (match[1] + (match[2] || 1)) - 0,
b: match[3] - 0
};
};
var _position = dom.oneObject(String("eq gt lt first last even odd").match(/\w+/g));
//http://www.cnblogs.com/rubylouvre/archive/2009/11/25/1610044.html
dom.queryId = function (id, context) {
var el = (context || document).getElementById(id);
return el && [el] || []
};
if(dom.env.mixupsName){
//如果浏览器的getElementById不能区分name与id
dom.queryId = function(id,root){
root = root || document;
var el = root.getElementById(id);
if (el && el.attributes['id'].value === id) {
return [el]
} else {
var all = root.all[id];
for(var i=0;el=all[i++];){
if(el.attributes['id'].value === id)
return [el]
}
return []
}
}
}
var queryWildcard = function (root) {//获得页面上或者某个元素节点下的所有元素
var all = root.all || root.getElementsByTagName("*"),result;
if(dom.ie){
result = dom.filter(all,function(el){
return el.nodeType === 1
});
if(dom.env.traverseAllElements)
dom.merge(result,(root.getElementsByTagName("param") || []))
return result;
}else{
return dom.slice(all);
}
};
var queryTag = function(tagName, lastResult, tools){
var result = [],ri = 0, n = lastResult.length, uniqResult = {},nodes;
if(dom.lib.isNot){
var all = n ? lastResult :queryWildcard(tools.context);
return dom.filter(all,function(el){
return el.tagName !== tagName;
});
}
if (n === 0) {
return dom.slice(tools.context.getElementsByTagName(tagName))
} else {
var i = 0 ,j ,jn ,node ,uid ;
if(dom.lib.quickTag ){
dom.lib.quickTag = false;
while(i < n){
nodes = lastResult[i++].getElementsByTagName(tagName);
j = 0;
jn = nodes.length;
while(j < jn){
node = nodes[j++];
uid = tools.uuid(node);
if(uniqResult[uid] !== node){
result[ri++] = uniqResult[uid] = node;
}
}
}
return result;
}else{
while(i < n){
node = lastResult[i++];
if(node.nodeName === tagName )
result[ri++] = node;
}
return result;
}
}
}
var queryClass = function (className, lastResult, tools) {
var result = [],ri = 0,i = 0, n = lastResult.length, el,
reg = new RegExp('(?:^|[ \\t\\r\\n\\f])' + className + '(?:$|[ \\t\\r\\n\\f])');
if (n === 0) {//查找模式
if (tools.byClass) {
return Array.prototype.slice.call(tools.context.getElementsByClassName(className));
} else {
var els = tools.context.all || tools.context.getElementsByTagName("*");
for(i=0;el=els[i++];){
if(reg.test(el.className || el.getAttribute("class")))
result[ri++] = el;
}
return result;
}
} else {//过滤模式
for(i=0;el=lastResult[i++];){
if(reg.test(el.className || el.getAttribute("class")) ^ dom.lib.isNot)
result[ri++] = el;
}
return result;
}
}
var queryDescendant = function(className,lastResult,tools){//后代选择器
var result = [],ri = 0, isAll = !className,//如果为空字符串或undefined
uniqResult = {}, i = 0 ,n = lastResult.length, j ,jn ,
nodes, node,el, reg, uid;
if(!isAll){
reg = new RegExp('(?:^|[ \\t\\r\\n\\f])' + className + '(?:$|[ \\t\\r\\n\\f])');
}
lastResult = lastResult.sort( dom.lib.pageOrder );
//className二级查找模式,利用原生的getElementsByClassName实现高速化
if(!isAll && tools.byClass){
while(i < n){
nodes = lastResult[i++].getElementsByClassName(className);
j = 0;
jn = nodes.length;
while(j < jn){
node = nodes[j++];
uid = tools.uuid(node)
if(uniqResult[uid] !== node){
result[ri++] = uniqResult[uid] = node;
}
}
}
}else{
while(i < n){
el = lastResult[i++];
if(uniqResult[tools.uuid(el)] !== el){
nodes = el.all || el.getElementsByTagName("*");
j = 0;
jn = nodes.length;
//className二级查找模式
while(j < jn){
node = nodes[j++];
if(uniqResult[tools.uuid(node)] === node) break;
if(node.nodeType === 1 && (isAll || reg.test(node.className || node.getAttribute("class")))){
uid = tools.uuid(node);
if(uniqResult[uid] === node){
break;
}else{
result[ri++] = uniqResult[uid] = node;
}
}
}
}
}
}
return result;
};
var queryChild = function (nodeName,lastResult,tools) {//亲子选择器
var isAll = nodeName === "*",result = [],ri = 0,
n = lastResult.length,i = 0 ,nodes ,node,j , jn,
prop = tools.children ? "children" : "childNodes";
while(i < n){
nodes = lastResult[i++][prop];
j = 0;
jn = nodes.length;
while(j < jn){
node = nodes[j++];
if( ( tools.children || node.nodeType === 1) &&(isAll || nodeName === node.nodeName))
result[ri++] = node;
}
}
return result;
};
var queryGeneral = function (nodeName,lastResult,tools) {//兄长选择器
var isAll = nodeName === "*",result = [],ri = 0,
n = lastResult.length,i = 0 ,uniqResult = {}, uid, node,
prop = tools.element ? "nextElementSibling" :"nextSibling"
while(i < n){
node = lastResult[i++];
if(uniqResult[tools.uuid(node)] !== node){
for(node = node[prop];node;node = node[prop]){
if(uniqResult[node.uuid] === node) break;
if(( tools.element || node.nodeType === 1) && (isAll || nodeName === node.nodeName)){
uid = tools.uuid(node)
if(uniqResult[uid]){
break;
}else{
result[ri++] = uniqResult[uid] = node;
}
}
}
}
}
return result;
};
var queryAdjacent = function (nodeName,lastResult,tools) { //相邻选择器
var isAll = nodeName === "*", result = [],ri = 0,
n = lastResult.length,i = 0 ,node,
prop = tools.element ? "nextElementSibling" :"nextSibling";
while(i < n){
node = lastResult[i++];
for(node = node[prop];node;node = node[prop]){
if(tools.element || node.nodeType === 1){
if (isAll || nodeName === node.nodeName)
result[ri++] = node;
break;
}
}
}
return result;
};
var queryAttribute = function (name,operator,value,lastResult, root) { //属性选择器
if(lastResult.length === 0)
lastResult = queryWildcard(root);
var result = [],ri = 0,reg;
switch (operator) {
case '$=':reg = new RegExp( value + '$' );break ;
case '~=':reg = new RegExp( '(?:^|[ \\t\\r\\n\\f])' + value + '(?:$|[ \\t\\r\\n\\f])');break;
case '|=':reg = new RegExp( '(?:^|\\|)' + value + '(?:$|\\|)');break;
}
var el , i = 0 ,n = lastResult.length,attrib,flag
while(i < n){
el = lastResult[i++];
attrib = dom.attr(el, name);//取得元素的实际属性值
flag = (attrib != null) && (attrib !== "");
if(flag && operator)
switch (operator) {
case '=': flag = attrib === value ;break ;
case '!=': flag = attrib !== value ;break ;
case '^=': flag = attrib.indexOf(value) === 0 ;break ;
case '*=': flag = attrib.indexOf(value) !== -1 ;break ;
default : flag = reg.test(attrib);break;
}
if (!!flag ^ dom.lib.isNot)
result[ri++] = el;
}
return result;
}
var queryPseudo = function (type, exp, lastResult,tools){//伪类选择器
var position = _position
if(lastResult.length === 0 )
lastResult = queryWildcard(tools.context);
var filter = filters[type], i = 0, n = lastResult.length, result = [],ri = 0, el;
if(position[type]){//处理位置伪类
//如果exp为空白则将集合的最大索引值传进去,否则将exp转换为数字
exp = (exp === ""|| exp === void 0) ? n - 1 : ~~exp;
while(i < n){
el = lastResult[i];
if(filter(i++, exp) ^ dom.lib.isNot)
result[ri++] = el;
}
return result;
}
if((typeof filter === "object") && filter.curry){
var p = parseNth(exp);
return filter.curry(lastResult, tools, p.a, p.b);
}
//处理target root checked disabled empty enabled lang 等伪类
while(i < n){
el = lastResult[i++];
if(filter(el, exp, tools.context) ^ dom.lib.isNot)
result[ri++] = el;
}
return result;
};
//适配器,根据选择器的类型调用相应的函数去处理
var dispatcher_ = {
wildcard:function(array, nodes, tools) { //通配符选择器
return dom.lib.isNot ? [] :queryWildcard(tools.context);
},
tag : function(array, nodes, tools){ //标签选择器
var nodeName = tools.xml ? array[0] :array[0].toUpperCase();
return queryTag(nodeName , nodes, tools);
},
id : function(array, nodes, tools){ //ID选择器
var id = array[1];
return dom.lib.isNot ? queryAttribute("id","=",id, nodes ,tools.context)
:dom.queryId(id, tools.context);
},
"class":function(array, nodes, tools){ //类选择器
var className = array[1];
return dom.lib.isNot ? queryAttribute("class","~=",className,nodes, tools.context):
queryClass(className, nodes, tools);
},
descendant:function(array, nodes, tools){ //后代选择器(第一个参数为className)
return queryDescendant(array[2], nodes, tools);
},
child:function(array, nodes,tools){ //亲子选择器(第一个参数为nodeName)
var nodeName = array[2] || "*"
nodeName = tools.xml ? nodeName :nodeName.toUpperCase();
return queryChild(nodeName,nodes,tools);
},
general:function(array, nodes,tools){ //兄长选择器(第一个参数为nodeName)
var nodeName = array[2] || "*"
nodeName = tools.xml ? nodeName :nodeName.toUpperCase();
return queryGeneral(nodeName,nodes,tools);
},
adjacent:function(array, nodes,tools){ //相邻选择器(第一个参数为nodeName)
var nodeName = array[2] || "*"
nodeName = tools.xml ? nodeName :nodeName.toUpperCase();
return queryAdjacent(nodeName,nodes,tools)
},
attribute:function(array, nodes, tools){ //属性选择器
return queryAttribute(array[1], array[2], array[4], nodes, tools.context);
},
pseudo:function(array, nodes,tools){ //伪类选择器
return queryPseudo(array[1], array[3], nodes, tools);
}
}
var isRelationAndTag_ = dom.oneObject(String("adjacent general child descendant tag").match(/\w+/g));
var removeBlank_ = /\s*([>,\+\~=])\s*(?=(?:(?:[^"']*"[^"']*){2}|(?:[^"']*'[^"']*){2})*[^"']*$)/g;
dom.query = function (selectors, context, lastResult) {
var result = [],
types = types_,//引用外围变量
regexes = regexes_,//引用外围变量
dispatcher = dispatcher_,//引用外围变量
removeBlank = removeBlank_,
root = context.documentElement,
//若关系选择器与tag选择器跟着ID选择器,则可以直接kill掉前面的
isRelationAndTag = isRelationAndTag_,
xml = dom.isXML(context),
tools = {//用于装载当前文档的各种判定
xml : xml,
children : "children" in root,
context : context,
//http://www.w3.org/TR/ElementTraversal/
element : "nextElementSibling" in root,
byClass : "getElementsByClassName" in root,
uuid: xml ? getUIDXML : getUIDHTML
};
selectors = selectors.replace(removeBlank, function ($1, $2) {
return $2;
});
selectors = selectors.replace(/^\s+|\s+$/g, '');
if("querySelectorAll" in context){
try{
var els = context.querySelectorAll(selectors);
if(dom.env.sliceNodes){
return Array.prototype.slice.call(els);
}else{
return dom.filter(els,function(el){
return el.nodeType === 1
});
}
}catch(e){ }
}
while (selectors.length) {
var type = types[selectors.charAt(0)] || "tag",
regex = regexes[type],
array = selectors.match(regex),//使用match才有rightContext,exec没有
segment = RegExp.lastMatch;
selectors = RegExp.rightContext;
//后代选择器跟紧标签选择器的情形进行优化,如p span或#id span 或.class span
//属于高级查找 var els = el.getElementsByTagName(tag)
if(lastResult.length && type === "descendant"){
if(/[a-z0-9A-Z_]/.test(selectors.charAt(0))){//处理div div的情形
dom.lib.quickTag = true;
continue;
}
if(selectors.charAt(0) === "*"){//处理div *的情况
selectors = selectors.slice(1)
}
}
//如果是并联选择器,那么就把上次的结果集放进最终结果集中
if(type === "combine") {
result = result.concat(lastResult);
lastResult = [];
continue;
} else if(isRelationAndTag[type] && selectors.charAt(0) === "#"){
continue;
}else if(segment.slice(0,4) === ":not"){
var add = segment.match(regexes.pseudo)[3] || "";
selectors = add + ":yes()"+ selectors ;//自定义一个伪类,标明反选选择器的结束
dom.lib.isNot = true; //使用反选选择器,反选选择器的括号里可以含有以下选择器
continue; //id,class,attribute,pseudo(not除外)
}else if(segment.slice(0,4) === ":yes"){
dom.lib.isNot = false;
continue;
}
lastResult = dispatcher[type](array, lastResult, tools );
//如果结果集的长度为零,则中止循环
if (lastResult === undefined || lastResult.length === 0) {
break;
}
}
//返回最终结果
return result.concat(lastResult);
}
})(window[escape(document.URL.split("#")[0])],this);
下载回来后对着文件点右键-->属性-->解除锁定。
如果大家对上述方法有什么更好的实现,请不吝赐救!