• mass.query v3 发布


    我想query v2这种设计思路已到瓶颈了,把选择器群组切割成最小颗粒,然后在循环中从文档对象找到一个或多个节点,然后再到下一个循环找到它们的孩子或直接从它们当中筛选……为此付出的代价也非常多——颗粒越多,循环次数也越多,虽然我在每个分支不断让指针向后移。但这也太累了,有些颗粒需要合并了才有意义。不过最令人痛心的问题是,也是所有从左到右选择的选择器不得不面临问题是,如果出现并联、亲子、兄长、相邻等选择器,得到的元素集合就很有可能不是按文档顺序排列。因此我下一版将尝试从右到左。不过从右到左最大的问题就是后代选择器,这种不断往上回溯的算法非常难,这也是jQuery在处理关系选择器容易出现错的原因。

    其实无论是从左到右,还是从右到左,只要能把代码量压缩我都无怨言。因为很快就步入IE9的时代,querySelectorAll将支持更多的伪类(而且更有用)。因此这样人为的选择器只为向前兼容而存在,这样的代码我当然希望它占用我框架代码量的比重越少越好。我最近也想到几种方法,能让我的选择器跑得更快,但这也增加200多行代码,于是我不干了。

    有人提出20/80原则,但这意味着一些选择器跑不了,但我宁愿慢一点也不愿放弃支持。其实选择一个或多个元素速度根本可以忽略不计,性能都耗在处理节点的各种操作上。

    有人抱怨大循环的分支非常复杂,但明显易读性与执行效率往往不是一伙的,就像排序算法,越快的实现就越难看懂。这次把结构做好的,相对的,速度被拉下来了。

    我知道,需要新的思路了……

    /*
    query selector version 2.4
    Copyright 2010
    Dual licensed under the MIT or GPL Version 2 licenses.
    author "司徒正美(zhongqincheng)"
    http://www.cnblogs.com/rubylouvre/
     */
    (function(window,undefined){
        var parseNth = function (exp) {
            var match = reg_nth.exec(exp === "even" && "2n" || exp === "odd" && "2n+1" || !/\D/.test(exp) && "0n+" + exp || exp);
            return {
                a: (match[1] + (match[2] || 1)) - 0,
                b: match[3] - 0
            };
        }
        // "nth-child", "nth-last-child", "nth-of-type", "nth-last-of-type"
        var queryPseudoHasExp = function(start,next,flag_all){
            return {
                curry :function(lastResult,flag_not,getUID,name,exp){
                    if ( exp === "n" && name.indexOf("child") > 0) {
                        return flag_not ? [] : lastResult;
                    }
                    var result = [],ri = 0,i = 0, uniqResult = {},p = parseNth(exp),a = p.a, b= p.b, c,  el, uid, find,tagName, node,
                    flag_elem = "lastElementChild" in lastResult[0];
                    start = flag_elem ? start : start.replace("Element","");
                    next = flag_elem ? next : next.replace("Element","");
                    while((el = lastResult[i++])){
                        uid = el.uniqueID || getUID(el);
                        find = uniqResult[uid];
                        if (find === void 0) {
                            for ( c = 0, node = el.parentNode[start], tagName = el.nodeName;node; node = node[next])
                                if ((flag_elem || node.nodeType === 1) && (flag_all || node.nodeName === tagName)) {
                                    ++c;
                                    uniqResult[getUID(node)] = a === 0 ? c === b : (c - b) % a === 0 && (c - b) / a >= 0;
                                }
                            find = uniqResult[uid];
                        }
                        if (find ^ flag_not)
                            result[ri++] = el;
                    }
                    return result;
                }
            }
        }
    
        var queryPseudoNoExp = function(direction,flag_all){
            return {
                curry : function(lastResult,flag_not){
                    var result = [],ri = 0, i = 0,el,node,tagName,find,
                    flag_elem = "lastElementChild" in lastResult[0],
                    prop_prev = flag_elem ? prop_prev : "previousSibling",
                    prop_next = flag_elem ? prop_prev : "nextSibling"
                    while((el = lastResult[i++])){
                        tagName = flag_all || el.nodeName, find = null;
                        if (find === null && direction <= 0){
                            for (node = el[prop_prev]; node; node = node[prop_prev])
                                if ((flag_elem || node.nodeType === 1) && (flag_all || node.nodeName === tagName)) {
                                    find = false;
                                    break;
                                }
                        }
                        if (find === null && direction >= 0){
                            for (node = el[prop_next]; node; node = node[prop_next])
                                if ((flag_elem || node.nodeType === 1) && (flag_all || node.nodeName === tagName)) {
                                    find = false;
                                    break;
                                }
                        }
                        if (find === null)//如果本身就是first-child或last-child
                            find = true;
                        if (find ^ flag_not)//参与运算的两个值,如果两个相应bit位相同,则结果为0,否则为1。
                            result[ri++] = el;
                    }
                    return result;
                }
            }
        };
    
        var map_attr = {
            "accept-charset": "acceptCharset",
            accesskey: "accessKey",
            bgcolor: "bgColor",
            cellpadding: "cellPadding",
            cellspacing: "cellSpacing",
            "char": "ch",
            charoff: "chOff",
            "class": "className",
            codebase: "codeBase",
            codetype: "codeType",
            colspan: "colSpan",
            datetime: "dateTime",
            defaultchecked:"defaultChecked",
            defaultselected:"defaultSelected",
            defaultvalue:"defaultValue",
            "for": "htmlFor",
            frameborder: "frameBorder",
            "http-equiv": "httpEquiv",
            ismap: "isMap",
            longdesc: "longDesc",
            maxlength: "maxLength",
            margin"marginWidth",
            marginheight:'marginHeight',
            nohref: "noHref",
            noresize:"noResize",
            noshade: "noShade",
            readonly: "readOnly",
            rowspan: "rowSpan",
            tabindex: "tabIndex",
            usemap: "useMap",
            vspace: "vSpace",
            valuetype: "valueType"
        };
        var A_slice = Array.prototype.slice;
        window.dom = {
            UID:1,
            oneObject : function(array,val){
                var result = {},value = val !== void 0 ? val :1;
                for(var i=0,n=array.length;i < n;i++)
                    result[array[i]] = value;
                return result;
            },
            slice:function(nodes,start,end){
                return A_slice.call(nodes,(start || 0),(end || nodes.length))
            },
            isXML : function(doc){
                return (!!doc.xmlVersion) || (!!doc.xml) || (Object.prototype.toString.call(doc) === '[object XMLDocument]') ||
                (doc.nodeType === 9 && doc.documentElement.nodeName !== 'HTML');
            },
    
            queryId : function (id, root) {
                var el = (root || document).getElementById(id);
                return el && [el] || []
            },
    
            queryTag : function (tagName, parents, getUID) {
                var result = [], ri = 0, uniqResult = {},i , node, uid ,n = parents.length;
                switch (n) {
                    case 0:
                        return result;
                    case 1:
                        var nodes =  parents[0].getElementsByTagName(tagName);
                        return  this.slice(nodes)
                    default:
                        for (var k = 0 ; k < n ; k++) {
                            for (i = 0,nodes = parents[k].getElementsByTagName(tagName); node = nodes[i++];) {
                                if(dom.support.diffComment || node.nodeType === 1){
                                    uid = node.uniqueID || getUID(node);
                                    if (!uniqResult[uid]) {
                                        uniqResult[uid] = result[ri++] = node;
                                    }
                                }
                            }
                        }
                        return result;
                }
            },
            _filters : { //伪类选择器的过滤器
                enabled: function(el){//标准
                    return el.disabled === false && el.type !== "hidden";
                },
                disabled: function(el){//标准
                    return el.disabled === true;
                },
                checked: function(el){//标准
                    return el.checked === true;
                },
                indeterminate:function(el){//标准
                    return el.indeterminate = true && el.type === "checkbox"
                },
                selected: function(el){
                    el.parentNode.selectedIndex;//处理safari的bug
                    return el.selected === true;
                },
                empty: function (el) {//标准
                    return !el.firstChild;
                },
                lang: function (el, value) {//标准
                    var reg = new RegExp("^" + value, "i")
                    while (el && !el.getAttribute("lang"))
                        el = el.parentNode;
                    return  !!(el && reg.test(el.getAttribute("lang")));
                },
                header: function(el){
                    return /h\d/i.test( el.nodeName );
                },
                button: function(el){
                    return "button" === el.type || el.nodeName === "BUTTON";
                },
                input: function(el){
                    return /input|select|textarea|button/i.test(el.nodeName);
                },
                hidden : function( el ) {
                    return el.type === "hidden" || (el.offsetWidth === 0 ) || (!-[1,] && el.currentStyle.display === "none") ;
                },
                visible : function( el ) {
                    return el.type !== "hidden" && (el.offsetWidth || el.offsetHeight || (!-[1,] && el.currentStyle.display !== "none"));
                },
                target:function(el,exp,context){//标准
                    var id = context.location.hash.slice(1);
                    return (el.id || el.name) === id;
                },
                parent : function( el ) {
                    return !!el.firstChild;
                },
                contains: function(el, exp) {
                    return (el.textContent||el.innerText||'').indexOf(exp) !== -1
                },
    
                has: function( el,exp ) {
                    var a =  dom.query(exp, [el]).length;
                    return !!a
                },
    
                "first-child":      queryPseudoNoExp(-1, true),//标准
                "last-child":       queryPseudoNoExp( 1, true),//标准
                "only-child":       queryPseudoNoExp( 0, true),//标准
                "first-of-type":    queryPseudoNoExp(-1, false),//标准
                "last-of-type":     queryPseudoNoExp( 1, false),//标准
                "only-of-type":     queryPseudoNoExp( 0 ,false),//标准
                "nth-child":        queryPseudoHasExp("firstElementChild", "nextElementSibling",     true),//标准
                "nth-last-child":   queryPseudoHasExp("lastElementChild",  "previousElementSibling", true),//标准
                "nth-of-type":      queryPseudoHasExp("firstElementChild", "nextElementSibling",     false),//标准
                "nth-last-of-type": queryPseudoHasExp("lastElementChild",  "previousElementSibling", false),//标准
                //与位置相关的过滤器
                first: function(index){
                    return index === 0;
                },
                last: function(index, num){
                    return index === num;
                },
                even: function(index){
                    return (index & 1) === 0;
                },
                odd: function(index){
                    return (index & 1) === 1;
                },
                lt: function(index, num){
                    return index < num;
                },
                gt: function(index, num){
                    return index > num;
                },
                eq: function(index, num){
                    return index ===  num;
                },
                not:function(){}
            }
        }
        var reg_nth =  /(-?)(\d*)n([-+]?\d*)/;
        var reg_split = /(?:\(.*\)|[^,#:\.\s+>~[\](])+|[\.\[\]#:+>~,]|\s+/g
        var reg_id=  /^#([^,#:\.\s\xa0\u3000\+>~\[\(])+$/
        var reg_tag =  /[\w\u00c0-\uFFFF_-]+/;
        var reg_pseudo =  /^(\w[-\w]*)(\((.+)\))?$/
        var reg_href = /^(?:src|href|style)$/;
        var reg_blank = /\s*([>,\+\~=])\s*(?=(?:(?:[^"']*"[^"']*){2}|(?:[^"']*'[^"']*){2})*[^"']*$)/g;
        var reg_attribute = /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/ ;
        var one_position = dom.oneObject("eq|gt|lt|first|last|even|odd".split("|"));
        "text|radio|checkbox|file|password|submit|image|reset".replace(/\w+/g, function(name){
            dom._filters[name] = function(el){
                return el.type = name;
            }
        });
        var queryAttribute = function(el,name){
            var special = map_attr[name];
            if(special)
                return el[special];
            var flag = reg_href.test(name) ? 2 : 0;
            return el.getAttribute(name,flag) || el[name];
        };
        var documentOrder = !-[1,] ? function (a, b) {
            return (a.sourceIndex - b.sourceIndex);
        }:function (a, b) {
            return (3 - (a.compareDocumentPosition(b) & 6));
        }
    
        dom.support = {
            sliceNodes : true
        };
        var HTML = document.documentElement;
        var div = document.createElement("div");
        HTML.insertBefore(div, HTML.firstChild);
        var id = new Date - 0
        div.innerHTML = '<a name="'+id+'"></a><b id="'+id+'"></b>';
        dom.support.diffName = document.getElementById(id) !== div.firstChild;
        try{//检测是否支持
            A_slice.call(div.childNodes)
        }catch(e){
            dom.support.sliceNodes = false;
        }
        div.appendChild(document.createComment(''))
        dom.support.diffComment = div.getElementsByTagName("*").length !== 3;
        HTML.removeChild(div)
        if(!dom.support.diffName){
            //如果浏览器的getElementById不能区分name与id
            dom.queryId = function(id,root){
                root = root || document;
                if(root.getElementById){
                    var el = root.getElementById(id);
                    return el && el.attributes['id'].value === id ? [el] :[]
                } else {
                    var all = root.all[id];
                    for(var i=0;el=all[i++];){
                        if(el.attributes['id'].value === id)
                            return [el]
                    }
                    return []
                }
            }
        }
        if(!dom.support.sliceNodes){
            //如果浏览器的getElementById不能区分name与id
            dom.slice = function(nodes,start,end){
                var i = nodes.length,result = [];
                while(i--){
                    result[i] = nodes[i];
                }
                return  A_slice.call(result,(start || 0),(end || result.length));
            }
        }
    
        var getUIDHTML= function(node){
            return node.uniqueID || (node.uniqueID = dom.UID++);
        },
        getUIDXML = function(node){
            var uid = node.getAttribute("uniqueID");
            if (!uid){
                uid = dom.UID++;
                node.setAttribute("uniqueID", uid);
            }
            return uid;
        }
        //sss 为选择器群组,lastResult为undefined或纯数组
        dom.query = function(sss,lastResult,flag_not,flag_xml){
            if (typeof sss !== "string")
                return [];
            sss = sss.replace(/^[^#\(]*(#)/, "$1");
            lastResult = lastResult || [document];
            var match = lastResult[0],
            doc = match.nodeType === 9 ? match : (match.ownerDocument || match.document);
            flag_xml = flag_xml !== void 0 ? flag_xml : dom.isXML(doc),
            flag_not = !!flag_not;
            var result = [], ri = 0,  i = 0,flag_sort,flag_class,
            getUID = flag_xml? getUIDXML : getUIDHTML,
            flag_elem = "lastElementChild" in doc.documentElement ,
            prop = flag_elem ? "nextElementSibling" :"nextSibling",
            next,node,nodes,uniqResult,flag_all,uid,name,value;
            if(reg_id.test(sss))
                return dom.queryId(sss.slice(1),doc);
            if(/^\w+$/.test(sss)){
                return dom.queryTag(sss,lastResult,getUID);
            }
            if(doc.querySelectorAll && !flag_not){
                var flag_sqa = true;
                if(lastResult.length > 1)
                    flag_sort = true;
                for (; node = lastResult[i];i++ ){
                    node.id = node.id || node.uniqueID
                    if( node.nodeType === 1 && node.uniqueID )
                        sss = "#"+node.id+" "+ sss;
                    try {
                        result = result.concat(dom.slice(lastResult[i].querySelectorAll(sss)));
                    } catch (e) {
                        flag_sqa = false;
                    }finally{//IE8下querySelectorAll不在当前节点的孩子们中搜索
                        if (node.nodeType === 1 && node.uniqueID && node.id === node.uniqueID) {
                            node.removeAttribute( "id" );
                        }
                    }
                }
                if(flag_sqa){
                    return result
                }
            }
            sss = sss.replace(/^\s+|\s+$/g, '').replace(reg_blank,"$1").match(reg_split);
            var to_s = Object.prototype.toString,filter,tagName,not;
            for (var s = 0, ss ; ss = sss[s++];) {
                next = sss[s];
                uniqResult =  nodes = [];
                filter = flag_class = i = ri = 0
                switch(ss){
                    case "#"://★★★★(1)ID选择器
                        if(!flag_xml && !flag_not){//XML不支持getElementById
                            nodes = dom.queryId(next, doc);
                        }else{
                            filter = ["id", "=", next];
                        }
                        s++;
                        break;
                    case "."://★★★★(2)类选择器
                        s++;
                        if( lastResult.length === 1 && doc.getElementsByClassName && (!flag_not)){
                            nodes = dom.slice(lastResult[0].getElementsByClassName(next));
                        }else{
                            flag_class = true;
                            filter = ["class", "~=", next];
                        }
                        break;
                    case "["://★★★★(3)属性选择器
                        while(next!=="]"){
                            ss += next;
                            next = sss[++s];
                        }
                        ss += next;
                        match = ss.match(reg_attribute);
                        filter = [match[1], match[2], match[4]];
                        s++;
                        break;
                    case ","://★★★★(4)并联选择器
                        result = result.concat(lastResult);
                        lastResult = nodes = [doc];
                        flag_sort = true;
                        continue;
                    case ">"://★★★★关系选择器
                    case "+":
                    case "~":
                        tagName = "*"
                        if (next && reg_tag.test(next)) {
                            tagName = flag_xml ? next : next.toUpperCase();
                            s++;
                        }
                        flag_all = tagName === "*";
                        flag_sort = true;
                        var els,el,j
                        switch (ss) {
                            case ">"://★★★(5)亲子选择器
                                while((el = lastResult[i++])){
                                    els = el.children || el.childNodes;
                                    for(j = 0; node = els[j++];)
                                        if(node.nodeType === 1 &&(flag_all || tagName === node.nodeName))
                                            nodes[ri++] = node;
                                }
                                break;
                            case "+"://★★★(6)相邻选择器
                                while((node = lastResult[i++])){
                                    while((node = node[prop])){
                                        if(node.nodeType === 1){
                                            if (flag_all || tagName === node.nodeName)
                                                nodes[ri++] = node;
                                            break;
                                        }
                                    }
                                }
                                break;
                            case "~":// ★★★(7)兄长选择器
                                while((node = lastResult[i++])){
                                    while ((node = node[prop])){
                                        if(uniqResult[node.uniqueID] === node) break;
                                        if ( (flag_elem || node.nodeType === 1) && (flag_all || tagName === node.nodeName)) {
                                            uid = node.uniqueID || getUID(node);
                                            if(uniqResult[uid]){
                                                break;
                                            }else{
                                                nodes[ri++] = uniqResult[uid] = node;
                                            }
                                        }
                                    }
                                }
                        }
                        break;
                    case ":":
                        s++;
                        switch (next.slice(0,4)) {
                            case "scop"://直接查找不进行过滤
                                nodes = [doc];
                                break;
                            case "root"://直接查找不进行过滤
                                nodes = [doc.documentElement];
                                break;
                            case "link"://直接查找不进行过滤
                                match = doc.links;
                                if (match) {
                                    nodes = dom.slice(match);
                                    break
                                }
                            default:
                                match = next.match(reg_pseudo);
                                name = match[1],value = match[3], filter = dom._filters[name];
                        }
                        break;
                    default://处理(9)通配符选择器(10)标签选择器与(11)后代选择器
                        tagName = ss === " "? next : ss;
                        match = reg_tag.test(tagName);
                        if( tagName !== ss && match ){
                            s++;
                        }
                        tagName = match ? tagName : "*"
                        tagName = flag_xml ? tagName : tagName.toUpperCase();
                        nodes = (flag_not  && (!match) && sss[s] === void 0) ? [] : dom.queryTag(tagName, lastResult, getUID);
                        break;
                }
                if(filter && !nodes.length ){
                    not = flag_not && sss[s] === void 0;
                    lastResult = lastResult[0] !== doc ? lastResult : dom.queryTag("*", lastResult, getUID);
                    switch(to_s.call(filter||"")){
                        case "[object Function]":
                            if(one_position[name]){  //处理位置伪类
                                if(flag_sort)
                                    lastResult = lastResult.sort(documentOrder);
                                //如果exp为空白则将集合的最大索引值传进去,否则将exp转换为数字
                                value = (value === ""|| value === void 0) ? lastResult.length - 1 : ~~value;
                                for (; node = lastResult[i];){
                                    if(filter(i++, value) ^ not )
                                        nodes[ri++] = node;
                                }
                            }else if(name==="not"){
                                nodes = dom.query(value,lastResult,true,flag_xml);
                            }else{
                                //处理target root checked disabled empty enabled lang 等伪类
                                for (; node = lastResult[i++];){
                                    if(!!filter(node, value, doc) ^ not)
                                        nodes[ri++] = node;
                                }
                            }
                            break;
                        case "[object Object]":
                            //处理结构伪类中的子元素过滤伪类
                            nodes = filter.curry(lastResult, not, getUID, name, value);
                        case "[object Array]":  //处理属性伪类
                            //处理属性伪类
                            var operator = filter[1], value = filter[2], attrib, flag;
                            for (; node = lastResult[i++];){
                                attrib = flag_class? node.className : queryAttribute(node, filter[0]);//取得元素的实际属性值
                                flag = (attrib != null) && (attrib !== "");
                                if(flag && operator){
                                    switch (operator) {
                                        case "=":
                                            flag = attrib === value;
                                            break;
                                        case "!=":
                                            flag = attrib !== value;
                                            break;
                                        case "~=":
                                            flag = (" " + attrib + " ").indexOf(value) !== -1;
                                            break;
                                        case "^=":
                                            flag = attrib.indexOf(value) === 0;
                                            break;
                                        case "$=":
                                            flag = attrib.lastIndexOf(value) + value.length === attrib.length;
                                            break;
                                        case "*=":
                                            flag = attrib.indexOf(value) !== -1;
                                            break;
                                        case  "|=":
                                            flag = attrib === value || attrib.substring(0, value.length + 1) === value + "-";
                                            break;
                                    }
                                }
                                if (!!flag  ^ not ){
                                    nodes[ri++] = node;
                                }
                            }
                    }
                }//结束过滤
                lastResult = nodes;
                if(!lastResult.length ) {
                    break;
                }
            }
            result = result.concat(lastResult);
            if(result.length > 1 && flag_sort){
                i=ri=0,uniqResult = {},nodes= [];
                for(;node = result[i++];){
                    uid = node.uniqueID || getUID(node);
                    if (uniqResult[uid]){
                        break;
                    }else {
                        uniqResult[uid] = nodes[ri++] = node;
                    }
                }
                result = nodes.sort(documentOrder);
            }
            return result;
        }
    
    })(window);
    
  • 相关阅读:
    break-continue
    函数定义
    函数类型
    为何要继承SpringBootServletInitializer,为何要实现configure这方法
    查询一个表中的两个字段值相同的数据
    数据库中查出来的时间多8小时&查询数据正常展示少8小时
    @JsonFormat与@DateTimeFormat注解的使用
    用js获取当前月份的天数
    js获取当前年,月,日,时,分,秒
    maven配置和安装
  • 原文地址:https://www.cnblogs.com/rubylouvre/p/1896916.html
Copyright © 2020-2023  润新知