虚拟dom节点,支持querySelector,
方法:
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.js
//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.js
//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
}