虚拟dom节点,支持querySelector

虚拟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
}

猜你喜欢

转载自www.cnblogs.com/caoke/p/11314569.html