SOURCE

console 命令行工具 X clear

                    
>
console
const MATCHTYPE={
        TAG:'Tag',
        ID:'id',
        CLASS:'CLASS'
    }

    const RELATIONTYPE={
        SUBSELECTOR:'SubSelector',//NO COMBINATOR
        DESCENDANT:'Descendant', // "Space" combinator
        CHILD:  'Child',             // > combinator
        DIRECTADJACENT:'DirectAdjacent',    // + combinator
        INDIRECTADJACENT:'IndirectAdjacent'  // ~ combinator
    }

    //            body span{
    //                font-size:30px;
    //            }
    //            div > div{
    //                width: 300px;
    //            }
    //            div.calss1 + span >div{
    //                height:300px;
    //            }
    //            div,span,img{
    //                margin:0;
    //            }
    window.cssStr = `
             div{
  margin:0;
}
.dp1{
  height: 300px;
  margin:20px;
}
.dp1 > span{
  color:#fff;
}
.sp1 + #s1{
  display: inline-block;
}
.div1 div.div2 {
  background: #fff;
  margin:10px;
}
.div1 div.div2 .div3 ~ span{
  font-size: 20px;
}
            `;
    var html = `
       <html>
        <head></head>
        <body>
            <div   class="dp1">
               111
              <span class="sp1">111</span>
              <span id="s1">222</span>
           </div>
            <section/>
            <div class="div1">
                <div class='div2'>
                        <div class='div3'>
                            <div>333</div>
                        </div>
                        <div></div>
                        <span></span>
                </div>
            </div>
        </body>
</html>
    `;

    function weight(inlineStylePoint, idPoint, classPsuedoAttriPoint, elementsPoints) {
        this.inlineStylePoint = inlineStylePoint.toString(16)+'';
        this.idPoint = idPoint.toString(16)+'';
        this.classPsuedoAttriPoint = classPsuedoAttriPoint.toString(16)+'';
        this.elementsPoints = elementsPoints.toString(16)+'';
        this.points = parseInt('0x'+this.inlineStylePoint + this.idPoint +this.classPsuedoAttriPoint+ this.elementsPoints,16);
    }
    weight.prototype.large = function (weight) {
        return this.points > weight.points;
    }
    weight.prototype.small = function (weight){
        return this.points < weight.points;
    }
    weight.prototype.equal = function (weight){
        return this.points == weight.points;
    }
    function _parseCssSelectorToWeight(innerStyles){
        let first = 0 ;//inline
        let second = 0;//id
        let third = 0;//class
        let fourth = 0;//element
        for(let i = 0 ;innerStyles && i<innerStyles.length;i++){
            let innerStyleRule = innerStyles[i];
            let matchTypes = innerStyleRule.matchType;
            for(let j = 0 ;matchTypes && j<matchTypes.length; j++){
                switch (matchTypes[j]){
                    case MATCHTYPE.ID:
                         second++;
                        break;
                    case MATCHTYPE.CLASS:
                         third++;
                        break;
                    case MATCHTYPE.TAG:
                         fourth++;
                        break;
                    default :
                         first++;
                        break;
                }
            }
        }
        return new weight(first,second,third,fourth);

    }
    function applyTheCssStyle(dom) {
        //relationship
    }

    function processTheCombinators() {

    }

    //~
    //div div >  div div + span
    function generatorTheArraySelectorList(selectorText) {
        let selectorRex = /([^>~\+\s]+)([\s>~\+]+)?/ig;
        let result;
        let selectorList = [];
        while (result = selectorRex.exec(selectorText)) {
            let originCombinator = result[2];
            let holeMatch = result[0];
            let selector = result[1];
            let combintor = originCombinator && originCombinator.trim() ? originCombinator.trim() : (originCombinator && !originCombinator.trim() ? " " : undefined);
            if (selector) {
                selectorList.push(selector);
            }
            if (combintor) {
                selectorList.push(combintor);
            }
            // console.log('regxsess: ',result)
        }
        return selectorList;

    }
    function innerStyleRule(values,matchTypes,relation){
        this.values = values;

        this.matchType =[];
        //
        for(let i = values.length-1 ;i>=0;i--) {

            switch (matchTypes && matchTypes[i]) {
                case '.':
                    this.matchType.push(MATCHTYPE.CLASS);
                    break;
                case "#":
                    this.matchType.push(MATCHTYPE.ID);
                    break;
                default :
                    this.matchType.push(MATCHTYPE.TAG);
                    break
            }
        }
        switch (relation) {
            case ' ':
                this.relation = RELATIONTYPE.DESCENDANT;
                break;
            case '+':
                this.relation = RELATIONTYPE.DIRECTADJACENT;
                break;
            case '~':
                this.relation = RELATIONTYPE.INDIRECTADJACENT;
                break;
            case '>':
                this.relation = RELATIONTYPE.CHILD;
                break;
            default :
                this.relation = RELATIONTYPE.SUBSELECTOR;
                break;
        }


    }
    function isCombinator(str){
        if(!str)return false;
        return !!str.match(/[\s\+>~]/g)
    }
    function innerSelectorRule(selectorArray){
        //combinator 只存在于中间
        let innerSelectorRules = [];
        //匹配任何类名,单词
        let matchValue  = /([\w_-]+)/ig;
        //匹配选择器 类选择器 和 id选择器
        let typeRegex = /([#\.]+)/ig;
        for(let i = selectorArray.length-1 ; i>=0;i--){
            let selector  = selectorArray[i];
            let value = selector.match(matchValue);
            let type = selector.match(typeRegex);
            //process css in claass
            value &&  innerSelectorRules.push(new innerStyleRule(value ,type,selectorArray[i-1]))
        }
        return innerSelectorRules;
    }
    let styleSheets1 = [];


    //转换驼峰
    function switchToCam(str, delimiter) {
        let regStr = '-+([^-])';
        if (delimiter) {
            regStr = regStr.replace(/-/g, delimiter);
        }
        let camRegex = new RegExp(regStr, 'i');
        let matched = str.match(camRegex);
        if (matched) {
            return str.replace(matched[0], matched[1].toUpperCase());
        }
        else {
            return str;
        }
    }

    CSSStyleRule.prototype._resetCursor=function(){
        this.cursor= 0;
    };
    CSSStyleRule.prototype._isCursorEnd=function(){
        return this.cursor == this.innerStyles.length-1;
    };

    let IDSTYLERULEMAP=[];
    let CLASSSTYLEMAP=[];
    let TAGSTYLEMAP=[];
    let UNKNOWSTYLEMAP=[];
    let CSSRULEHASHMAP={
        ID:IDSTYLERULEMAP,
        CLASS:CLASSSTYLEMAP,
        TAG:TAGSTYLEMAP,
        UNKNOW:UNKNOWSTYLEMAP
    }
    ///CSS DEFINITION
    function CSSStyleRule(cssText, selectorText, cssBody) {
        this.cssText = cssText;
        this.selectorText = selectorText;
        this.selectorArray = generatorTheArraySelectorList(selectorText)
        this.innerStyles =innerSelectorRule(this.selectorArray);
        this.cursor = 0;
        this.cssBody = cssBody;
        this.style = {};
        let rules = this.cssBody.split(';');
        if (!rules)return;
        //last one is empty.toString(16)
        if (!rules[rules.length - 1]) {
            rules.pop();
        }
        for (let i = 0; i < rules.length; i++) {
            let rulePair = rules[i].trim();
            let ruleKeyValue = rulePair.split(':');
            let ruleKey = ruleKeyValue[0];
            let ruleValue = ruleKeyValue[1];
            this.style[switchToCam(ruleKey)] = ruleValue;
        }
        this.weight = _parseCssSelectorToWeight(this.innerStyles);
        //最右
        let rightRule = this.innerStyles[0];
        if(rightRule.matchType.indexOf(MATCHTYPE.ID)>=0){
            CSSRULEHASHMAP.ID.push(this);
            return;
        }
        if(rightRule.matchType.indexOf(MATCHTYPE.CLASS)>=0){
            CSSRULEHASHMAP.CLASS.push(this)
            return;
        }
        if(rightRule.matchType.indexOf(MATCHTYPE.TAG)>=0){
            //TAG是一定要得,需要过滤的是没有class 和 id的情况
            CSSRULEHASHMAP.TAG.push(this);
            return;
        }

        CSSRULEHASHMAP.UNKNOW.push(this);


    }
    function cssRuleGenerator(cssSnippet) {
        let cssRegex = /([^\{\}]+?){([^\{\}]+)}/ig;
        let result;
        let stylesheet = [];
        while (result = cssRegex.exec(cssSnippet)) {
          //  console.log('regex: ', result);
            let holeMatch = result[0];
            let selector = result[1] ;
            let cssRuleBody = result[2];
            let selectorList = selector;
            selectorList = selectorList.trim();
            let groups = selectorList.split(',');
            for (let i = 0; i < groups.length; i++) {
                let seletorTex = groups[i].trim();
                stylesheet.push(new CSSStyleRule(holeMatch.trim(), seletorTex.trim(), cssRuleBody.trim()));
            }

        }
        return stylesheet
    }
    //console.log(switchToCam('font_size', "_"));


    //!IMPORTANT!!!!!!!!!!!
    //!IMPORTANT!!!!!!!!!!!
    //!IMPORTANT!!!!!!!!!!!
    //!IMPORTANT!!!!!!!!!!!
    //!IMPORTANT!!!!!!!!!!!
    var styleSheets = [];
    cssRuleGenerator(styleSheets, cssStr);
   // console.log(styleSheets)



    let IDHASHMAP = {};

    function htmlNode(name, parent, value, attributes) {
        this.name = name;
        this.parent = parent;
        this.value = value;
        this.attributes = attributes;
    }
    function attributeNode(key, value) {
        this.key = key;
        this.value = value;
    }
    function LiGetElementsById(id) {
        return IDHASHMAP[id];
    }
    htmlNode.prototype.LiGetElementsById = LiGetElementsById;
    htmlNode.prototype.innerHTML = '';
    htmlNode.prototype.getAttribute = function(key){
        for(let i= 0 ;(i<this.attributes.length)&&this.attributes;i++){
            let attriNode = this.attributes[i];
            if(attriNode.key == key){
               return attriNode;
            }
        }
    };

    htmlNode.prototype.hasId = function(idName){
        let attriNode  = this.getAttribute('id');
        if(!attriNode)return;
        return attriNode.value == idName;
    };
    htmlNode.prototype.hasClassName = function(className){
        let attriNode  = this.getAttribute('class');
        if(!attriNode)return;
        let classValue = attriNode.value;
        let classList  = classValue.split(/[\s]+/g);
        for(let j = 0 ;(j<classList.length)&&classList;j++){
            let classname = classList[j];
            if(classname == className)return true;
        }
    };
    let htmlstack = [];
    function getTop(htmlstack) {
        if (htmlstack.length) {
            return htmlstack[htmlstack.length - 1]
        }
        else {
            return {};
        }
    }
    function popStack(htmlstack) {
        htmlstack.pop();
    }

    function constructTheTree(nodeStack) {
        //give every node a chilren property
        let root;
        for (var i = 0; i < nodeStack.length; i++) {
            let node = nodeStack[i];
            if (node.parent.name) {
                if (!node.parent.children) {
                    node.parent.children = [];
                    node.parent.children.push(node)
                }
                else {
                    node.parent.children.push(node);
                }
            }
            else {
                root = node
            }
        }
        root.parent = null;
        return root;
    }

    function findAttributeKeyPair(str) {
        let attributeRegex = /(([\w]+)=(('|")[\s\w-]+('|")))/g;
        let result;
        let pairs = [];
        let attributeCollection = [];
        while (result = attributeRegex.exec(str)) {
            pairs.push(result[1]);
        }
        for (let i = 0; i < pairs.length; i++) {
            let pair = pairs[i];
            let keyValue = pair.split('=');
            let key = keyValue[0];
            let value = keyValue[1].replace(/['"]+/g, '');
            attributeCollection.push(new attributeNode(key, value))
        }
        return attributeCollection;
    }
    function findTheHtmlTag(str) {
        return /^<?([a-zA-Z]+)\s*/g.exec(str)[1];
    }

    function getAttribute(str) {
        let atts = this.attributes;
        let length = atts && atts.length;
        for (let i = 0; i < length && length; i++) {
            let attri = atts[i];
            let key = attri.key;
            let value = attri.value;
            if (key == str) return value;
        }
        return null
    }
    function parseQueryById(dom) {
        let idValue = getAttribute.call(dom, 'id');
        if (idValue) {
            IDHASHMAP[idValue] = dom;
        }
    }
    function getInnerHtml(regexResult, node) {
        let holeHtml = regexResult.input;
        let tag = node.name;
        let leftHtml = holeHtml.slice(regexResult.index + regexResult[0].length, holeHtml.length);
        let re = new RegExp("<\\/(" + tag + ")>", "i"); // re为/<\/(div)>/i 没有/g标志,可以用match,效果和exec一样
        let regMatched = leftHtml.match(re);
        return leftHtml.substring(0, regMatched.index)
    }

    function _isMatchNodeRight(cssRule,node){
        return _isMatchNodeIndex(cssRule,node,0)
    }

    function _isMatchNodeIndex(cssRule,node,index){
        if(!node)return;
        if(!node.name)return;
        let styles = cssRule.innerStyles;
        let innerStyle = styles && styles[index];
        let matchFlagCount = 0;
        if(!innerStyle)return;
        let matchTypes = innerStyle.matchType;
        if(!matchTypes)return;
        let values = innerStyle.values;
        for(let i =0 ;i<matchTypes.length ;i++){
                if(matchTypes[i] == MATCHTYPE.TAG && node.name && node.name == values[i]){
                    matchFlagCount++;
                }
                if(matchTypes[i] == MATCHTYPE.CLASS && node.hasClassName && node.hasClassName(values[i])){
                    matchFlagCount++;
                }
                if(matchTypes[i] == MATCHTYPE.ID && node.hasId && node.hasId(values[i])){
                    matchFlagCount++;
                }
        }
        return matchFlagCount==matchTypes.length;
    }


    function iteratorTheCssStyle(cssStyleRule, node) {

        let RuleCombinator = cssStyleRule.innerStyles[cssStyleRule.cursor].relation;
        switch (RuleCombinator) {
            case RELATIONTYPE.DESCENDANT:
                 //console.log('descent')
                if (!_isMatchNodeIndex(cssStyleRule, node, cssStyleRule.cursor))return;
                cssStyleRule.cursor++;
                let parent = node.parent;
                if (!parent) return;
                while (!_isMatchNodeIndex(cssStyleRule, parent, cssStyleRule.cursor)) {
                    parent = parent.parent;
                    //找到第一个匹配的
                    if (!parent) return;
                }
                if (cssStyleRule._isCursorEnd()) {
                    return true
                }
                //父级匹配到了
                return iteratorTheCssStyle(cssStyleRule, parent);

            case RELATIONTYPE.SUBSELECTOR:
                return _isMatchNodeIndex(cssStyleRule, node, cssStyleRule.cursor);
            case RELATIONTYPE.CHILD:
                //>
                if (!_isMatchNodeIndex(cssStyleRule, node, cssStyleRule.cursor))return;
                cssStyleRule.cursor++;
                let parentC = node.parent;
                if(!parentC)return;
                if(!_isMatchNodeIndex(cssStyleRule, parentC, cssStyleRule.cursor))return;
                if (cssStyleRule._isCursorEnd()) {
                    return true
                }
                return iteratorTheCssStyle(cssStyleRule,parentC);
            case RELATIONTYPE.DIRECTADJACENT:
                //+
                if (!_isMatchNodeIndex(cssStyleRule, node, cssStyleRule.cursor))return;
                cssStyleRule.cursor++;
                //previousSibling todo 有可能为#text 要有previousSiblingElement
                let previousSibling = node.preslibingElement;
                if(!_isMatchNodeIndex(cssStyleRule, previousSibling, cssStyleRule.cursor))return;
                if (cssStyleRule._isCursorEnd()) {
                    return true
                }
                return iteratorTheCssStyle(cssStyleRule,previousSibling);
            case RELATIONTYPE.INDIRECTADJACENT:
                //~ find all pre
                if (!_isMatchNodeIndex(cssStyleRule, node, cssStyleRule.cursor))return;
                cssStyleRule.cursor++;
                let previousSibling_inDirect = node.preslibingElement;
                if (!previousSibling_inDirect) return;
                while (!_isMatchNodeIndex(cssStyleRule, previousSibling_inDirect, cssStyleRule.cursor)) {
                    previousSibling_inDirect = previousSibling_inDirect.preslibingElement;
                    //找到第一个匹配的
                    if (!previousSibling_inDirect) return;
                }
                if (cssStyleRule._isCursorEnd()) {
                    return true
                }
                //同级匹配到了
                return iteratorTheCssStyle(cssStyleRule, previousSibling_inDirect);
        }

    }

    function _overrideNodeStyle(node,style){
        node.style  = Object.assign(node.style , style)
    }
    function _counterOverrideNodeStyle(node,style){
        node.style = Object.assign(style , node.style);
    }
    function _constructTheWantedStyleSheets(node){
        let styleSheets = [];
        if(node.getAttribute('id')){
            styleSheets = styleSheets.concat(CSSRULEHASHMAP.ID);
        }
        if(node.getAttribute('class')){
            styleSheets = styleSheets.concat(CSSRULEHASHMAP.CLASS);
        }
        styleSheets =  styleSheets.concat(CSSRULEHASHMAP.TAG);
        styleSheets = styleSheets.concat(CSSRULEHASHMAP.UNKNOW);
        return styleSheets;

    }


    function quickSort(arr) {
        if (arr.length <= 1) { return arr; }
        var pivotIndex = Math.floor(arr.length / 2);
        var pivotItem = arr[pivotIndex];
        var pivot = arr.splice(pivotIndex, 1)[0].weight.points;

        var left = [];
        var right = [];
        for (var i = 0; i < arr.length; i++){
            if (arr[i].weight.points < pivot) {
                left.push(arr[i]);
            } else {
                right.push(arr[i]);
            }
        }
        return quickSort(left).concat([pivotItem], quickSort(right));
    }

    function embedCsstoHtmlNode(node) {

        let wantedStyles = _constructTheWantedStyleSheets(node);
        //按照权重对sheet进行排序
        let sortedWantedStyles = quickSort(wantedStyles);
        //console.log(sortedWantedStyles)
        //根据node有没有id,有没有class  去拿HASHMAP的值 TAG,UNKNOW一定要拿,ID,CLASS如果NODE没有,可以不拿
        for (let i = 0; i < sortedWantedStyles.length; i++) {
            sortedWantedStyles[i]._resetCursor();

            if (iteratorTheCssStyle(sortedWantedStyles[i], node)) {
                if(!node.style)node.style={};
                _overrideNodeStyle(node,sortedWantedStyles[i].style);
            }
        }
    }

    //todo children 合并入下面
    //firstChildren lastChildren
    //outhtml
    function generatorHtmlNode(html) {
        //todo 把id加入hashMap
        //todo class也可以加入hashMap呀,需要遍历吗?
        //clear html
        html = html.replace(/[\r\n]+/g, '');
        html = html.trim();
        let tagRegex = /<(\/)?([^\/<>]+)(\/)?>/g;
        let htmlNodes = [];
        let htmlCopy = html;
        let result;
        let root;
        let cursor = 0;
        let preslibingNode;
        let preslibingElement;
        while (result = tagRegex.exec(htmlCopy)) {
         //   console.log('regex:', result);
            let holeMatch = result[0];
            let isEnd = !!result[1];
            let matchGroup = result[2];
            let isEndOfOneTag = !!result[3];//是单闭合标签
            let tagName = findTheHtmlTag(result[2]);
            tagName = tagName.trim();
            let planText = html.slice(cursor, result.index);
            let parentNode = getTop(htmlstack);
            let flag = (tagName == parentNode.name && isEnd);
            if (planText.trim()) {
                //文本不需要入栈
                //parentNode 即为 父节点
                let textNode = new htmlNode('#text', parentNode, planText);//htmlstack.push(textNode);
                if (preslibingNode && parentNode.previousSibling != preslibingNode && preslibingNode != parentNode && parentNode.nextSibling != preslibingNode) {
                    //previousSibling
                    //构建双向链表
                    textNode.previousSibling = preslibingNode
                    preslibingNode.nextSibling = textNode;
                }
                preslibingNode = textNode;
                //结果栈 保留所有结点
                htmlNodes.push(textNode);
                if(parentNode.name){
                    //不是root
                    if (!parentNode.children) {
                        parentNode.children = [];
                        parentNode.children.push(textNode)
                    }
                    else {
                        parentNode.children.push(textNode);
                    }
                }
                else{
                    root  = textNode
                }
            }
            if (!parentNode.name || !flag) {
                //parentNode 即为 父节点
                //非文本以及单闭合
                let node = new htmlNode(tagName, parentNode, holeMatch, findAttributeKeyPair(matchGroup));

                parseQueryById(node);
                if (preslibingNode && parentNode.previousSibling != preslibingNode && preslibingNode != parentNode && parentNode.nextSibling != preslibingNode) {
                    //previousSibling
                    //构建双向链表
                    node.previousSibling = preslibingNode
                    preslibingNode.nextSibling = node;
                }
                if (preslibingElement && parentNode != preslibingElement) {
                    //previousSibling
                    //构建双向链表
                    node.preslibingElement = preslibingElement
                    preslibingElement.nextSiblingElement = node;
                }

                //临时栈
                if (!isEndOfOneTag) {
                    //不是单闭合标签
                    node.innerHTML = getInnerHtml(result, node);
                    htmlstack.push(node);

                }
                else {
                    //example of <section/>
                    node.innerHTML = '';
                }
                preslibingElement = node
                preslibingNode = node;
                //结果栈 保留所有结点
                htmlNodes.push(node);

                //构造树
                if(parentNode.name){
                    //不是root
                    if (!parentNode.children) {
                        parentNode.children = [];
                        parentNode.children.push(node)
                    }
                    else {
                        parentNode.children.push(node);
                    }
                }
                else{
                    root  = node
                }
                embedCsstoHtmlNode(node)

            }
            else {
                //slibingNode 不可能为空对象 因为为空对象就会进入上面那个循环了
                preslibingNode = parentNode;
                preslibingElement =parentNode
                //遇到结束标识
                popStack(htmlstack);
            }
            cursor = result.index + holeMatch.length;
        }
        root.parent = null;
        return root;

    }
    let htmlstr = ''
    function treeShow(root,level){
        let space = '--';
        let name = '';
        for(let i =0;i<level;i++){
            name += space
        }
        if(root.name!='#text') htmlstr+=('<div>'+name +   root.value.replace(/</g,'&lt;').replace(/>/g,'&gt;')+  (root.style?JSON.stringify(root.style):'') +'</div>')
        for(let i = 0;root.children&&i<root.children.length;i++){
            treeShow(root.children[i],level+1);
        }
        return htmlstr

    }
//    var htmlstackc = generatorHtmlNode(html);
//    var lidocument = constructTheTree(htmlstackc);
//    var dom1 = lidocument.LiGetElementsById('fds');
//    console.log('html tree ', lidocument);
//    treeShow(lidocument,0);
//    console.log('dom1: ', dom1);

 document.addEventListener('DOMContentLoaded',function(e){
        let htmlarea = document.getElementById('htmlarea');
        let cssarea  = document.getElementById('cssarea');
        htmlarea.value = window.html;
        cssarea.value = window.cssStr
    })
    document.getElementById('parse').addEventListener('click',(e)=>{
        let htmlarea = document.getElementById('htmlarea');
        let cssarea  = document.getElementById('cssarea');


        CSSRULEHASHMAP.ID=[];
        CSSRULEHASHMAP.CLASS=[];
        CSSRULEHASHMAP.TAG=[];
        CSSRULEHASHMAP.UNKNOW=[];
       let styleSheets =  cssRuleGenerator(cssarea.value);
    console.log(CSSRULEHASHMAP)
        //cssarea.value

            var lidocument = generatorHtmlNode(htmlarea.value);
           // var dom1 = lidocument.LiGetElementsById('fds');

            console.log('html tree111 ', lidocument);
        let container = document.getElementById('container');
        htmlstr= ''
        container.innerHTML = ''
        container.innerHTML = treeShow(lidocument,0);
           // console.log('dom1: ', dom1);
    });
<div>

    <![CDATA[
    &nbsp;
    ]]>

</div>
<div>html</div>
<textarea name="" id="htmlarea" cols="100" rows="30"></textarea>
<div>css</div>
<textarea name="" id="cssarea" cols="100" rows="30"></textarea>
<button id="parse">解析</button>
<!--<script src="lib/template.js"></script>-->
<div id="container"></div>
#parse{
            width: 300px;
            height: 300px;
            font-size: 30px;
            margin-top: 30px;

        }
        textarea{
            display: block;
        }