简单css选择器实现支持#id、.className、@name、tagName.className、node节点五种格式

简单css选择器的实现

前段时间阅读过jQuery的一部分源码,其中对于选择器(selector)的实现部分,感觉看下来比较吃力,于是就根据平时封装和收集的一些API,特别是dom操作部分的,自己实现了一个简单的css选择器,支持#id、.className、@name、tagName.className、node节点五种格式获取页面的元素或元素集合。

废话不多说,贴上js代码:

// dom操作对象
var dom = {
    // 检测window 
    isWindow: function(obj) {
        return obj && typeof obj === 'object' && 'setInterval' in obj;
    },
    // 是否是节点
    isNode: function(obj) {
        return !!(obj && obj.nodeType);
    },
    // 是否是节点列表
    isNodeList: function(obj) {
        // xxx类型调用toString()方法返回[object xxx]
        return !!(obj && (obj.toString() === '[object NodeList]' || 
            obj.toString() === '[object HTMLCollection]'));
    },
    // 根据id获取元素
    getElemByID: function(id) {
        return typeof id === 'string' ? document.getElementById(id) : id;
    },
    // 根据tagName获取元素集合
    getElemsByTagName: function(tagName, root) {
        var elems = (root || document).getElementsByTagName(tagName);
        return elems !== null && elems.length ? elems : null;
    },
    // 根据className获取元素(返回的是一个HTMLCollection)
    getElemsByClassName: function(className, root, tagName) {
        root = root || document; // 没有传入根节点,则默认为document
        tagName = tagName || '*'; // 没有传入标签,则默认获得所有标签
        var i = 0,
            classElements = [],
            elements = root.getElementsByTagName(tagName),
            elementsLen = elements.length;
            pattern = new RegExp('(^|\\s)' + className + '(\\s|$)'); // className为要搜索的参数
        // 遍历所有元素,如果匹配到传入的className,则把对应的元素添加到数组中
        for (; i < elementsLen; i++) {
            if (pattern.test(elements[i].className)) {
                classElements.push(elements[i]);
            }
        }
        return classElements; // 返回匹配的元素集合
    },
    // 根据name获取元素集合
    getElemsByName: function(name) {
        var i, 
            elems = document.getElementsByName(name), // 原生方法(注意浏览器支持情况)
            elemsLen,
            arr = [];
        return elems !== null && elems.length ? elems : null; // 返回HTMLCollection
    },
    // 判断样式类是否存在
    hasClass: function(elem, className) {
        var reg = new RegExp("^|\\s" + className + "\\s|$");
        return reg.test(elem.className) ? true : false;
    }
};

/**
 * 简单css选择器 支持#id,.className,@formName,还有tagName.className,node节点五种格式
 * @param {String || Object}
 * @param {Element} [root] 可选,从哪个根节点查找
 * @return {object || HTMLCollection} 单个元素或元素集合
 */
var $ = function(selector, root) {
    // 重用变量
    var    i,
        elems,
        elemsLen,
        matchAry = [];

    // 如果选择器selector为空,则终止执行。
    if (selector === undefined) {
        return; 
    // 如果选择器为节点或者为节点列表,则直接返回。
    } else if (dom.isNode(selector) || dom.isNodeList(selector)) {
        return selector;
    // 如果选择器为window对象,则直接返回。
    } else if (dom.isWindow(selector)) {
        return selector;
    }

    var selector = selector.toString();
    // 匹配"#id"的情况
    if (selector.indexOf('#') === 0) { 
        return dom.getElemByID(selector.substring(1));

    // 匹配".className"的情况
    } else if (selector.indexOf('.') === 0) {
        elems = dom.getElemsByClassName(selector.substring(1), root);

        if (elems.constructor && elems.constructor.toString().indexOf('Array') > -1) { // 判断elems为数组对象
            matchAry = elems;
        } else {
            for (i = 0, elemsLen = elems.length; i < elemsLen; i++) {
                matchAry.push(elems[i]);
            }
        }

        return matchAry; // 返回匹配的元素集合

    // 匹配"@name"的情况
    } else if (selector.indexOf('@') === 0) {
        elems = dom.getElemsByName(selector.substring(1));
        
        for (i = 0, elemsLen = elems.length; i < elemsLen; i++) {
            matchAry.push(elems[i]);
        }

        return matchAry; // 返回匹配的元素集合

    // 匹配"tagName.className"的情况
    } else {
        if (selector.indexOf('.') > 0 && selector.indexOf('.') < selector.length) {
            // 根据tagName获取元素集合
            elems = dom.getElemsByTagName(selector.substring(0, selector.indexOf('.')), root);
            var    className = selector.substr(selector.indexOf('.') + 1);
            
            for (i = 0, elemsLen = elems.length; i < elemsLen; i++) {
                // 如果元素匹配到className,则把该元素添加到数组matchAry中
                if (dom.hasClass(elems[i], className)) {
                    matchAry.push(elems[i]);
                }
            }        
        } else { // 否者如果没有查找到".",则调用getElemsByTagName方法
            matchAry = dom.getElemsByTagName(selector, root);
        }

        return matchAry; // 返回匹配的元素集合
    }
};

demo实例截图:

firebug下测试结果截图:

结语:虽然该css选择器与jQuery的实现比起来是不堪一击的,尤其是在实现技巧、性能方面。但在平时一些小项目或效果的实现中,对于js获取页面元素的操作,感觉就够用了,而不用总是依赖框架去实现一些页面的交互效果。

原文地址:https://www.cnblogs.com/cyStyle/p/3080933.html