JavaScript實現(xiàn)自己的DOM選擇器原理及代碼
更新時間:2013年03月04日 14:26:47 作者:
實現(xiàn)自己的DOM選擇器時匹配行為也應(yīng)該和瀏覽原生匹配行為一致,接下來本文將詳細介紹下實現(xiàn)思路及方法,感興趣的你可以參考下或許對你鞏固知識有所幫助
解釋器模式(Interpreter):定義一種語法格式,通過程序解釋執(zhí)行它并完成相應(yīng)的任務(wù)。在前端編程場景中可以應(yīng)用解釋器模式來解釋CSS選擇符實現(xiàn)DOM元素的選擇。
開放封閉原則:面向?qū)ο笾械拈_放封閉原則是類或模塊應(yīng)該對擴展開放對修改封閉,在這個dom選擇器中實現(xiàn)id選擇器,元素選擇器,類選擇器,如果以后需要屬性選擇器的話定義一個屬性選擇器實現(xiàn)相應(yīng)的方法,同時在簡單工廠中增加相應(yīng)的創(chuàng)建屬性選擇器對象分支即可。
匹配原理:瀏覽器在匹配CSS選擇符時是按照從右到左匹配的,所以實現(xiàn)自己的DOM選擇器時匹配行為也應(yīng)該和瀏覽原生匹配行為一致。
代碼:
(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;
//調(diào)用原生選擇器
if(!simple.test(query) && context.querySelectorAll){
return context.querySelectorAll(query);
}else {
//調(diào)用自定義選擇器
return interpret(query, context);
}
}
//解釋執(zhí)行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);
};
//根據(jù)父級元素過濾
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;
}
//根據(jù)查詢選擇符創(chuàng)建相應(yīng)選擇器對象
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));
開放封閉原則:面向?qū)ο笾械拈_放封閉原則是類或模塊應(yīng)該對擴展開放對修改封閉,在這個dom選擇器中實現(xiàn)id選擇器,元素選擇器,類選擇器,如果以后需要屬性選擇器的話定義一個屬性選擇器實現(xiàn)相應(yīng)的方法,同時在簡單工廠中增加相應(yīng)的創(chuàng)建屬性選擇器對象分支即可。
匹配原理:瀏覽器在匹配CSS選擇符時是按照從右到左匹配的,所以實現(xiàn)自己的DOM選擇器時匹配行為也應(yīng)該和瀏覽原生匹配行為一致。
代碼:
復(fù)制代碼 代碼如下:
(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;
//調(diào)用原生選擇器
if(!simple.test(query) && context.querySelectorAll){
return context.querySelectorAll(query);
}else {
//調(diào)用自定義選擇器
return interpret(query, context);
}
}
//解釋執(zhí)行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);
};
//根據(jù)父級元素過濾
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;
}
//根據(jù)查詢選擇符創(chuàng)建相應(yīng)選擇器對象
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));
相關(guān)文章
javascript 初學(xué)教程及五子棋小程序的簡單實現(xiàn)
下面小編就為大家?guī)硪黄猨avascript 初學(xué)教程及五子棋小程序的簡單實現(xiàn)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-07-07JS 獲取瀏覽器和屏幕寬高等信息的實現(xiàn)思路及代碼
本節(jié)代碼主要使用了Document對象關(guān)于窗口的一些屬性,附實現(xiàn)代碼及源程序解決,有需求的朋友可以參考下2013-07-07如何使用require.context實現(xiàn)優(yōu)雅的預(yù)加載
這篇文章主要介紹了使用require.context實現(xiàn)優(yōu)雅的預(yù)加載?,需要的朋友可以參考下2023-05-05JavaScript如何實現(xiàn)數(shù)組按屬性分組
在JavaScript中,有多種方法可以對數(shù)組按屬性進行分組,這篇文章主要為大家至少介紹了6種常見的方法,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-08-08