教你寫一個簡單的dom選擇器
公司的师兄原来写了个dom选择器,感觉不错啊!!!原来自己从来没有想过能写这些,所以我们今天一起来试试吧
我写的这个很简单,就是最简单的那种了,但是公司师兄写的很好。。。。。。
简单dom选择器
先上个完整的代码吧,因为我已经写起了:
1 <html xmlns="http://www.w3.org/1999/xhtml">
2 <head>
3 <title></title>
4 </head>
5 <body>
6 <div id="parent">
7 <div class="child1" id="c">
8 <div>
9 </div>
10 <input type="button" value="测试" class="child1.1" />
11 </div>
12 <div class="child1">
13 </div>
14 <div class="child2">
15 </div>
16 <div class="child2 child1">
17 <div class="child1.1">
18 <input class="l" value="三层测试" />
19 </div>
20 </div>
21 <input type="button" value="测试2" />
22 </div>
23 <script type="text/javascript">
24
25 (function () {
26 var queryFunc = {
27 '#': function (id) {
28 var arr = [];
29 arr.push(document.getElementById(id))
30 return arr;
31 },
32 '.': function (className) {
33 var els = document.getElementsByTagName('*');
34 var reg = new RegExp('(^|\\s)' + className + '(\\s|$)');
35 var arr = [];
36 for (var i = 0, len = els.length; i < len; i++) {
37 if (reg.test(els[i].className)) {
38 arr.push(els[i]);
39 }
40 }
41 return arr;
42 },
43 'tag': function (tag) {
44 return document.getElementsByTagName(tag);
45 }
46 };
47
48 var filterFunc = {
49 '#': function (el, id) {
50 return this.commomFunc(el, function (p) {
51 if (p.id == id) {
52 return true;
53 }
54 });
55 },
56 '.': function (el, className) {
57 var reg = new RegExp('(^|\\s)' + className + '(\\s|$)');
58 return this.commomFunc(el, function (p) {
59 if (reg.test(p.className)) {
60 return true;
61 }
62 });
63 },
64 'tag': function (el, tag) {
65 return this.commomFunc(el, function (p) {
66 if (p.tagName.toLowerCase() == tag) {
67 return true;
68 }
69 });
70 },
71 commomFunc: function (el, callback) {
72 var p = el;
73 var end = false;
74 while (!end) {
75 p = p.parentNode || p.parentElement || null;
76 if (!p) return false;
77 var b = callback && callback(p);
78 if (b) return b;
79 if (p.tagName == 'BODY') {
80 end = true;
81 }
82 }
83 return false;
84 }
85 };
86
87 var getKV = function (str) {
88 if (!str || typeof str != 'string') return null;
89 var k = str.substring(0, 1);
90 var v = str;
91 if (k == '.' || k == '#') {
92 v = str.substring(1, str.length);
93 } else {
94 k = 'tag';
95 }
96 return {
97 k: k,
98 v: v
99 };
100 }
101
102 var query = function (str) {
103 var kv = getKV(str)
104 return (queryFunc[kv.k] && queryFunc[kv.k](kv.v));
105 };
106 var filter = function (el, str) {
107 var kv = getKV(str);
108 return (filterFunc[kv.k] && filterFunc[kv.k](el, kv.v));
109 };
110 var explodeSelector = function (str) {
111 if (!str) return [];
112 //第一步去掉多余的空格
113 str = str.replace(/\s+/g, ' ');
114 var arr = str.split(' ');
115 return arr;
116 };
117 //筛选元素是否具有该属性
118
119 var queryAll = function (str) {
120 var arrSelector = explodeSelector(str);
121 var len = arrSelector.length;
122
123 //当前索引
124 var index = len - 2;
125 var curDom = null;
126 var curSelector = null;
127
128 //第一轮筛选出来的元素
129 var els = query(arrSelector[len - 1]);
130 //只有一个选择器便直接返回了
131 if (len == 1) return els;
132
133 while (index != -1) {
134 //获取当前的筛选选择器
135 curSelector = arrSelector[index];
136 var tmpArr = [];
137 for (var i = 0, len = els.length; i < len; i++) {
138 var tmpEl = els[i];
139 if (filter(tmpEl, curSelector))
140 tmpArr.push(tmpEl);
141 }
142 els = tmpArr;
143 index--;
144 }
145 return els;
146 };
147 window.queryAll = queryAll;
148 })();
149
150 var selector = [
151 'div',
152 'div input',
153 '#parent',
154 '#parent .child1',
155 '#parent .child2',
156 '.child1 .child1.1',
157 '#c .child1.1',
158 '#parent .child1 .child1.1 input'
159 ];
160
161 for (var i in selector) {
162 console.log(selector[i] + '——' + queryAll(selector[i]));
163 var s = '';
164 }
165
166 </script>
167 </body>
168 </html>1 <div id="parent"> 2 <div class="child1" id="c"> 3 <div> 4 </div> 5 <input type="button" value="测试" class="child1.1" /> 6 </div> 7 <div class="child1"> 8 </div> 9 <div class="child2"> 10 </div> 11 <div class="child2 child1"> 12 <div class="child1.1"> 13 <input class="l" value="三层测试" /> 14 </div> 15 </div> 16 <input type="button" value="测试2" /> 17 </div>
简单的结果,然后我们来简单看看代码:
1 (function () {
2 var queryFunc = {
3 '#': function (id) {
4 var arr = [];
5 arr.push(document.getElementById(id))
6 return arr;
7 },
8 '.': function (className) {
9 var els = document.getElementsByTagName('*');
10 var reg = new RegExp('(^|\\s)' + className + '(\\s|$)');
11 var arr = [];
12 for (var i = 0, len = els.length; i < len; i++) {
13 if (reg.test(els[i].className)) {
14 arr.push(els[i]);
15 }
16 }
17 return arr;
18 },
19 'tag': function (tag) {
20 return document.getElementsByTagName(tag);
21 }
22 };
23
24 var filterFunc = {
25 '#': function (el, id) {
26 return this.commomFunc(el, function (p) {
27 if (p.id == id) {
28 return true;
29 }
30 });
31 },
32 '.': function (el, className) {
33 var reg = new RegExp('(^|\\s)' + className + '(\\s|$)');
34 return this.commomFunc(el, function (p) {
35 if (reg.test(p.className)) {
36 return true;
37 }
38 });
39 },
40 'tag': function (el, tag) {
41 return this.commomFunc(el, function (p) {
42 if (p.tagName.toLowerCase() == tag) {
43 return true;
44 }
45 });
46 },
47 commomFunc: function (el, callback) {
48 var p = el;
49 var end = false;
50 while (!end) {
51 p = p.parentNode || p.parentElement || null;
52 if (!p) return false;
53 var b = callback && callback(p);
54 if (b) return b;
55 if (p.tagName == 'BODY') {
56 end = true;
57 }
58 }
59 return false;
60 }
61 };
62
63 var getKV = function (str) {
64 if (!str || typeof str != 'string') return null;
65 var k = str.substring(0, 1);
66 var v = str;
67 if (k == '.' || k == '#') {
68 v = str.substring(1, str.length);
69 } else {
70 k = 'tag';
71 }
72 return {
73 k: k,
74 v: v
75 };
76 }
77
78 var query = function (str) {
79 var kv = getKV(str)
80 return (queryFunc[kv.k] && queryFunc[kv.k](kv.v));
81 };
82 var filter = function (el, str) {
83 var kv = getKV(str);
84 return (filterFunc[kv.k] && filterFunc[kv.k](el, kv.v));
85 };
86 var explodeSelector = function (str) {
87 if (!str) return [];
88 //第一步去掉多余的空格
89 str = str.replace(/\s+/g, ' ');
90 var arr = str.split(' ');
91 return arr;
92 };
93 //筛选元素是否具有该属性
94
95 var queryAll = function (str) {
96 var arrSelector = explodeSelector(str);
97 var len = arrSelector.length;
98
99 //当前索引
100 var index = len - 2;
101 var curDom = null;
102 var curSelector = null;
103
104 //第一轮筛选出来的元素
105 var els = query(arrSelector[len - 1]);
106 //只有一个选择器便直接返回了
107 if (len == 1) return els;
108
109 while (index != -1) {
110 //获取当前的筛选选择器
111 curSelector = arrSelector[index];
112 var tmpArr = [];
113 for (var i = 0, len = els.length; i < len; i++) {
114 var tmpEl = els[i];
115 if (filter(tmpEl, curSelector))
116 tmpArr.push(tmpEl);
117 }
118 els = tmpArr;
119 index--;
120 }
121 return els;
122 };
123 window.queryAll = queryAll;
124 })();基本思路
① 获取选择器字符串,并将之分解为一个数组
var explodeSelector = function (str) {
if (!str) return [];
//第一步去掉多余的空格
str = str.replace(/\s+/g, ' ');
var arr = str.split(' ');
return arr;
};② 与CSS选择器一致,根据各种条件选取相关元素
1 var queryFunc = {
2 '#': function (id) {
3 var arr = [];
4 arr.push(document.getElementById(id))
5 return arr;
6 },
7 '.': function (className) {
8 var els = document.getElementsByTagName('*');
9 var reg = new RegExp('(^|\\s)' + className + '(\\s|$)');
10 var arr = [];
11 for (var i = 0, len = els.length; i < len; i++) {
12 if (reg.test(els[i].className)) {
13 arr.push(els[i]);
14 }
15 }
16 return arr;
17 },
18 'tag': function (tag) {
19 return document.getElementsByTagName(tag);
20 }
21 };③ 根据选择器与获得的dom数组,判断其父元素是否具有相关属性(id,className,tag),有便留下来,没有就不管他
过滤下来的就是我们要的元素:
1 var filterFunc = {
2 '#': function (el, id) {
3 return this.commomFunc(el, function (p) {
4 if (p.id == id) {
5 return true;
6 }
7 });
8 },
9 '.': function (el, className) {
10 var reg = new RegExp('(^|\\s)' + className + '(\\s|$)');
11 return this.commomFunc(el, function (p) {
12 if (reg.test(p.className)) {
13 return true;
14 }
15 });
16 },
17 'tag': function (el, tag) {
18 return this.commomFunc(el, function (p) {
19 if (p.tagName.toLowerCase() == tag) {
20 return true;
21 }
22 });
23 },
24 commomFunc: function (el, callback) {
25 var p = el;
26 var end = false;
27 while (!end) {
28 p = p.parentNode || p.parentElement || null;
29 if (!p) return false;
30 var b = callback && callback(p);
31 if (b) return b;
32 if (p.tagName == 'BODY') {
33 end = true;
34 }
35 }
36 return false;
37 }
38 }; 1 var queryAll = function (str) {
2 var arrSelector = explodeSelector(str);
3 var len = arrSelector.length;
4
5 //当前索引
6 var index = len - 2;
7 var curDom = null;
8 var curSelector = null;
9
10 //第一轮筛选出来的元素
11 var els = query(arrSelector[len - 1]);
12 //只有一个选择器便直接返回了
13 if (len == 1) return els;
14
15 while (index != -1) {
16 //获取当前的筛选选择器
17 curSelector = arrSelector[index];
18 var tmpArr = [];
19 for (var i = 0, len = els.length; i < len; i++) {
20 var tmpEl = els[i];
21 if (filter(tmpEl, curSelector))
22 tmpArr.push(tmpEl);
23 }
24 els = tmpArr;
25 index--;
26 }
27 return els;
28 };④,然后,就没有然后了。。。。
不足与提高
这个代码明显就是玩具,三无产品:
① 无测试
② 无子选择器/兄弟选择器
③ 无性能
但是,以上东西暂时和我无关啦,因为学习嘛。。。。
最后附上师兄选择器代码:
1 <!doctype html>
2 <html>
3 <head>
4 <title>aiQuery test</title>
5 <script type="text/javascript">
6 /**
7 * aiQuery
8 * @author ouxingzhi
9 */
10 void function (window, document, undefined) {
11 var location = window.location,
12 Slice = [].slice,
13 RegTrim = /(?:^\s+|\s+$)/,
14 RegBlank = /\s+/,
15 RegOperate = /\s*(?:\s|>|\+|~(?!\=))\s*/i,
16 RegElement = /^([\w\-]+|\*)?(?:\#([\w\-]+))?(?:\.([\w\-]+))?(?:\[([\w-]+)(?:([~|\^|\$|\*|\|]?=)['"]?([\w-]+)['"]?)?\])?(?:\:([\w-]+(?:\([\w-]+\))?))?$/i;
17 function AIQuery(Selector, Content) {
18 Selector = Selector.replace(RegTrim, '');
19 Content = Content || document;
20 if (Content.querySelectorAll) {
21 return Slice.call(Content.querySelectorAll(Selector));
22 } else {
23 return querySelectorAll(Selector, Content)
24 }
25 }
26 function querySelectorAll(Selector, Content) {
27 var Groups = Selector.split(/\s*\,\s*/img),
28 Results = [];
29 for (var i = 0,
30 len = Groups.length; i < len; i++) {
31 Results = Results.concat(Find(Groups[i], Content))
32 }
33 return Results
34 }
35 function Find(Selector, Content) {
36 var Results = [],
37 atoms = Selector.split(RegOperate),
38 operates = Selector.match(RegOperate);
39 operates = operates || [];
40 for (var i = 0,
41 len = operates.length; i < len; i++) (operates[i] = /^\s+$/.test(operates[i]) ? ' ' : operates[i].replace(RegTrim, ''));
42 var Results = EachTo(' ', atoms.shift(), operates, atoms, Content);
43 return Results
44 }
45 function EachTo(op, at, operates, atoms, Content) {
46 var Results = [],
47 Median = [],
48 operate,
49 atom;
50 if (Content.constructor === Array || 'length' in Content) {
51 for (var i = 0,
52 len = Content.length; i < len; i++) {
53 Results = Results.concat(EachTo(op, at, operates.slice(0), atoms.slice(0), Content[i]))
54 }
55 } else if (Content.constructor === String) {
56 Content = Find(Content, document);
57 Results.concat(EachTo(op, at, operates.slice(0), atoms.slice(0), Content[i]))
58 } else {
59 Median = GetElementByAny(op, at, Content);
60 if (Median) {
61 if (operates && operates.length && atoms && atoms.length) {
62 Results = EachTo(operates.shift(), atoms.shift(), operates, atoms, Median)
63 } else {
64 Results = Median
65 }
66 }
67 }
68 return Results
69 }
70 function GetElementByAny(op, at, Content) {
71 if (typeof OperateFunction[op] !== 'undefined') {
72 return OperateFunction[op](at, Content)
73 }
74 }
75 var OperateFunction = {
76 ' ': function (at, Content) {
77 var einfo = buildElementInfo(at),
78 preNodes = [];
79 if (!einfo) return [];
80 if (einfo.Id) {
81 preNodes = document.getElementById(einfo.Id);
82 preNodes = preNodes ? [preNodes] : []
83 } else if (einfo.ClassName && Content.getElementsByClassName) {
84 preNodes = Content.getElementsByClassName(einfo.ClassName);
85 preNodes = preNodes || []
86 } else if (einfo.TagName && Content.getElementsByTagName) {
87 preNodes = Content.getElementsByTagName(einfo.TagName);
88 preNodes = preNodes || []
89 } else {
90 preNodes = Content.getElementsByTagName('*');
91 preNodes = preNodes || []
92 };
93 return filterNode(einfo, preNodes)
94 },
95 '>': function (at, Content) {
96 var einfo = buildElementInfo(at);
97 preNodes = Content.childNodes || [];
98 if (!einfo) return [];
99 return filterNode(einfo, preNodes)
100 },
101 '+': function (at, Content) {
102 if (Content === document) return [];
103 var einfo = buildElementInfo(at);
104 if (!einfo) return [];
105 var results = [],
106 preNodes = (function () {
107 var nextNode = Content.nextSibling;
108 while (nextNode && nextNode.nodeType != 1) {
109 nextNode = nextNode.nextSibling
110 }
111 return nextNode
112 })();
113 preNodes = preNodes ? [preNodes] : [];
114 if (preNodes.length) {
115 results = filterNode(einfo, preNodes)
116 } else {
117 results = []
118 }
119 return results
120 },
121 '~': function (at, Content) {
122 if (Content === document) return [];
123 var einfo = buildElementInfo(at),
124 preNodes = [];
125 if (!einfo) return [];
126 var sibling = Content.parentNode ? Content.parentNode.childNodes : null;
127 if (sibling) {
128 for (var i = 0,
129 len = sibling.length; i < len; i++) if (Content !== sibling[i]) preNodes.push(sibling[i])
130 }
131 return filterNode(einfo, preNodes)
132 }
133 };
134 function buildElementInfo(at) {
135 var Einfo = RegElement.exec(at);
136 if (!Einfo) return;
137 return {
138 TagName: Einfo[1] || undefined,
139 Id: Einfo[2] || undefined,
140 ClassName: Einfo[3] || undefined,
141 AttrName: Einfo[4] || undefined,
142 AttrOper: Einfo[5] || undefined,
143 AttrVal: Einfo[6] || undefined,
144 FakeClass: Einfo[7] || undefined
145 }
146 }
147 function filterNode(Einfo, Nodes) {
148 var results = [],
149 RegClassName,
150 isMatch;
151 if (Einfo.ClassName) RegClassName = new RegExp('\\b' + Einfo.ClassName + '\\b', 'i');
152 for (var i = 0,
153 len = Nodes.length; i < len; i++) {
154 isMatch = true;
155 if (Einfo.TagName !== undefined && Einfo.TagName.toUpperCase() !== Nodes[i].nodeName) isMatch = false;
156 if (Einfo.Id !== undefined && Einfo.Id !== Nodes[i].id) isMatch = false;
157 if (Einfo.ClassName !== undefined && !Nodes[i].className.match(RegClassName)) isMatch = false;
158 isMatch = isMatchAttribute(Einfo, Nodes[i], isMatch);
159 isMatch = isMatchFakeClass(Einfo, Nodes[i], isMatch);
160 if (isMatch) results.push(Nodes[i])
161 }
162 return results
163 }
164 function isMatchAttribute(Einfo, node, isMatch) {
165 if (Einfo.AttrName === undefined && Einfo.AttrOper === undefined && Einfo.AttrVal === undefined) { } else if (Einfo.AttrName !== undefined && Einfo.AttrOper === undefined && Einfo.AttrVal === undefined && node.getAttribute && node.getAttribute(Einfo.AttrName) !== null) {
166 isMatch = true
167 } else if (Einfo.AttrName !== undefined && Einfo.AttrOper !== undefined && Einfo.AttrVal !== undefined && node.getAttribute) {
168 switch (Einfo.AttrOper) {
169 case '=':
170 isMatch = node.getAttribute(Einfo.AttrName) === Einfo.AttrVal;
171 break;
172 case '~=':
173 isMatch = !!(node.getAttribute(Einfo.AttrName) && node.getAttribute(Einfo.AttrName).match(new RegExp('(?:^|\\s+)' + Einfo.AttrVal + '(?:$|\\s+)', 'i')));
174 break;
175 case '^=':
176 isMatch = !!(node.getAttribute(Einfo.AttrName) && node.getAttribute(Einfo.AttrName).match(new RegExp('^' + Einfo.AttrVal, 'i')));
177 break;
178 case '$=':
179 isMatch = !!(node.getAttribute(Einfo.AttrName) && node.getAttribute(Einfo.AttrName).match(new RegExp(Einfo.AttrVal + '$', 'i')));
180 break;
181 case '*=':
182 isMatch = !!(node.getAttribute(Einfo.AttrName) && node.getAttribute(Einfo.AttrName).match(new RegExp(Einfo.AttrVal, 'i')));
183 break;
184 case '|=':
185 isMatch = !!(node.getAttribute(Einfo.AttrName) && node.getAttribute(Einfo.AttrName).match(new RegExp('(?:^|\\-)' + Einfo.AttrVal + '(?:$|\\-)', 'i')));
186 break
187 }
188 }
189 return isMatch
190 }
191 function isMatchFakeClass(Einfo, node, isMatch) {
192 if (Einfo.FakeClass === undefined) { } else {
193 switch (Einfo.FakeClass) {
194 case 'empty':
195 isMatch = node.innerHTML.replace(RegTrim, '').length == 0;
196 break;
197 case 'checked':
198 if (node.nodeName.match(/(?:INPUT|TEXTAREA|BUTTON|SELECT|OPTION)/i)) isMatch = !!node.checked;
199 break;
200 case 'enabled':
201 if (node.nodeName.match(/(?:INPUT|TEXTAREA|BUTTON|SELECT|OPTION)/i)) isMatch = !!node.disabled;
202 break;
203 case 'disabled':
204 if (node.nodeName.match(/(?:INPUT|TEXTAREA|BUTTON|SELECT|OPTION)/i)) isMatch = !!node.disabled;
205 break;
206 case 'target':
207 var hash = location.hash.replace('#', '');
208 isMatch = hash === node.id || (node.name && hash === node.name);
209 break
210 }
211 }
212 return isMatch
213 }
214 window['aiQuery'] = AIQuery;
215 } (window, document);
216 </script>
217 </head>
218 <body>
219 <div class="aaa">
220 <div class="bbb">
221 <label>
222 用户名:</label>
223 <input type="text" id="username" />
224 </div>
225 <pre>
226 //使用方法
227 alert(aiQuery('.aaa .bbb [type=text]'));
228 alert(aiQuery('.aaa label + input'));
229 alert(aiQuery('#username'));
230 </pre>
231 </div>
232 <script type="text/javascript">
233 alert(aiQuery('.aaa .bbb [type=text]'));
234 alert(aiQuery('.aaa label + input'));
235 alert(aiQuery('#username'));
236 </script>
237 </body>
238 </html>然后,就没有然后了。。。
小插曲-浮点数计算
今天项目重遇到一个问题,我和我的小伙伴都惊呆了:
你没有看错,33.1 * 3 就是那么多!!!
尼玛已证明这是一个js的bug,我可以这么说么???
解决方案,求回复!
parseInt(33.1 * 3 * 100) / 100
點擊查看更多內容
為 TA 點贊
評論
評論
共同學習,寫下你的評論
評論加載中...
作者其他優質文章
正在加載中
感謝您的支持,我會繼續努力的~
掃碼打賞,你說多少就多少
贊賞金額會直接到老師賬戶
支付方式
打開微信掃一掃,即可進行掃碼打賞哦

