巴扎黑2017-04-10 14:39:25
首先,我要说没必要来实现选择器这种轮子,虽然我不反对造轮子,但选择器这种轮子已经非常成熟了,而且现代浏览器已经内置了选择器。
不过如果你坚持还是要继续的话,我可以简单探讨下。顺便说一下,jquery的选择器用的是sizzle,它以前用的是自己写的,不过后来大概也觉得这个轮子没啥意思。。。
首先要把你的查询字符串解析成查询链,这个过程简单但是繁杂,因为除了常见的css选择器,还有各种伪类。我们就拿最简单的一个查询来举例子把,我把我们的选择器叫做X
X('#header .nav ul');
这段代码经过我们的解析后会变成类似这样的结构
[
{type: 'id', value: 'header'},
{type: 'class', value: 'nav'},
{type: 'tag', value: 'ul'}
]
OK,这样一个简单的查询链就出来了,理论上我们按照这个顺序一步一步就可以得到期望的结果了。
注意,如果你没有用querySelector
这个函数,那么基本上就是利用 getElementById
, getElementsByName
, getElementsByTagName
, getElementsByClassName
这几个函数来实现了
我们可以把上面查询链的type给映射到具体的操作,类似
var handlers = {
'id' : function (el, value) {
return el.getElementById(value);
},
...
};
最后,我们遍历这条查询链,根据每个节点的type来查询value,然后把每个节点结果作为下一个查询的el
,依此类推
注意,以上只是做一个选择器的基本原理,实际情况要复杂的多
PHPz2017-04-10 14:39:25
以前不知道瀏覽器有 querySelector,所以自己寫了一個,不過功能不全。
額外的好處是修改一下也可以用來創建元素(類似 Emmet)。
效率肯定不如瀏覽器自帶的。
function $(x) {
var s = (function(t) {
var s = { tagName: "", id: "", classes: [], attributes: [] }, p = [], i, j;
var m = {
"]" : "["
}, n = false;
for (i = t.length - 1, j = t[i]; i >= 0; j = t[--i]) {
if (!n) {
switch (j) {
case ".":
s.classes.push(p.join(""));
p = [];
break;
case "#":
s.id = p.join("");
p = [];
break;
case "]":
n = true;
p = [];
break;
default:
p.unshift(j);
}
} else {
switch (j) {
case "[":
s.attributes.push(p.join(""));
p = [];
n = false;
break;
default:
p.unshift(j);
}
}
}
s.tagName = (p.join(""));
//alert(s.tagName + ", #" + s.id + ", ." + s.classes.join(", ."));
return s;
}(x));
var y = [];
if (s.id)
y = [document
.getElementById(s.id)]
.filter(function(v) {
if (s.tagName)
return v.tagName === s.tagName.toUpperCase();
return true;
});
else if (s.tagName)
y = (Array.prototype.slice.call(document
.getElementsByTagName(s.tagName)));
else if (s.classes.length > 0 || s.attributes.length > 0)
if (!y || !y.length > 0)
y = Array.prototype.slice.call(document
.getElementsByTagName('*'));
if (s.classes.length > 0)
y = y.filter(function(v) {
var c = v.className.split(" ");
return s.classes.every(function(n) {
return c.indexOf(n) !== -1;
});
});
if (s.attributes.length > 0)
y = y.filter(function(v) {
return s.attributes.every(function(n) {
n = n.split("=");
var attributeName = n[0].trim();
if (v.hasAttribute(attributeName))
if (n.length === 1)
return true;
else {
return v.getAttribute(attributeName) === n[1].trim().replace(/^"|"$/g, '');
}
return false;
});
});
return y;
}