自己写的节点选择器,求高手优化

zh19881213 2012-02-29 12:53:36
源代码:


var isSelect = function(p/* path */, r/* root */) {

//定义变量
var position = null;
var first = null;
var last = null;
var parentNode = null;

//定义正则
var queueRegExp = null;
var indexRegExp = /(\/\/|\/)(\w+|\*)(?:\[(.*?)\])?(?:\/(\.+))?/g;
var replaceRegExp = /(@|\b)(.+?)(?: ?)($|\)|\&|\|)/g;
var replaceAttrRegExp = /(\w+)(\!\=|<\=|\=>|\=|<|>)?([^$]+)?/;
var replaceNumberRegExp = /^(?:\s+)?(\d+)(?:\s+)?$/;
var replaceSymbolRegExp = /\=>|\=/g;
var replacePositionRegExp = /position/g;
var replaceFirstRegExp = /first/g;
var replaceLastRegExp = /last/g;

//定义集合
var queue = [];
var index = [];

//定义函数
var queueSelect = function(e/* element */, i/* iterative */, p/* path */) {

//判断迭代
if(index.length <= i) {

//判断路径
if(queueRegExp.test(p)) {

//定义差异路径
var _p = p.match(queueRegExp);

//定义向上索引
var _i = index.slice(-1)[0].$4.length;

//遍历迭代索引
for(var i = index.length - 1, j = e; 0 <= i; i--, j = j.parentNode) {

if(index[i].$3 && !eval(index[i].$3.replace(replaceRegExp, function($1, $2, $3, $4) {return replaceSelect(j, index[i].$2, $2, $3, $4)}))) {
return;
}

if(index[i].$1 === '//' && i > 0) {
for(var k = _p.pop().split('/').length; 0 < k; k--) {
j = j.parentNode;
}
}

}

//判断向上索引
if(_i) {

for(var i = _i; 0 < i; i--) {
e = e.parentNode;
}

for(var i = queue.length - 1; 0 <= i; i--) {
if(queue[i] === e) {
return;
}
}

}

//添加队列元素
queue.push(e);

}

}

};

var indexSelect = function(e/* element */, i/* iterative */, p/* path */) {

//参数赋值
i++;
p += '/' + e.tagName;

if(e.nodeType === 1) {

//队列选择
queueSelect(e, i, p);

//遍历元素
for(var j = e.firstChild; j; j = j.nextSibling) {
indexSelect(j, i, p);
}

}
};

var replaceSelect = function(e/* element */, t/* tag */, $1, $2, $3) {

//判断行为
if($1 === '@') {

//判断格式
if(replaceAttrRegExp.test($2)) {

//定义属性值
var _$1 = e.getAttribute(RegExp.$1);

//判断属性值
if(_$1 !== null) {

//定义比较值
var _$3 = parseFloat(RegExp.$3) || RegExp.$3;

//判断比较值
if(typeof _$3 === 'number') {

_$1 = parseFloat(_$1);

switch(RegExp.$2) {
case '=' : return (_$1 == _$3) + $3;
case '<' : return (_$1 < _$3) + $3;
case '>' : return (_$1 > _$3) + $3;
case '!=' : return (_$1 != _$3) + $3;
case '<=' : return (_$1 <= _$3) + $3;
case '=>' : return (_$1 >= _$3) + $3;
}

} else {

_$1 = _$1.toLowerCase();

switch(RegExp.$2) {
case '=' : return (_$1 == _$3 ) + $3;
case '<' : return (_$1 != _$3 && _$3.indexOf(_$1) > -1) + $3;
case '>' : return (_$1 != _$3 && _$1.indexOf(_$3) > -1) + $3;
case '!=' : return (_$1 != _$3 ) + $3;
case '<=' : return (_$1 == _$3 || _$3.indexOf(_$1) > -1) + $3;
case '=>' : return (_$1 == _$3 || _$1.indexOf(_$3) > -1) + $3;
}

}

} else {
return false + $3;
}

} else {
return false + $3;
}

} else {

//判断赋值
if(e.parentNode === parentNode) {
position++;
} else {
position = 0;
first = 0;
last = -1;
parentNode = e.parentNode;
}

//判断格式
if(replaceNumberRegExp.test($2)) {
return (RegExp.$1 == position) + $3;
} else {

//计算表达式
$2 = eval($2
.replace(
replacePositionRegExp,
position
)
.replace(
replaceFirstRegExp,
first
)
.replace(
replaceLastRegExp,
function() {
if(last < 0) {
if(e === r) {
last = 0;
} else {
for(var i = parentNode.firstChild; i; i = i.nextSibling) {
if(i.nodeType === 1 && (i.tagName.toLowerCase() === t || t === '*')) {
last++;
}
}
}
}
return last;
}
)
.replace(
replaceSymbolRegExp,
function($) {
return $ === '=' ? '==' : '>=';
}
)
);

//转换表达式
if(typeof $2 === 'number') {
$2 = $2 == position;
}

return $2 + $3;
}

}

};

