js实现完整的css3选择器.

此代码效果基本等同"selectorAPI"
if (!String.prototype.trim)
    String.prototype.trim = function() {
        return this.replace(/^\s+|\s+$/g, '');
    };

if (!Array.prototype.indexOf)
    Array.prototype.indexOf = function(searchElement /*, fromIndex */) {
        var n, k, t = Object(this), len = t.length >>> 0;
        if (len === 0) return -1;
        n = 0;
        if (arguments.length > 1) {
            n = Number(arguments[1]);
            if (n != n) {
                n = 0;
            } else if (n != 0 && n != Infinity && n != -Infinity) {
                n = (n > 0 || -1) * Math.floor(Math.abs(n));
            }
        }
        if (n >= len) return -1;
        for (k = n >= 0 ? n : Math.max(len - Math.abs(n), 0); k < len; k++)
            if (k in t && t[k] === searchElement) return k;
        return -1;
    };

if (!Array.prototype.map) {
    Array.prototype.map = function(callback, thisArg) {
        var T, A, k;
        var O = Object(this);
        var len = O.length >>> 0;
        if (thisArg) T = thisArg;
        A = new Array(len);
        k = 0;
        while (k < len) {
            var kValue, mappedValue;
            if (k in O) {
                kValue = O[k];
                mappedValue = callback.call(T, kValue, k, O);
                A[ k ] = mappedValue;
            }
            k++;
        }
        return A;
    };
}





