zoukankan      html  css  js  c++  java
  • 简单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获取页面元素的操作,感觉就够用了,而不用总是依赖框架去实现一些页面的交互效果。

  • 相关阅读:
    EasyTransaction主要源码分析
    编程哲理小故事:Tina的运动会方阵
    多维扩展点的思考与设计——解决渠道、产品增加引发的腐化问题
    分布式事务框架Seata及EasyTransaction架构的比对思考
    设计,架构,框架之间是什么关系?
    你知道如何画好一幅架构图么?
    学会分享痛苦
    建立你自己的博客
    使用正确的工具软件
    掌握主动权
  • 原文地址:https://www.cnblogs.com/cyStyle/p/3080933.html
Copyright © 2011-2022 走看看