JavaScript实现自己的DOM选择器原理及代码


Posted in Javascript onMarch 04, 2013

解释器模式(Interpreter):定义一种语法格式,通过程序解释执行它并完成相应的任务。在前端编程场景中可以应用解释器模式来解释CSS选择符实现DOM元素的选择。

开放封闭原则:面向对象中的开放封闭原则是类或模块应该对扩展开放对修改封闭,在这个dom选择器中实现id选择器,元素选择器,类选择器,如果以后需要属性选择器的话定义一个属性选择器实现相应的方法,同时在简单工厂中增加相应的创建属性选择器对象分支即可。

匹配原理:浏览器在匹配CSS选择符时是按照从右到左匹配的,所以实现自己的DOM选择器时匹配行为也应该和浏览原生匹配行为一致。

代码:

(function (ns) { 
/* 
//tagName 
console.log(dom.get("p")); 
//#id 
console.log(dom.get("#div")); 
//.class 
console.log(dom.get(".span", document.body)); 
//tag.class 
console.log(dom.get("div.span")); 
//#id .class 
console.log(dom.get("#div .span")); 
//.class .class 
console.log(dom.get(".ul .li-test")); 
*/ 
var doc = document; 
var simple = /^(?:#|\.)?([\w-_]+)/; 
function api(query, context) { 
context = context || doc; 
//调用原生选择器 
if(!simple.test(query) && context.querySelectorAll){ 
return context.querySelectorAll(query); 
}else { 
//调用自定义选择器 
return interpret(query, context); 
} 
} 
//解释执行dom选择符 
function interpret(query, context){ 
var parts = query.replace(/\s+/, " ").split(" "); 
var part = parts.pop(); 
var selector = Factory.create(part); 
var ret = selector.find(context); 
return (parts[0] && ret[0]) ? filter(parts, ret) : ret; 
} 
//ID选择器 
function IDSelector(id) { 
this.id = id.substring(1); 
} 
IDSelector.prototype = { 
find: function (context) { 
return document.getElementById(this.id); 
}, 
match: function(element){ 
return element.id == this.id; 
} 
}; 
IDSelector.test = function (selector) { 
var regex = /^#([\w\-_]+)/; 
return regex.test(selector); 
}; 
//元素选择器 
function TagSelector(tagName) { 
this.tagName = tagName.toUpperCase(); 
} 
TagSelector.prototype = { 
find: function (context) { 
return context.getElementsByTagName(this.tagName); 
}, 
match: function(element){ 
return this.tagName == element.tagName.toUpperCase() || this.tagName === "*"; 
} 
}; 
TagSelector.test = function (selector) { 
var regex = /^([\w\*\-_]+)/; 
return regex.test(selector); 
}; 
//类选择器 
function ClassSelector(className) { 
var splits = className.split('.'); 
this.tagName = splits[0] || undefined ; 
this.className = splits[1]; 
} 
ClassSelector.prototype = { 
find: function (context) { 
var elements; 
var ret = []; 
var tagName = this.tagName; 
var className = this.className; 
var selector = new TagSelector((tagName || "*")); 
//支持原生getElementsByClassName 
if (context.getElementsByClassName) { 
elements = context.getElementsByClassName(className); 
if(!tagName){ 
return elements; 
} 
for(var i=0,n=elements.length; i<n; i++){ 
if( selector.match(elements[i]) ){ 
ret.push(elements[i]); 
} 
} 
} else { 
elements = selector.find(context); 
for(var i=0, n=elements.length; i<n; i++){ 
if( this.match(elements[i]) ) { 
ret.push(elements[i]); 
} 
} 
} 
return ret; 
}, 
match: function(element){ 
var className = this.className; 
var regex = new RegExp("^|\\s" + className + "$|\\s"); 
return regex.test(element.className); 
} 
}; 
ClassSelector.test = function (selector) { 
var regex = /^([\w\-_]+)?\.([\w\-_]+)/; 
return regex.test(selector); 
}; 
//TODO:属性选择器 
function AttributeSelector(attr){ 
this.find = function(context){ 
}; 
this.match = function(element){ 
}; 
} 
AttributeSelector.test = function (selector){ 
var regex = /\[([\w\-_]+)(?:=([\w\-_]+))?\]/; 
return regex.test(selector); 
}; 
//根据父级元素过滤 
function filter(parts, nodeList){ 
var part = parts.pop(); 
var selector = Factory.create(part); 
var ret = []; 
var parent; 
for(var i=0, n=nodeList.length; i<n; i++){ 
parent = nodeList[i].parentNode; 
while(parent && parent !== doc){ 
if(selector.match(parent)){ 
ret.push(nodeList[i]); 
break; 
} 
parent = parent.parentNode; 
} 
} 
return parts[0] && ret[0] ? filter(parts, ret) : ret; 
} 
//根据查询选择符创建相应选择器对象 
var Factory = { 
create: function (query) { 
if (IDSelector.test(query)) { 
return new IDSelector(query); 
} else if (ClassSelector.test(query)) { 
return new ClassSelector(query); 
} else { 
return new TagSelector(query); 
} 
} 
}; 
ns.dom || (ns.dom = {}); 
ns.dom.get = api; 
}(this));
Javascript 相关文章推荐
jQuery UI Datepicker length为空或不是对象错误的解决方法
Dec 19 Javascript
JQuery调webservice实现邮箱验证(检测是否可用)
May 21 Javascript
Node.js实现在目录中查找某个字符串及所在文件
Sep 03 Javascript
js格式化时间小结
Nov 03 Javascript
jQuery插件zoom实现图片全屏放大弹出层特效
Apr 15 Javascript
Node.js下自定义错误类型详解
Oct 17 Javascript
node通过express搭建自己的服务器
Sep 30 Javascript
Express系列之multer上传的使用
Oct 27 Javascript
原生JS控制多个滚动条同步跟随滚动效果
Dec 22 Javascript
vue2.0实现前端星星评分功能组件实例代码
Feb 12 Javascript
vue.js实现备忘录demo
Jun 26 Javascript
node.js 基于 STMP 协议和 EWS 协议发送邮件
Feb 14 Javascript
jQuery:节点(插入,复制,替换,删除)操作
Mar 04 #Javascript
JS获取后台Cookies值的小例子
Mar 04 #Javascript
JQuery获取各种宽度、高度(format函数)实例
Mar 04 #Javascript
javascript加号&quot;+&quot;的二义性说明
Mar 04 #Javascript
js给dropdownlist添加选项的小例子
Mar 04 #Javascript
jQuery侧边栏随窗口滚动实现方法
Mar 04 #Javascript
利用js实现选项卡的特别效果的实例
Mar 03 #Javascript
You might like
fleaphp rolesNameField bug解决方法
2011/04/23 PHP
php 备份数据库代码(生成word,excel,json,xml,sql)
2013/06/23 PHP
CI框架使用composer安装的依赖包步骤与方法分析
2016/11/21 PHP
php封装实现钉钉机器人报警接口的示例代码
2020/08/08 PHP
非常有用的40款jQuery 插件推荐(系列二)
2011/12/25 Javascript
jquery动画4.升级版遮罩效果的图片走廊--带自动运行效果
2012/08/24 Javascript
JavaScript模拟可展开、拖动与关闭的聊天窗口实例
2015/05/12 Javascript
深入理解JQuery中的事件与动画
2016/05/18 Javascript
Javascript类型系统之String字符串类型详解
2016/06/21 Javascript
angular源码学习第一篇 setupModuleLoader方法
2016/10/20 Javascript
AngularJS中$apply方法和$watch方法用法总结
2016/12/13 Javascript
微信小程序 实现动态显示和隐藏某个控件
2017/04/27 Javascript
Node.js编写CLI的实例详解
2017/05/17 Javascript
JavaScript使用atan2来绘制箭头和曲线的实例
2017/09/14 Javascript
基于JS实现快速读取TXT文件
2020/08/25 Javascript
python 文件与目录操作
2008/12/24 Python
分享15个最受欢迎的Python开源框架
2014/07/13 Python
Python UnicodeEncodeError: 'gbk' codec can't encode character 解决方法
2015/04/24 Python
python文件特定行插入和替换实例详解
2017/07/12 Python
Keras 切换后端方式(Theano和TensorFlow)
2020/06/19 Python
资生堂美国官网:Shiseido美国
2016/09/02 全球购物
物业保安主管岗位职责
2013/12/25 职场文书
转党组织关系介绍信
2014/01/08 职场文书
青年创业培训欢迎词
2014/01/10 职场文书
党的群众路线教育实践活动心得体会900字
2014/03/07 职场文书
《宋庆龄故居的樟树》教学反思
2014/04/07 职场文书
党员干部廉洁承诺书
2014/05/28 职场文书
企业员工集体活动方案
2014/08/17 职场文书
儿园租房协议书范本
2014/12/02 职场文书
甲午风云观后感
2015/06/02 职场文书
2016年庆祝六一儿童节活动总结
2016/04/06 职场文书
2019餐饮行业创业计划书!
2019/06/27 职场文书
导游词之临安白水涧
2019/11/05 职场文书
Django项目如何正确配置日志(logging)
2021/04/29 Python
教你怎么用Python实现多路径迷宫
2021/04/29 Python
MySQL单表千万级数据处理的思路分享
2021/06/05 MySQL