虛擬dom節點,支持querySelector,css
方法:html
hasQuerySelector 輸入css選擇器,判斷是否選中dom
querySelectorToHtml 輸入css選擇器,輸出命中的html
querySelector 輸入css選擇器,輸出bitmap數據
HtmlNode.js
//HtmlNode.js const Api=require('./Api'); const compiler = require('vue-template-compiler'); //命中規則 /*css rule矩陣, 行對應selector '.id', 列對應html節點 ['body','body div','body div div','body div p','body div span','body div span a'] [ [0,0,0,0,1,0], ] */ class HtmlNode{ constructor(htmlText){ let htmlAst=htmlText==='string'?compiler.compile(htmlText).ast:htmlText; //記錄selector查找歷史 this.selectotCache={}; //構建html語法樹和矩陣bitmap this.htmlAst=htmlAst; this.htmlList=this.getAllChild(this.htmlAst); } //選擇器是否命中dom節點 hasQuerySelector(selector){ return this.querySelector(selector).some(function (item) { return item===1 }) } //根據selector獲取html文本 querySelectorToHtml(selector){ const selectorArr=this.querySelector(selector); let html=''; for(let i=0;i<selectorArr.length;i++){ if(selectorArr[i]===1){ html+=Api.AstToHtml(this.htmlList[i]) } } return html; } //獲取選擇器和它得子元素 queryAllSelector(selector){ const arr=this.querySelector(selector); for(let i=0;i<arr.length;i++){ if(arr[i]===1){ const cLen=this.getAllChild(this.htmlList[arr[i]]).length; for(let k=1;k<cLen;k++){ i++; arr[i]=1; } } } return arr; } //遞歸獲取全部當前元素、子元素 getAllChild(nodeAst){ return Api.depthSearch(nodeAst).filter(function (node) { return node.type===1; }) } //多是多選擇器 querySelector(selector){ if(/,/.test(selector)){ const arr=selector.split(','); const data=[]; for(let i=0;i<arr.length;i++){ const item=this.queryOneSelector(arr[i]); for(let k=0;k<item.length;k++){ if(item[k]===1){ data[k]=1; }else{ data[k]=0; } } } return data; }else{ return this.queryOneSelector(selector) } } //查詢css_rule,返回[array astNode] queryOneSelector(selector){ selector=selector.trim();//去掉左右空格 //解析css rule const selectorArr=[] selector.replace(/(.+?)([ >~\+]+(?!\d)(?! *:)|$)/ig,function (m,p1,p2) { selectorArr.push(p1,p2); }) // console.log(selectorArr) this.selectorArr=selectorArr; // console.log(selectorArr) //設置緩存 let preSelector=''; for(let i=0;i<selectorArr.length;i=i+2){ const exec=selectorArr[i-1]||''; const curSelector=selectorArr[i]; this.setSelectotCache(preSelector,exec,curSelector); preSelector=preSelector+exec+curSelector } const arr=new Array(this.htmlList.length).fill(0); // if(/ ::/.test(selector)) // console.log(selector,selectorArr) this.selectotCache[selector].forEach( (node) =>{ arr[this.htmlList.indexOf(node)]=1; }) return arr; } //記錄selector查詢html語法樹 setSelectotCache(preSelector,exec,curSelector){ const nextSelector=preSelector+exec+curSelector; //已有緩存 if(this.selectotCache[nextSelector]){return;} if(!preSelector&&!exec){ this.selectotCache[curSelector]=this.breadthHit(curSelector,this.htmlAst) return; } const arr=this.selectotCache[preSelector]; this.selectotCache[nextSelector]=[]; if(/^ +$/.test(exec)){ arr.forEach((node)=>{ this.selectotCache[nextSelector]=this.selectotCache[nextSelector].concat(this.breadthHit(curSelector,node)); }) }else if(/^ *> *$/.test(exec)){ arr.forEach((node)=>{ this.selectotCache[nextSelector]=this.selectotCache[nextSelector].concat(this.childHit(curSelector,node)); }) }else if(/^ *\+ *$/.test(exec)){ arr.forEach((node)=>{ this.selectotCache[nextSelector]=this.selectotCache[nextSelector].concat(this.sublingHit(curSelector,node)); }) }else if(/^ *~ *$/.test(exec)){ arr.forEach((node)=>{ this.selectotCache[nextSelector]=this.selectotCache[nextSelector].concat(this.sublingsHit(curSelector,node)); }) }else{ console.log('exec異常:'+exec) } } //css_rule:element+element sublingHit(tag,astNode){ if(!astNode.parent){ return [astNode].filter( (node) =>{ return this.hitNode(tag,node); }) } return Api.nextSublingSearch(astNode,astNode.parent).filter( (node) =>{ return this.hitNode(tag,node); }) } //css_rule:element~element sublingsHit(tag,astNode){ return Api.nextSublingsSearch(astNode,astNode.parent).filter(function (node) { return this.hitNode(tag,node); }) } //css_rule:element element breadthHit(tag,astNode){ return Api.breadthSearch(astNode).filter( (node)=> { return node.type===1&&this.hitNode(tag,node); }) } //css_rule:element>element childHit(tag,astNode){ return Api.childSearch(astNode).filter( (node)=> { return node.type===1&&this.hitNode(tag,node); }) } //tag是否命中ast節點,返回true、false hitNode(selector,astNode) { //分割字符串 (tag)、(id、class)(val) if(selector==='*'){ return true; }else if(/:root/.test(selector)){ return astNode.tag==='html'; }else{ const arr=[]; //tag if(/(^[a-z]+)/i.test(selector)){ const tag=RegExp.$1; arr.push(astNode.tag===tag) } //class if(/\.([\w-]+)/.test(selector)){ const val=RegExp.$1; arr.push(astNode.attrsMap.class&&astNode.attrsMap.class.split(' ').indexOf(val)>-1); } //id if(/#(\w+)/.test(selector)){ const val=RegExp.$1; arr.push(astNode.attrsMap.id===val); } //屬性 if(/\[([\w-]+)(~=|=||=)?(\w+)?\]/.test(selector)){ const key=RegExp.$1; const exec=RegExp.$2; const val=RegExp.$3; // console.log(selector,'屬性選擇器,只判斷是否存在屬性') arr.push(astNode.attrsMap.hasOwnProperty(key)); } //僞類選擇器 if(/(\:.+)/.test(selector)){ const key=RegExp.$1; // console.log(selector,'解析->',selector.replace(/\:.+$/,'')) arr.push(true) // arr.push(astNode.attrsMap.id===val); } if(arr.length==0){ // console.log(this.selectorArr) console.log(selector,this.selectorArr,'css 解析異常') } return arr.every((item)=>item); } } } module.exports=HtmlNode;
Api.jsvue
//Api.js const treeSearch=require('./treeSearch'); const AstToHtml = require("./AstToHtml"); //遍歷子節點 function childSearch(node,childProp='children'){ return node[childProp]; } //遍歷兄弟節點 function nextSublingsSearch(node,pnode,childProp='children'){ const parr=pnode[childProp].filter((node)=>{ return node.type===1 }); return parr.slice(parr.indexOf(node)+1); } //遍歷下一個兄弟節點 function nextSublingSearch(node,pnode,childProp='children'){ return nextSublingsSearch(node,pnode).slice(0,1); } module.exports={ AstToHtml, childSearch, nextSublingsSearch, nextSublingSearch, ...treeSearch }
AstToHtml.jsnode
//html語法樹節點類型 const typeMap= { '1':function (node) { if(node.attrs&&node.attrs.length>0){ node.attrs.forEach(function (item) { item.type='attrs' }) } return [ '<', node.tag, node.staticClass?[' class=',node.staticClass]:'', node.attrs&&node.attrs.length>0?[' ',joinSymbol(node.attrs,' ')]:'', '>', node.children, '</', node.tag, '>', ]; }, attrs:function (node) { return [node.name,'=',node.value] }, '3':function (node) { return node.text; }, } //語法樹轉string function AstChildToString(children) { let str=''; children.forEach(function (node) { str+=AstToHtml(node) }) return str; } //元素之間添加符號 function joinSymbol(oriArr,symbol,pre) { if(oriArr.length===0){return '';} const arr=[]; if(pre){ arr.push(pre) } oriArr.forEach(function (node,i) { arr.push(node); if(i<oriArr.length-1){ arr.push(symbol); } }) if(pre){ arr.push(pre) } return arr; } //語法樹轉string function AstToHtml(ast){ if(Object.prototype.toString.call(ast)==='[object Array]'){ return AstChildToString(ast); }else if(Object.prototype.toString.call(ast)==='[object String]'){ return ast; }else if(ast===null){ return ''; } let code=typeMap[ast.type](ast); if(Object.prototype.toString.call(code)==='[object Array]'){ const arr=code.map(function(obj){ if(Object.prototype.toString.call(obj)==='[object Object]'){ return AstToHtml(obj); }else if(Object.prototype.toString.call(obj)==='[object Array]'){ return AstToHtml(obj); } return obj; }) return arr.join(''); }else{ return code; } } module.exports=AstToHtml;
treeSearch.js緩存
//treeSearch.js //廣度遍歷html節點 function breadthSearch(item, childProp='children'){ const nodeList=[item] let index=0; while (index<nodeList.length){ const node=nodeList[index++]; if(node[childProp]){ for(let k in node[childProp]){ nodeList.push(node[childProp][k]); } } } return nodeList; } //深度遍歷html節點 function depthSearch(node,childProp='children'){ const nodeList=[] const depthEach=function(item){ nodeList.push(item); if(item[childProp]){ for(let k in item[childProp]){ depthEach(item[childProp][k]); } } } depthEach(node); return nodeList; } module.exports={ breadthSearch,depthSearch }