都别说工资低了,我们来一起写简单的dom选择器吧!

前言

我师父(http://www.cnblogs.com/aaronjs/)说应当阅读框架(jquery),所以老夫就准备开始看了

然后公司的师兄原来写了个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>
View Code
 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>
View Code

然后,就没有然后了。。。

小插曲-浮点数计算

今天项目重遇到一个问题,我和我的小伙伴都惊呆了:

你没有看错,33.1 * 3 就是那么多!!!

尼玛已证明这是一个js的bug,我可以这么说么???

解决方案,求回复(我会说我想骗点回复么???)

 

parseInt(33.1 * 3 * 100) / 100

 

 

posted on 2013-08-14 19:35  叶小钗  阅读(6769)  评论(37编辑  收藏  举报