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 相关文章推荐
javascript使用eval或者new Function进行语法检查
Oct 16 Javascript
JS获取当前网址、主机地址项目根路径
Nov 19 Javascript
jQuery实现首页图片淡入淡出效果的方法
Jun 10 Javascript
第三章之Bootstrap 表格与按钮功能
Apr 25 Javascript
js+html5实现canvas绘制椭圆形图案的方法
May 21 Javascript
JavaScript实现图片瀑布流和底部刷新
Jan 02 Javascript
Angular.Js之Scope作用域的学习教程
Apr 27 Javascript
详解vue中$nextTick和$forceUpdate的用法
Dec 11 Javascript
使用webpack/gulp构建TypeScript项目的方法示例
Dec 18 Javascript
Javascript实现鼠标点击冒泡特效
Dec 24 Javascript
bootstrap实现tab选项卡切换
Aug 09 Javascript
vue组件中传值EventBus的使用及注意事项说明
Nov 16 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
类的另类用法--数据的封装
2006/10/09 PHP
phpexcel导入excel数据使用方法实例
2013/12/24 PHP
PHP随机生成信用卡卡号的方法
2015/03/23 PHP
使用laravel的migrate创建数据表的方法
2019/09/30 PHP
arguments对象
2006/11/20 Javascript
用 JSON 处理缓存
2007/04/27 Javascript
JavaScript使用Math.Min返回两个数中较小数的方法
2015/04/06 Javascript
Js与Jq 获取页面元素值的方法和差异对比
2015/04/30 Javascript
JavaScript统计网站访问次数的实现代码
2015/11/18 Javascript
JavaScript中的this陷阱的最全收集并整理(没有之一)
2017/02/21 Javascript
微信小程序开发中的疑问解答汇总
2017/07/03 Javascript
详解Angular-ui-BootStrap组件的解释以及使用
2018/07/13 Javascript
vue仿element实现分页器效果
2018/09/13 Javascript
vue.draggable实现表格拖拽排序效果
2018/12/01 Javascript
微信小程序中显示倒计时代码实例
2019/05/09 Javascript
Vue $emit()不能触发父组件方法的原因及解决
2020/07/28 Javascript
在Python中使用成员运算符的示例
2015/05/13 Python
RC4文件加密的python实现方法
2015/06/30 Python
python实现rsa加密实例详解
2017/07/19 Python
windows下Python实现将pdf文件转化为png格式图片的方法
2017/07/21 Python
python实现自动网页截图并裁剪图片
2018/07/30 Python
Python列表list常用内建函数实例小结
2019/10/22 Python
如何修复使用 Python ORM 工具 SQLAlchemy 时的常见陷阱
2019/11/19 Python
Python中实现一行拆多行和多行并一行的示例代码
2020/09/06 Python
Python+OpenCV图像处理——图像二值化的实现
2020/10/24 Python
香港个人化生活购物网站:Ballyhoo Limited
2016/09/10 全球购物
东南亚旅游平台:The Trip Guru
2018/01/01 全球购物
阿根廷在线宠物商店:Puppis
2018/03/23 全球购物
微软新西兰官方网站:Microsoft New Zealand
2018/08/17 全球购物
SQL面试题
2013/04/30 面试题
桥梁工程专业求职信
2014/04/21 职场文书
2015年化验员工作总结
2015/04/10 职场文书
2019年农民幸福观调查的实践感悟
2019/12/19 职场文书
Django项目如何获得SSL证书与配置HTTPS
2021/04/30 Python
Vue Element UI自定义描述列表组件
2021/05/18 Vue.js
Vue OpenLayer测距功能的实现
2022/04/20 Vue.js