window.joop = joop = (function() {
    var fn = {
        extend: function(destination) {
            for (var i = 1, length = arguments.length; i < length; i++)
                for (var property in arguments[i]) destination[property] = arguments[i][property];
            return destination;
        },
        isFunction: function(object) {
            return typeof object === "function";
        },
        isString: function(object) {
            return typeof object === "string";
        },
        isNumber: function(object) {
            return typeof object === "number";
        },
        toArray: function(iterable) {
            if (!iterable) return new Array;
            if ("toArray" in iterable) return iterable.toArray();
            var length = iterable.length || 0, results = new Array(length);
            while (length--) results[length] = iterable[length];
            return results;
        },
        unique: function(array) {
            for (var i = 0, results = [], length = array.length; i < length; i++)
                if (results.indexOf(array[i]) === -1) results.push(array[i]);
            return results;
        }
    };
    var ready = (function(arg) {
        var queue = [], loaded = 0, onload = window.onload || function() {
        };
        function init() {
            if (loaded) return;
            loaded = 1;
            for (var i = 0, length = queue.length; i < length; i++) queue[i](arg);
        }
        function delay() {
            /loaded|complete/.test(document.readyState) ? init() : window.setTimeout(delay, 0);
        }
        if (window.attachEvent) window.attachEvent("onload", init);
        if (window.addEventListener) window.addEventListener("load", init, false);
        if (document.addEventListener) document.addEventListener("DOMContentLoaded", init, false);
        window.onload = function() {
            onload();
            init();
        };
        delay();
        return function(fn) {
            loaded ? fn(arg) : queue.push(fn);
        };
    })(entry);
    var cssSelector = (function(document) {
        "use strict";
        var pattern = {grou: "\\s*(,)\\s*", rela: "\\s*([\\s\\+>~])\\s*", elem: "(\\*|[\\.#]?[\\w-]+)", attr: "(\\[)\\s*([\\w-]+)\\s*(?:([~\\^\\$\\*\\|]?=)\\s*(['\"]?)(.*?)(\\4)\\s*)?(\\])"};
        pattern.pseu = "(:{1,2}[\\w-]+)(?:\\(([\\s\\w\\+-]+|(?:" + pattern.elem + '|' + pattern.attr.replace("\\4", "\\7") + "|pseu)+)\\))?";
        pattern.pseu = pattern.pseu.replace("|pseu", '|' + pattern.pseu.replace("|pseu", '').replace("\\7", "\\17"));
        pattern.all = (pattern.grou + '|' + pattern.rela + '|' + pattern.elem + '|' + pattern.attr + '|' + pattern.pseu).replace("\\17", "\\27").replace("\\7", "\\17").replace("\\4", "\\7");
        var regexp = {
            all: new RegExp(pattern.all, "gim"),
            grou: new RegExp('^' + pattern.grou + '$', "im"),
            elem: new RegExp('^' + pattern.elem + '$', "im"),
            rela: new RegExp('^' + pattern.rela + '$', "im"),
            attr: new RegExp('^' + pattern.attr + '$', "im"),
            pseu: new RegExp('^' + pattern.pseu + '$', "im")
        };
        function parse(selectors) {
            var chains = [[]], index = 0, result;
            var chain = selectors.trim().match(regexp.all);
            function chainRel(rel, tag) {
                chains[index].push(["rel", rel, tag]);
            }
            function chainEle(tag) {
                if (!chains[index].length) chainRel(' ', tag);
                chains[index][chains[index].length - 1][2] = chains[index][chains[index].length - 1][2] || tag;
            }
            function chainAtt(att, exp, val) {
                if (!chains[index].length) chainEle('*');
                if (chains[index][chains[index].length - 1][0] === "rel") chainEle('*');
                chains[index].push(["att", att, exp, val]);
            }
            function chainPse(name, val) {
                if (!chains[index].length) chainEle('*');
                if (chains[index][chains[index].length - 1][0] === "rel") chainEle('*');
                chains[index].push(["pse", name, val]);
            }
            function chainNew() {
                chains.push([]);
                index++;
            }
            for (var i = 0; i < chain.length; i++) {
                if (regexp.grou.test(chain[i])) {
                    chainNew();
                } else if (regexp.elem.test(chain[i])) {
                    switch (chain[i].charAt(0)) {
                        case '.':
                            chainAtt("class", "~=", chain[i].substr(1));
                            break;
                        case '#':
                            chainAtt("id", '=', chain[i].substr(1));
                            break;
                        default:
                            chainEle(chain[i]);
                    }
                } else if (!!(result = regexp.rela.exec(chain[i]))) {
                    chainRel(result[1]);
                } else if (!!(result = regexp.attr.exec(chain[i]))) {
                    chainAtt(result[2], result[3], result[5]);
                } else if (!!(result = regexp.pseu.exec(chain[i]))) {
                    chainPse(result[1], result[2]);
                } else {
                    throw "Unrecognized at-rule or error parsing at-rule '" + chain[i] + "'.";
                }
            }
            return chains;
        }
        function query(elements, chain) {
            for (var i = 0; i < chain.length; i++) {
                switch (chain[i][0]) {
                    case "rel":
                        elements = getByRel(elements, chain[i][1], chain[i][2]);
                        break;
                    case "att":
                        elements = getByAtt(elements, chain[i][1], chain[i][2], chain[i][3]);
                        break;
                    case "pse":
                        elements = getByPse(elements, chain[i][1], chain[i][2]);
                        break;
                    default:
                        throw "Selector expected.  Ruleset ignored due to bad selector.";
                }
            }
            return elements;
        }
        function getByRel(elements, rel, tag) {
            for (var i = 0, lctag = tag.toLowerCase(), results = []; i < elements.length; i++) {
                var element = elements[i];
                switch (rel) {
                    case ' ':
                        results = results.concat(fn.toArray(element.getElementsByTagName(tag)));
                        break;
                    case '>':
                        for (var j = 0; j < element.children.length; j++)
                            if (lctag === '*' || (element.children[j].tagName.toLowerCase() === lctag))
                                results.push(element.children[j]);
                        break;
                    case '+':
                    case '~':
                        element = element.nextSibling;
                        do {
                            if (element && element.tagName && element.tagName.toLowerCase() === lctag)
                                results.push(element);
                        } while (element && (rel === '+' ? !element.tagName : true) && !!(element = element.nextSibling));
                        break;
                    default:
                        if (element.tagName.toLowerCase() === lctag) results.push(element);
                }
            }
            return results;
        }
        function getByAtt(elements, att, exp, val) {
            for (var i = 0, results = []; i < elements.length; i++) {
                var element = elements[i], value = element.getAttribute(att);
                switch (exp) {
                    case '=':
                        if (val === value) results.push(element);
                        break;
                    case "~=":
                        if (new RegExp("(^|\\s)" + val + "(\\s|$)").test(value)) results.push(element);
                        break;
                    case "^=":
                        if (new RegExp('^' + val).test(value)) results.push(element);
                        break;
                    case "$=":
                        if (new RegExp(val + '$').test(value)) results.push(element);
                        break;
                    case "*=":
                        if (new RegExp(val).test(value)) results.push(element);
                        break;
                    case "|=":
                        if (new RegExp("(^|-)" + val + "(-|$)").test(value)) results.push(element);
                        break;
                    default:
                        if (value !== null) results.push(element);
                }
            }
            return results;
        }
        function getByPse(elements, name, arg) {
            for (var i = 0, results = []; i < elements.length; i++) {
                var element = elements[i], parent = element.parentNode, children = parent.children;
                switch (name) {
                    case ":checked":
                        if (element.checked) results.push(element);
                        break;
                    case ":disabled":
                        if (element.disabled) results.push(element);
                        break;
                    case ":empty":
                        if (!element.childNodes.length) results.push(element);
                        break;
                    case ":enabled":
                        if (!element.disabled) results.push(element);
                        break;
                    case ":first-child":
                        if (children[0] === element) results.push(element);
                        break;
                    case ":first-of-type":
                        for (var j = 0; j < children.length; j++) {
                            if (children[j].tagName === element.tagName) {
                                if (children[j] === element) results.push(element);
                                break;
                            }
                        }
                        break;
                    case ":lang":
                        if (new RegExp("(^|-)" + arg + "(-|$)").test(element.lang)) results.push(element);
                        break;
                    case ":last-child":
                        if (children[children.length - 1] === element) results.push(element);
                        break;
                    case ":last-of-type":
                        for (var j = children.length; j > 0; j--) {
                            if (children[j - 1].tagName === element.tagName) {
                                if (children[j - 1] === element) results.push(element);
                                break;
                            }
                        }
                        break;
                    case ":not":
                        if (!query([element], simchain(arg)).length) results.push(element);
                        break;
                    case ":nth-child":
                        for (var j = 0; j < children.length; j++) {
                            if (children[j] === element) {
                                if (comparen(arg, j + 1)) results.push(element);
                                break;
                            }
                        }
                        break;
                    case ":nth-last-child":
                        for (var j = children.length; j > 0; j--) {
                            if (children[j - 1] === element) {
                                if (comparen(arg, children.length - j + 1)) results.push(element);
                                break;
                            }
                        }
                        break;
                    case ":nth-last-of-type":
                        for (var j = children.length, k = 0; j > 0; j--) {
                            if (children[j - 1].tagName === element.tagName) k++;
                            if (children[j - 1] === element) {
                                if (comparen(arg, k)) results.push(element);
                                break;
                            }
                        }
                        break;
                    case ":nth-of-type":
                        for (var j = 0, k = 0; j < children.length; j++) {
                            if (children[j].tagName === element.tagName) k++;
                            if (children[j] === element) {
                                if (comparen(arg, k)) results.push(element);
                                break;
                            }
                        }
                        break;
                    case ":only-child":
                        if (children.length === 1) results.push(element);
                        break;
                    case ":only-of-type":
                        for (var j = 0, k = 0; j < children.length; j++)
                            if (children[j].tagName === element.tagName) k++;
                        if (k === 1) results.push(element);
                        break;
                    case ":root":
                        if (element === document.documentElement) results.push(element);
                        break;
                    case ":target":
                        if (window.location && window.location.hash === '#' + element.id)
                            results.push(element);
                        break;
                    default:
                        throw "Don't support this pseudo-class: " + name;
                }
            }
            return results;
        }
        function simchain(str) {
            var chain = parse(str)[0];
            if (chain[0][2] === '*') {
                chain.shift();
            } else {
                chain[0][1] = '';
            }
            return chain;
        }
        function comparen(exp, val) {
            if (!isNaN(exp)) return parseInt(exp) === val;
            if (exp === "odd") return val % 2 === 1;
            if (exp === "even") return val % 2 === 0;
            if (/\d+n/.test(exp)) {
                var formula = exp.replace(/(\d+)n/, "$1*n"), pv, nv, n = 0;
                do {
                    pv = eval(formula.replace('n', n++));
                    nv = eval(formula.replace('n', n));
                    if (pv === val) return true;
                } while (pv !== val && (pv < nv ? pv < val : pv > val));
            }
        }
        return function(element, selectors) {
            var elements = new Array().concat(arguments.length === 1 ? document : element);
            for (var i = 0, results = [], chains = parse(arguments.length === 1 ? element : selectors); i < chains.length; i++)
                results = results.concat(query(elements, chains[i]));
            return fn.unique(results);
        };
    })(document);
    function namespace(ns) {
        var parts = ns.split('.'), obj = window;
        for (var i = 0; i < parts.length; i++)
            obj = obj[parts[i]] = obj[parts[i]] || {};
        return obj;
    }
    function jclass() {
        var bclass, bproto = {}, proto = {initialize: function() {
            }};
        function nclass() {
            this.initialize.apply(this, arguments);
        }
        for (var i = 0, length = arguments.length; i < length; i++) {
            var methods = arguments[i];
            if (fn.isFunction(methods)) {
                bclass = function() {
                };
                bclass.prototype = methods.prototype;
                methods = new bclass;
                fn.extend(bproto, methods);
            }
            fn.extend(proto, methods);
        }
        nclass.prototype = proto;
        nclass.parent = bproto;
        return nclass;
    }
    function wrapper(element) {
        if (!element || isFunction(element) || element._element || element.initialize)
            return element;
        var wrapClass, object;
        if (element === window) {
            wrapClass = Element.Window;
        } else if (element === document) {
            wrapClass = Element.Document;
        } else if (element.nodeType !== 1) {
            return element;
        } else {
            wrapClass = Element['tag' + element.tagName.toUpperCase()] || Element;
        }
        wrapper.cache = wrapper.cache || [];
        for (var i = 0, length = wrapper.cache.length; i < length; i++)
            if (wrapper.cache[i]._element === element)
                return wrapper.cache[i];
        object = new wrapClass(element);
        wrapper.cache.push(object);
        return object;
    }
    function getById() {
        if (arguments.length > 1) {
            for (var i = 0, elements = [], length = arguments.length; i < length; i++)
                elements.push(getById(arguments[i]));
            return elements;
        }
        return wrapper(isString(arguments[0]) ? document.getElementById(arguments[0]) : arguments[0]);
    }
    function getBySelector(selectors) {
        return cssSelector(selectors);
    }
    function entry(arg) {
        if (arguments.length > 1) {
            for (var i = 0, elements = [], length = arguments.length; i < length; i++)
                elements.push(entry(arguments[i]));
            return elements;
        }
        return fn.isFunction(arg) ? ready(arg) : getById(arg);
    }
    return fn.extend(entry, {
        version: 20131102,
        fn: fn,
        ready: ready,
        namespace: namespace,
        jclass: jclass,
        wrapper: wrapper,
        getById: getById,
        getBySelector: getBySelector,
        cssSelector: cssSelector
    });
})();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值