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

本文介绍了一种简易的CSS选择器实现方式,支持#id、.className、@name及tagName.className等多种格式,适用于小型项目中的DOM操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

简单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/archive/2013/05/15/3080933.html

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>CAD属性块分类树状视图</title> <style> /* 禁用整个页面的滚动条 */ html, body { margin: 0; padding: 0; height: 100%; width: 100%; overflow: hidden; font-family: "Helvetica Neue", Helvetica, Arial, "PingFang SC", "Hiragino Sans GB", "Heiti SC", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif; white-space: nowrap; } /* 主容器 - 固定宽度避免闪烁 */ .container { display: flex; flex-direction: column; height: 100vh; width: 100vw; max-width: 100vw; overflow: hidden; background-color: rgba(255, 255, 255, 0.9); position: relative; } /* 标题样式 */ .header { background: linear-gradient(to right, #2c3e50, #4a6491); color: white; padding: 15px 20px; text-align: center; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); } .header h1 { margin: 0; font-size: 24px; font-weight: 600; } /* 内容区域 */ .content { display: flex; flex: 1; overflow: hidden; flex-direction: column; flex-wrap: nowrap; justify-content: flex-start; } .icon { color: #f1c40f; padding-right: 3px; } /* 树状结构容器 */ .tree-container { flex: 1; background: rgb(59 68 83); color: white; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08); padding: 0px 5px 5px 5px; display: flex; flex-direction: column; overflow: hidden; } /* 树状结构样式 */ .tree { list-style: none; padding: 0; margin: 0; } .tree, .tree ul { list-style: none; padding-left: 25px; } .tree li { margin: 2px 0; position: relative; } .node { display: flex; cursor: pointer; transition: all 0.2s; background-color: rgb(0 0 0 / 35%); flex-direction: row; flex-wrap: nowrap; justify-content: flex-start; white-space: nowrap; font-size: 1rem; } .node:hover { background-color: #877739; } .toggle { width: 20px; height: 20px; margin-right: 10px; display: flex; align-items: center; justify-content: center; background-color: #4a6572; color: white; font-size: 14px; cursor: pointer; transition: all 0.2s; } .toggle:hover { background-color: #344955; } .node-name { flex: 1; color: white; } .node-count { padding: 2px 8px; border-radius: 10px; font-size: 12px; min-width: 30px; text-align: center; } /* 自定义滚动条 */ .tree-scroll { flex: 1; overflow-y: scroll; overflow-x: auto; padding-right: 5px; border: 1px solid rgb(0 0 0); box-shadow: 0 1px 1px rgb(59 63 89); } /* 自定义滚动条样式 */ .tree-scroll::-webkit-scrollbar { width: 0px; } /* 整个水平滚动条容器 */ .tree-scroll::-webkit-scrollbar:horizontal { height: 10px; } .tree-scroll::-webkit-scrollbar-track { background: #f1f1f1; border-radius: 1px; } /* 水平滚动条轨道 */ .tree-scroll::-webkit-scrollbar-track:horizontal { background: #f1f1f1; /* 轨道背景色 */ border-radius: 5px; /* 圆角 */ } .tree-scroll::-webkit-scrollbar-thumb { background: #a0a0a0; border-radius: 1px; } /* 水平滚动条滑块 */ .tree-scroll::-webkit-scrollbar-thumb:horizontal { border-radius: 5px; /* 滑块圆角 */ border: 1px solid #f1f1f1; /* 滑块边框 */ } .tree-scroll::-webkit-scrollbar-thumb:hover { background: #808080; } /* 鼠标悬停时滑块样式 */ .tree-scroll::-webkit-scrollbar-thumb:horizontal:hover { background: #808080; } /* 统计信息面板 */ .stats-panel header { font-size: 14px; color: white; border-bottom: 1px dashed rgb(0 0 0); } .stats-panel { width: auto; margin-left: 0; margin-top: 5px; border: 1px solid rgb(0 0 0); } .stat-item { font-size: 12px; display: flex; justify-content: space-between; border-bottom: 1px dashed rgb(0 0 0); flex-direction: row; flex-wrap: nowrap; text-align: center; } .stat-item:last-child { border-bottom: none; } .stat-label { flex: 1; color: #f1c40f; display: flex; flex-direction: row; flex-wrap: nowrap; text-align: center; justify-content: flex-start; align-items: center; white-space: nowrap; padding-left: 2px; border-right: 1px dashed rgb(0 0 0); } .stat-value { flex: 1; color: #4ec3af; display: flex; white-space: nowrap; flex-direction: row; flex-wrap: nowrap; text-align: center; justify-content: flex-end; align-items: center; padding-right: 2px; } .search-box { width: 10%; position: relative; margin-top: 2px; border-bottom: 1px solid rgb(0 0 0); } .search-box input { padding: 0px 15px 0px 5px; border-radius: 30px; font-size: 14px; outline: none; transition: all 0.3s; } .search-box input:focus { } .search-icon { position: absolute; left: 15px; top: 50%; transform: translateY(-50%); color: #7f8c8d; } /* 响应式设计 */ @media (max-width: 768px) { .content { flex-direction: column; } } </style> </head> <body> <div class="container"> <div class="content"> <div class="tree-container"> <div class="tree-scroll" id="treeScroll"> <ul class="tree" id="treeRoot"> <!-- 树状结构将通过JavaScript动态生成 --> </ul> </div> <div class="stats-panel"> <header>属性</header> <div class="stat-item"> <span class="stat-label">名称:</span> <span class="stat-value" id="att_tag1">气动隔膜阀</span> </div> <div class="stat-item"> <span class="stat-label">位号:</span> <span class="stat-value" id="att_tag2" color>B1TA4001AV101</span> </div> <div class="stat-item"> <span class="stat-label">设备描述:</span> <span class="stat-value" id="att_tag3"></span> </div> <div class="stat-item"> <span class="stat-label">材质:</span> <span class="stat-value" id="att_tag4">文本标注 (5)</span> </div> <div class="stat-item"> <span class="stat-label">介质名称(中):</span> <span class="stat-value" id="att_tag5">17.75</span> </div> <div class="stat-item"> <span class="stat-label">介质粘度:</span> <span class="stat-value" id="att_tag6">17.75</span> </div> <div class="stat-item"> <span class="stat-label">介质密度:</span> <span class="stat-value" id="att_tag7">17.75</span> </div> <div class="stat-item"> <span class="stat-label">DOCUMENTNO_1:</span> <span class="stat-value" id="att_tag8">17.75</span> </div> <div class="stat-item"> <span class="stat-label">连接类型:</span> <span class="stat-value" id="att_tag7">17.75</span> </div> </div> </div> </div> </div> <script> // CAD属性块数据 const cadData = { name: "所有属性块", count: 142, children: [ { name: "机械部件", count: 45, children: [ { name: "齿轮", count: 12 }, { name: "轴承", count: 18 }, { name: "联轴器", count: 8 }, { name: "紧固件", count: 7 } ] }, { name: "电气元件", count: 32, children: [ { name: "开关", count: 10 }, { name: "继电器", count: 8 }, { name: "变压器", count: 6 }, { name: "连接器", count: 8 } ] }, { name: "管道系统", count: 28, children: [ { name: "阀门", count: 9 }, { name: "法兰", count: 7 }, { name: "管接头", count: 8 }, { name: "过滤器", count: 4 } ] }, { name: "结构组件", count: 22, children: [ { name: "梁", count: 8 }, { name: "柱", count: 7 }, { name: "支架", count: 5 }, { name: "底座", count: 2 } ] }, { name: "注释符号", count: 15, children: [ { name: "尺寸标注", count: 6 }, { name: "表面粗糙度", count: 4 }, { name: "焊接符号", count: 3 }, { name: "形位公差", count: 2 } ] } ] }; // 搜索功能 function setupSearch() { const searchInput = document.getElementById('searchInput'); searchInput.addEventListener('input', function() { const searchTerm = this.value.toLowerCase().trim(); const allLeafNodes = document.querySelectorAll('.tree-scroll'); // 重置所有样式 allLeafNodes.forEach(node => { node.classList.remove('highlight'); node.style.display = ''; }); // 如果没有搜索词,显示所有节点 if (searchTerm === '') { // 展开所有节点 document.querySelectorAll('node').forEach(detail => { detail.open = false; }); return; } // 搜索匹配项并高亮显示 let found = false; allLeafNodes.forEach(node => { const itemName = node.querySelector('.item-name').textContent.toLowerCase(); if (itemName.includes(searchTerm)) { node.classList.add('highlight'); found = true; // 展开父节点 let parent = node.closest('details'); while (parent) { parent.open = true; parent = parent.parentElement.closest('details'); } } else { node.style.display = 'none'; } }); if (!found) { // 如果没有找到匹配项,显示所有节点 allLeafNodes.forEach(node => { node.style.display = ''; }); } }); } // 生成树状结构 function generateTree(data, parentElement) { const li = document.createElement('li'); const node = document.createElement('div'); node.className = 'node'; const toggle = document.createElement('div'); toggle.className = 'toggle'; toggle.textContent = '➕'; const icon = document.createElement('div'); icon.className = 'icon'; icon.textContent = '👮'; const nameSpan = document.createElement('span'); nameSpan.className = 'node-name'; nameSpan.textContent = data.name; const countSpan = document.createElement('span'); countSpan.className = 'node-count'; countSpan.textContent = data.count; node.appendChild(icon); node.appendChild(nameSpan); node.appendChild(countSpan); node.appendChild(toggle); li.appendChild(node); if (data.children && data.children.length > 0) { const childUl = document.createElement('ul'); data.children.forEach(child => { generateTree(child, childUl); }); li.appendChild(childUl); // 添加展开/折叠事件 toggle.addEventListener('click', function(e) { e.stopPropagation(); const isExpanded = childUl.style.display !== 'none'; if (isExpanded) { childUl.style.display = 'none'; toggle.textContent = '➕'; node.classList.remove('expanded'); } else { childUl.style.display = 'block'; toggle.textContent = '➖'; node.classList.add('expanded'); } }); // 初始折叠状态 childUl.style.display = 'none'; } else { toggle.style.visibility = 'hidden'; } parentElement.appendChild(li); } // 初始化树状视图 document.addEventListener('DOMContentLoaded', function() { const treeRoot = document.getElementById('treeRoot'); generateTree(cadData, treeRoot); }); </script> </body> </html> 帮我修改这个代码,要求在树容器顶部加一个搜索框,只要根据输入内容在树节点中搜到结果,就会展开对应节点且高亮显示搜索结果
最新发布
07-03
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值