//索引赋值
while(indexRegExp.test(p.toLowerCase())) {

index.push({
$1 : RegExp.$1,
$2 : RegExp.$2,
$3 : RegExp.$3,
$4 : RegExp.$4
});

queueRegExp = (queueRegExp || '') + (RegExp.$1 === '//' ? '\\/(.*?)\\/' : '\\/') + (RegExp.$2 === '*' ? '\\w+' : RegExp.$2);

}

//正则赋值
queueRegExp = new RegExp('^' + queueRegExp + '$', 'i');

//选择索引
indexSelect(r, 0, '');

//返回队列
return queue;

};



性能测试:
测试页中插入了 5000 多节点,参数筛选条件有 3 个(就是下面的例子),各浏览器的表现情况如下:
Internet Explorer 6(IETester) => 188 毫秒
Internet Explorer 8 => 157 毫秒
Firefox => 29 毫秒
Safari => 18 毫秒
Opera => 48 毫秒
Chrome => 35 毫秒
没有研究过别的选择器,也不知道这个测试结果相对来说是快还是慢,希望大牛们看看有什么地方可以优化的么。

应用示例:


var $ = function() {

var d = new Date();
var q = isSelect('//input[@type=button && @value=测试 && position = 1]', document.body);

alert(new Date() - d);

};



语法说明:

函数接收两个参数,而且都是必须的,用的时候可以写一个包装方法。

第一个参数是路径(参照 xpath 语法的规范),第二个参数是搜索的容器节点。
...全文
232 25 打赏 收藏 转发到动态 举报
写回复
用AI写文章
25 条回复
切换为时间正序
请发表友善的回复…
发表回复
zh19881213 2012-03-01
  • 打赏
  • 举报
回复
发现有个逻辑错误,正在修改,感觉速度上还能在快 1/3
zh19881213 2012-03-01
  • 打赏
  • 举报
回复
我的选择器参考了 xpath 的语法,但是又结合了我自己一些思路,虽然像 xpath 但又不一样。

用的时候可以自己定义一些包装函数,比如可以叫包装函数实现一些定式,比如:.id #class 之类的。
daols 2012-02-29
  • 打赏
  • 举报
回复
opera9开始就支持xpath的...可以直接写...
楼主这个可以放到firefox里让ff支持xpath选择器了...
重新封装一下, 能完全兼容opera的xpath语法就好了..
那样就可以把用到xpath的opera-ujs无缝移植到ff的gm上了..
daols 2012-02-29
  • 打赏
  • 举报
回复
opera9开始就支持xpath的...可以直接写...
楼主这个可以放到firefox里让ff支持xpath选择器了...
重新封装一下, 能完全兼容opera的xpath语法就好了..
那样就可以把用到xpath的opera-ujs无缝移植到ff的gm上了..
狄默默斯基 2012-02-29
  • 打赏
  • 举报
回复
有什么用啊,,搞这么复杂,无非就是一个节点选择器嘛,,

为什么不直接赋个id,然后document.getElementById获取,

你这种写法的随意性,会导致自身的javascript知识混乱,

showjim 2012-02-29
  • 打赏
  • 举报
回复
个人认为要高效的话,需要动态生成函数,把path解析成一系列高效的函数,而不是满地正则。
只是一个建议
ootwo 2012-02-29
  • 打赏
  • 举报
回复
管理员怎么见字少的就删?
岑子哥 2012-02-29
  • 打赏
  • 举报
回复
测试了下,还不错,IE的浏览器是有点不理想!^_^
zh19881213 2012-02-29
  • 打赏
  • 举报
回复
你是来分析我的代码的么?我觉得你是来叫我膈应的
狄默默斯基 2012-02-29
  • 打赏
  • 举报
回复
[Quote=引用 13 楼 zh19881213 的回复:]

引用 10 楼 xjl756425616 的回复:
你還準備出框架,我早就出了n多時間了
http://topic.csdn.net/u/20120206/16/662e1010-ab19-4c7d-8abe-708f9d0697bf.html


唉,我发现我看错了,连“return this” 都不知道写,看来连链式调用都算不上,垃圾中的垃圾……
[/Quote]

你這傢伙這麼激動幹嘛啊,沒看見只是一個模型嗎,而且我覺得還是具體問題具體分析好。
zh19881213 2012-02-29
  • 打赏
  • 举报
回复
[Quote=引用 10 楼 xjl756425616 的回复:]
你還準備出框架,我早就出了n多時間了
http://topic.csdn.net/u/20120206/16/662e1010-ab19-4c7d-8abe-708f9d0697bf.html
[/Quote]

你的代码:
$("#sp").css({width:"100px",height:"100px",border:"1px solid red"});

而且你说就只知道抄 jq 的,知道什么叫用户体验么?你当所有美工都能写出正确的对象格式来?你敢叫参数这样发送么?你行么你?
$("#sp").css("width:100px; height:100px; border:1px solid red;");
zh19881213 2012-02-29
  • 打赏
  • 举报
