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 相关文章推荐
Using the TextRange Object
Oct 14 Javascript
javascript json 新手入门文档
Dec 03 Javascript
javascript parseInt与Number函数的区别
Jan 21 Javascript
JavaScript Event学习第七章 事件属性
Feb 07 Javascript
基于Jquery与WebMethod投票功能实现代码
Jan 19 Javascript
40个有创意的jQuery图片、内容滑动及弹出插件收藏集之一
Dec 31 Javascript
浅析Node.js中使用依赖注入的相关问题及解决方法
Jun 24 Javascript
针对JavaScript中this指向的简单理解
Aug 26 Javascript
JS设置手机验证码60s等待实现代码
Jun 14 Javascript
jQuery实现的页面详情展开收起功能示例
Jun 11 jQuery
js实现微信聊天界面
Aug 09 Javascript
js调用网络摄像头的方法
Dec 05 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即时捕捉PHP中的错误并发送email通知的实现代码
2013/01/19 PHP
PHP函数实现分页含文本分页和数字分页
2014/10/23 PHP
php实现多维数组排序的方法示例
2017/03/23 PHP
PHP标准库 (SPL)――Countable用法示例
2020/06/05 PHP
javascript的字符串按引用复制和传递,按值来比较介绍与应用
2012/12/28 Javascript
JavaScript实现在数组中查找不同顺序排列的字符串
2014/09/26 Javascript
js+csss实现的一个带复选框的下拉框
2014/09/29 Javascript
使用JS画图之点、线、面
2015/01/12 Javascript
js完美实现@提到好友特效(兼容各大浏览器)
2015/03/16 Javascript
理解javascript闭包
2015/12/15 Javascript
基于BootStrap Metronic开发框架经验小结【五】Bootstrap File Input文件上传插件的用法详解
2016/05/12 Javascript
如何利用Promises编写更优雅的JavaScript代码
2016/05/17 Javascript
详解JavaScript中Hash Map映射结构的实现
2016/05/21 Javascript
基于JS代码实现当鼠标悬停表格上显示这一格的全部内容
2016/06/12 Javascript
返回函数的JavaScript函数
2016/06/14 Javascript
AngularJs IE Compatibility 兼容老版本IE
2016/09/01 Javascript
JavaScript使用ZeroClipboard操作剪切板
2017/05/10 Javascript
JavaScript常见JSON操作实例分析
2018/08/08 Javascript
详解Vue项目在其他电脑npm run dev运行报错的解决方法
2018/10/29 Javascript
jQuery实现动态加载(按需加载)javascript文件的方法分析
2019/05/31 jQuery
[05:03]显微镜下的DOTA2第十期——Ti3豪之超神幽鬼
2014/06/23 DOTA
[01:54]TI4西雅图DOTA2选手欢迎晚宴 现场报道
2014/07/08 DOTA
[02:57]DOTA2亚洲邀请赛 SECRET战队出场宣传片
2015/02/07 DOTA
Python程序设计入门(4)模块和包
2014/06/16 Python
Python爬取qq music中的音乐url及批量下载
2017/03/23 Python
python 实现检验33品种数据是否是正态分布
2019/12/09 Python
Python实现像awk一样分割字符串
2020/09/15 Python
聊聊python在linux下与windows下导入模块的区别说明
2021/03/03 Python
用canvas显示验证码的实现
2020/04/10 HTML / CSS
PurCotton全棉时代官网:100%天然棉花生产的生活护理用品
2016/11/18 全球购物
班级安全教育实施方案
2014/02/23 职场文书
团队队名口号大全
2014/06/06 职场文书
大学生饮品店创业计划书范文
2019/07/10 职场文书
浅谈python数据类型及其操作
2021/05/25 Python
MySQL图形化管理工具Navicat安装步骤
2021/12/04 MySQL
UNION CREATIVE《Re:从零开始的异世界生活》雷姆手办
2022/03/20 日漫