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 CSS修改学习第二章 样式
Feb 19 Javascript
Javascript中的for in循环和hasOwnProperty结合使用
Jun 05 Javascript
jquery取消选择select下拉框示例代码
Feb 22 Javascript
Java与JavaScript中判断两字符串是否相等的区别
Mar 13 Javascript
使用cropper.js裁剪头像的实例代码
Sep 29 Javascript
实例讲解javascript实现异步图片上传方法
Dec 05 Javascript
AngularJS双向数据绑定原理之$watch、$apply和$digest的应用
Jan 30 Javascript
js中对象与对象创建方法的各种方法
Feb 27 Javascript
layui中select,radio设置不生效的解决方法
Sep 05 Javascript
jquery 遍历hash操作示例【基于ajax交互】
Oct 12 jQuery
vue 路由守卫(导航守卫)及其具体使用
Feb 25 Javascript
vue使用节流函数的踩坑实例指南
May 20 Vue.js
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
eaglephp使用微信api接口开发微信框架
2014/01/09 PHP
phpnow php探针环境检测代码
2014/11/04 PHP
PHP函数shuffle()取数组若干个随机元素的方法分析
2016/04/02 PHP
YII框架常用技巧总结
2019/04/27 PHP
jQuery ui插件的使用方法代码实例
2013/05/08 Javascript
动态创建script标签实现跨域资源访问的方法介绍
2014/02/28 Javascript
Nodejs Post请求报socket hang up错误的解决办法
2014/09/25 NodeJs
jQuery中change事件用法实例
2014/12/26 Javascript
JavaScript限定图片显示大小的方法
2015/03/11 Javascript
Jquery ajax 同步阻塞引起的UI线程阻塞问题
2015/11/17 Javascript
JavaScript代码性能优化总结篇
2016/05/15 Javascript
javascript中的面向对象
2017/03/30 Javascript
JS使用cookie实现只出现一次的广告代码效果
2017/04/22 Javascript
javascript+jQuery实现360开机时间显示效果
2017/11/03 jQuery
ES6 Promise对象的应用实例分析
2019/06/27 Javascript
javascript的惯性运动实现代码实例
2019/09/07 Javascript
在nuxt中使用路由重定向的实例
2020/11/06 Javascript
使用Mixin设计模式进行Python编程的方法讲解
2016/06/21 Python
Python tkinter事件高级用法实例
2018/01/31 Python
使用Python通过win32 COM实现Word文档的写入与保存方法
2018/05/08 Python
详解Python做一个名片管理系统
2019/03/14 Python
Python通过4种方式实现进程数据通信
2020/03/12 Python
史上最详细的Python打包成exe文件教程
2021/01/17 Python
css3设置box-pack和box-align让div里面的元素垂直居中
2014/09/01 HTML / CSS
使用CSS3制作响应式导航菜单的方法
2015/07/12 HTML / CSS
input元素的url类型和email类型简介
2012/07/11 HTML / CSS
美国最大的网络男装服装品牌:Bonobos
2017/05/25 全球购物
达拉斯牛仔官方商店:Dallas Cowboys Pro Shop
2018/02/10 全球购物
Tomcat的缺省是多少,怎么修改
2014/04/09 面试题
家长给幼儿园的表扬信
2014/01/09 职场文书
运动会四百米广播稿
2014/01/19 职场文书
社区好人好事材料
2014/12/26 职场文书
2015年公司工作总结
2015/04/25 职场文书
驾驶员安全责任协议书
2016/03/22 职场文书
写作指导:怎么书写竞聘演讲稿?
2019/07/04 职场文书
java executor包参数处理功能 
2022/02/15 Java/Android