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中与toggleClass等价的程序段 以及未来学习的方向
Mar 18 Javascript
input输入框的自动匹配(原生代码)
Mar 19 Javascript
文字不间断滚动(上下左右)实例代码
Apr 21 Javascript
jQuery之按钮组件的深入解析
Jun 19 Javascript
原始的js代码和jquery对比体会
Sep 10 Javascript
动态创建script标签实现跨域资源访问的方法介绍
Feb 28 Javascript
详解Vue用自定义指令完成一个下拉菜单(select组件)
Oct 31 Javascript
JavaScript实现微信号随机切换代码
Mar 09 Javascript
JavaScript事件委托原理与用法实例分析
Jun 07 Javascript
vue项目部署上线遇到的问题及解决方法
Jun 10 Javascript
vue element动态渲染、移除表单并添加验证的实现
Jan 16 Javascript
微信小程序 image组件遇到的问题
May 28 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正则
2006/07/07 PHP
PHP编程中八种常见的文件操作方式
2006/11/19 PHP
解决File size limit exceeded 错误的方法
2013/06/14 PHP
PHP连接Nginx服务器并解析Nginx日志的方法
2015/08/16 PHP
yii2 modal弹窗之ActiveForm ajax表单异步验证
2016/06/13 PHP
老鱼 浅谈javascript面向对象编程
2010/03/04 Javascript
不同浏览器的怪癖小结
2010/07/11 Javascript
js修改table中Td的值(定义td的单击事件)
2013/01/10 Javascript
jQuery用unbind方法去掉hover事件及其他方法介绍
2013/03/18 Javascript
javascript中数组的concat()方法使用介绍
2013/12/18 Javascript
为jQuery添加Webkit的触摸的方法分享
2014/02/02 Javascript
jquery操作复选框(checkbox)的12个小技巧总结
2014/02/04 Javascript
javascript数组操作方法小结和3个属性详细介绍
2014/07/05 Javascript
javascript检测是否联网的实现代码
2014/09/28 Javascript
js实现浮动在网页右侧的简洁QQ在线客服代码
2015/09/04 Javascript
jquery编写Tab选项卡滚动导航切换特效
2020/07/17 Javascript
jquery.qtip提示信息插件用法简单实例
2016/06/17 Javascript
微信小程序 网络API Websocket详解
2016/11/09 Javascript
jQuery+Ajax实现用户名重名实时检测
2017/06/01 jQuery
Vue2.0利用vue-resource上传文件到七牛的实例代码
2017/07/28 Javascript
详解vue-cil和webpack中本地静态图片的路径问题解决方案
2017/09/27 Javascript
node.js连接mysql与基本用法示例
2019/01/05 Javascript
详解微信小程序之scroll-view的flex布局问题
2019/01/16 Javascript
vue实现购物车的小练习
2020/12/21 Vue.js
[00:31]2016完美“圣”典风云人物:国士无双宣传片
2016/12/04 DOTA
用Python中的__slots__缓存资源以节省内存开销的方法
2015/04/02 Python
对Python 文件夹遍历和文件查找的实例讲解
2018/04/26 Python
学生信息管理系统python版
2018/10/17 Python
对Python通过pypyodbc访问Access数据库的方法详解
2018/10/27 Python
详解如何设置Python环境变量?
2019/05/13 Python
python 实现aes256加密
2020/11/27 Python
美国领先的家庭智能音响系统品牌:Sonos
2018/07/20 全球购物
会计自荐书
2013/12/02 职场文书
大学校园活动策划书
2014/02/04 职场文书
开朗女孩的自我评价
2014/02/10 职场文书
餐饮食品安全责任书
2015/01/29 职场文书