回复
[Quote=引用 10 楼 xjl756425616 的回复:]
你還準備出框架,我早就出了n多時間了
http://topic.csdn.net/u/20120206/16/662e1010-ab19-4c7d-8abe-708f9d0697bf.html
[/Quote]

唉,我发现我看错了,连“return this” 都不知道写,看来连链式调用都算不上,垃圾中的垃圾……
zh19881213 2012-02-29
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 wubai250 的回复:]
没事写这东西干什么,jquery不是很好用吗。
[/Quote]

送你句话,你工资永远过不了8k
zh19881213 2012-02-29
  • 打赏
  • 举报
回复
你内也叫框架,要你这么说我的框架也就早了,而且都好版了

就你那破玩意能查dom自定义的属性值么?能根据条件运算么?回去好好补补设计模式在来蛋B,懂什么叫装饰着模式?你那顶多就是链式调用函数,什么破玩意。
狄默默斯基 2012-02-29
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 zh19881213 的回复:]

引用 6 楼 xjl756425616 的回复:
有什么用啊,,搞这么复杂,无非就是一个节点选择器嘛,,

为什么不直接赋个id,然后document.getElementById获取,

你这种写法的随意性,会导致自身的javascript知识混乱,


以前一群二逼嫌我高调说“有本事写个jq出来”,我还真有本事,今天我能写出选择器,过些日子框架就出来,找喷爷就跟你们来,草。
……
[/Quote]

你還準備出框架,我早就出了n多時間了
http://topic.csdn.net/u/20120206/16/662e1010-ab19-4c7d-8abe-708f9d0697bf.html
wubai250 2012-02-29
  • 打赏
  • 举报
回复
没事写这东西干什么,jquery不是很好用吗。
zh19881213 2012-02-29
  • 打赏
  • 举报
回复
测试地址:http://www.zhanghong.name/test/isselect.htm
zh19881213 2012-02-29
  • 打赏
  • 举报
回复
还可以在最后加上向上索引

isSelect('//div[@id=mydiv]/...');

上边在最后加上的“/...”,意思是找到符合条件节点的父元素,斜杠后边有几个点(.),就代表向上几级

例如:
isSelect('//div[@id=mydiv]/.'); //从结果中向上查找 1 级
isSelect('//div[@id=mydiv]/..'); //从结果中向上查找 2 级
isSelect('//div[@id=mydiv]/...'); //从结果中向上查找 3 级
isSelect('//div[@id=mydiv]/....'); //从结果中向上查找 4 级
zh19881213 2012-02-29
  • 打赏
  • 举报
回复
一些示例:

\ 一个斜线代表子节点
\\ 连个斜线代表子孙节点

isSelect('/body/div', document.body);

上边的意思是查找 body 下边的 div ,可以写成下边这样直接查找子孙节点:
isSelect('//div', document.body);

也可以查找子孙的子孙
isSelect('//div//span', document.body);

还可以添加条件
isSelect('//div[@name=myname]//span', document.body);

多个条件同时使用也可以
isSelect('//div[(@name=myname1 || @name=myname2) && @innerhtml>你好]//span', document.body);

上边条件“@innerhtml>你好”中,我使用的是大于号(<)。意思是 innerhtml 属性的值包含“你好”。下边是所有可以使用的运算符:
= 相等
< 数字:右边大于左边,字符串:右边包含左边
> 数字:左边大于右边,字符串:左边包含右边
!= 不相等
<= 数字:右边大于等于左边,字符串:右边包含左边或相等
=> 数字:左边大于等于右边,字符串:左边包含右边或相等

在进行属性对比的时候,函数会先尝试将对比值转换成数字做数学比较,如果转换失败才会执行字符串比较。例如:
isSelect('//div[@id>12]'); //条件中的 12 可以转换成数字。

除了属性对比还可以使用位置对比
isSelect('//div[0]'); //找到第 1 个
isSelect('//div[position < 100]'); //找到前 100 个
isSelect('//div[position % 5 = 0]'); //找到位置与 5 取余等于 0 的
isSelect('//div[first]'); //找到第 1 个
isSelect('//div[last - 3]'); //找到倒数第 3 个
zh19881213 2012-02-29
  • 打赏
  • 举报
回复
[Quote=引用 6 楼 xjl756425616 的回复:]
有什么用啊,,搞这么复杂,无非就是一个节点选择器嘛,,

为什么不直接赋个id,然后document.getElementById获取,

你这种写法的随意性,会导致自身的javascript知识混乱,
[/Quote]

以前一群二逼嫌我高调说“有本事写个jq出来”,我还真有本事,今天我能写出选择器,过些日子框架就出来,找喷爷就跟你们来,草。

这里是技术讨论帖,说没用的滚出去!
加载更多回复(1)

87,992

社区成员

发帖
与我相关
我的任务
社区描述
Web 开发 JavaScript
社区管理员
  • JavaScript
  • 无·法
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