中级农民
- 积分
- 100
- 大米
- 颗
- 鳄梨
- 个
- 水井
- 尺
- 蓝莓
- 颗
- 萝卜
- 根
- 小米
- 粒
- 学分
- 个
- 注册时间
- 2016-6-9
- 最后登录
- 1970-1-1
|
再次谢谢楼主的经验贴,在这贴一下当时准备面试的时候准备的第四轮的代码
(假定只存在tagName, ID, class三种情况,因为45分钟如果能把特殊情况都考虑进去的话我觉得有点假。。。)
比如如果是selector_str = 'div#main p p.red span'
因为浏览器解析selector是从右到左的,所以我的思路是先找到最右的selector,找出所有满足的结果-即先找出所有的span元素
然后根据浏览器解析的原则进行dfs,找出满足条件的元素,即他们的parent element能满足前一个selector - 看找出的span元素的父节点是否有满足p.class 的,如果有,继续向上dfs
最后返回dfs后的元素
- var SELECTOR = function (selector, node) {
- node = node || document;
- var fns, getBySingleSelector, res;
- //check if the selector is valid
- if (typeof selector !== 'string') return [];
- selector = selector.split(' '); //[#main, li.selected a]
- fns = {
- id: function (sel) {
- return document.getElementById(sel); //return an Element Object
- },
- get: function (c_or_e, sel, par) {
- par = par || document;
- var i, len, temp,
- arr = [],
- get_what = (c_or_e === 'class') ? 'getElementsByClassName' : 'getElementsByTagName';
- //parent - node, nodeList, dom
- if (par.length > 1) {
- i = 0;
- len = par.length;
- while (i < len) {
- temp = par[i++][get_what](sel);
- Array.prototype.push.apply(arr, Array.prototype.slice.call(temp)); //because temp is an object
- }
- } else {
- arr = par[get_what](sel); //?
- }
- return arr;
- },
- eleFilter: function(c_or_e, sel, arr){
- if(arr === null || arr.length === 0) return arr;
- var i,ele,
- isClass,
- len = arr.length,
- res = [];
- isClass = c_or_e === 'class' ? true : false
- for(i = 0; i < len; i++){
- ele = arr[i];
- if((isClass&&ele.className.indexOf(sel) !== -1) || (!isClass && ele.nodeName === sel.toUpperCase()))
- res.push(ele);
- }
- return res;
- },
- eleValid: function(sel, node){
- if(sel === null || sel.length === 0) return true;
- if(sel.indexOf('#') > -1){
- sel = sel.split('#');
- if(fns.id(sel[1]) !== node) return false;
- if(sel[0].length > 0 && node.nodeName !== sel[0].toUpperCase()) return false;
- }else if(sel.indexOf('.') > -1){
- sel = sel.split('.');
- var i = 1, len = sel.length;
- for(i; i < len; i++){
- if(node.className.indexOf(sel[i]) === -1) return false;
- }
- if(sel[0].length > 0 && node.nodeName !== sel[0].toUpperCase()) return false;
- }else{
- return node.nodeName === sel.toUpperCase();
- }
- return true;
- }
- };
- getBySingleSelector = function(sel){
- var res = [], rep, i, len;
- if(sel === null || sel.length === 0 || typeof sel !== 'string') return [];
- if(sel.indexOf('#') > -1){
- sel = sel.split('#');
- rep = fns.id(sel[1]);
- if(sel[0].length > 0 && rep.nodeName !== sel[0].toUpperCase()) rep = null;
- res.push(rep);
- }else if(sel.indexOf('.') > -1){
- sel = sel.split('.');
- len = sel.length;
- res = fns.get('class',sel[1]);
- for(i = 2; i < len; i++){
- res = fns.eleFilter('class',sel[i],res);
- if(res.length === 0) break;
- }
- if(res.length > 0 && sel[0].length > 0){
- res = fns.eleFilter('tag',sel[0],res);
- }
- }else{
- res = fns.get('tag', sel);
- }
- return res;
- };
- function dfs(node, curNode, res, selectors, index){
- if(index === -1){
- if(curNode !== document && res.indexOf(node) === -1) res.push(node);
- return;
- }
- if(curNode === document) return;
- var sel = selectors[index];
- if(fns.eleValid(sel, curNode)){
- dfs(node, curNode.parentNode, res, selectors, index-1);
- }else{
- dfs(node, curNode.parentNode, res, selectors, index);
- }
- }
- function genFinalRes(input,selector){
- if(selector.length < 2) return input;
- var res = [];
- for(var i = 0; i < input.length; i++){
- var node = input[i];
- dfs(node, node.parentNode, res, selector,selector.length-2);
- }
- return res;
- }
- res = genFinalRes(getBySingleSelector(selector[selector.length-1]),selector);
- return res;
- };
复制代码
参考了https://code.tutsplus.com/tutorials/building-a-simple-css-selector-engine--net-16389
https://github.com/jquery/sizzle
|
|