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 相关文章推荐
js cookies实现简单统计访问次数
Nov 24 Javascript
ComboBox 和 DateField 在IE下消失的解决方法
Aug 30 Javascript
可自己添加html的伪弹出框实现代码
Sep 08 Javascript
jQuery实现列表内容的动态载入特效
Aug 08 Javascript
jQuery实现验证年龄简单思路
Feb 24 Javascript
基于JavaScript实现本地图片预览
Feb 08 Javascript
iscroll实现下拉刷新功能
Jul 18 Javascript
在Swiper内如何制作CSS3动画效果示例代码
Dec 07 Javascript
Vue父子组件双向绑定传值的实现方法
Jul 31 Javascript
vue富文本编辑器组件vue-quill-edit使用教程
Sep 21 Javascript
JavaScript常用进制转换及位运算实例解析
Oct 14 Javascript
JS实现页面侧边栏效果探究
Jan 08 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
PHP新手入门学习方法
2011/05/08 PHP
推荐一本PHP程序猿都应该拜读的书
2014/12/31 PHP
smarty中常用方法实例总结
2015/08/07 PHP
php mysql like 实现多关键词搜索的方法
2016/10/29 PHP
yii使用bootstrap分页样式的实例
2017/01/17 PHP
PHP关键特性之命名空间实例详解
2017/05/06 PHP
再分享70+免费的jquery 图片滑块效果插件和教程
2014/12/15 Javascript
javascript实现数独解法
2015/03/14 Javascript
jquery实现的淡入淡出下拉菜单效果
2015/08/25 Javascript
Bootstrap+jfinal退出系统弹出确认框的实现方法
2016/05/30 Javascript
react-native之ART绘图方法详解
2017/08/08 Javascript
JS实现简单的选择题测评系统代码思路详解(demo)
2017/09/03 Javascript
React Native 搭建开发环境的方法步骤
2017/10/30 Javascript
JavaScript模块详解
2017/12/18 Javascript
Python3搜索及替换文件中文本的方法
2015/05/22 Python
发布你的Python模块详解
2016/09/15 Python
Python实现字典去除重复的方法示例
2017/07/31 Python
pandas将DataFrame的列变成行索引的方法
2018/04/10 Python
python smtplib模块自动收发邮件功能(一)
2018/05/22 Python
对Django 转发和重定向的实例详解
2019/08/06 Python
详解Python 循环嵌套
2020/07/09 Python
python3 中使用urllib问题以及urllib详解
2020/08/03 Python
TheFork葡萄牙:欧洲领先的在线餐厅预订平台
2019/05/27 全球购物
什么是用户模式(User Mode)与内核模式(Kernel Mode) ?
2015/09/07 面试题
土木工程建筑专业毕业生求职信
2013/10/21 职场文书
新驾驶员个人自我评价
2014/01/03 职场文书
学校消防安全制度
2014/01/30 职场文书
运输服务质量承诺书
2014/03/27 职场文书
校园标语大全
2014/06/19 职场文书
学校群众路线专项整治方案
2014/10/31 职场文书
2014年党小组工作总结
2014/12/20 职场文书
2015年全国爱耳日活动总结
2015/02/27 职场文书
欠款纠纷起诉状
2015/05/19 职场文书
庆七一晚会主持词
2015/06/30 职场文书
小学运动会入场词
2015/07/18 职场文书
深入理解redis中multi与pipeline
2021/06/02 Redis