都别说工资低了,我们来一起写简单的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>
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