类似Vue的mvvm虚拟语法树AST解析器,html转AST,很好用!

这个也是参考别的大神的,今天翻到就拿出来了,反正用了蛮久了,现在也不知道是哪位大神的!

<!DOCTYPE html>
<html>
	<head>
		<meta charset="GBK">
		<title></title>
		<style>
* {
	margin: 0px;
	padding: 0px;
}

body {
	margin: 20px;
	font-family: Arial, "宋体";
	font-size: 12px;
	line-height: 20px;
	color: #333333;
}
</style>
	</head>
	<body>
		<div id="app">
			<input type='text' v-model='content'>
			<p a=1>aaa</p>
			<p b='222'>bbb</p>
		</div>
	</body>
	<script src="vue.js"></script>
	<script>
		//自行结束标签
       	var empty = makeMap('area,base,basefont,br,col,embed,frame,hr,img,' + 
		                    'input,isindex,keygen,link,meta,param,source,track,wbr');
		 
		function makeMap (values) {
		    values = values.split(/,/);
		    var map = {};
		    values.forEach(function (value) {
		        map[value] = 1;
		    });
		    return function (value) {
		        return map[value.toLowerCase()] === 1;
		 };
		}
		
      function parse(template){
		var root;//根节点
		var currentParent;//当前父节点
		var stack = [];//节点栈
		if(!template) return ;
		
		parseHtml(template,{
			start:function(tagName,attrs,unary){
				//元素节点
				var element={
					nodeType:1,
					tagName:tagName,
					attrs:attrs,
					children:[],
					parent:''
				}
				//如果是第一次进来
				if(!root){
					root = element;
				}
				
				if(currentParent){
					//添加子节点,并建立parent对应关系
					element.parent=currentParent
					currentParent.children.push(element);
				}
				if (!unary) {//如果不是自行结束标签
			        currentParent = element;
			        stack.push(element)
			    }
				
			},
			end:function(){//处理结束标签
				stack.length -=1;
				currentParent=stack[stack.length-1]
			},
			chars:function(text){//处理文字
				if(!currentParent){
					return;
				}
				var children = currentParent.children;
				children.push({
					nodeType:3,
					nodeValue:text,
					textContent:text
				})
			},
			comment:function (text) {// 处理注释
				if(!currentParent){
					return;
				}
				var children = currentParent.children;
				children.push({
					nodeType:8,
					nodeValue:text,
					textContent:text
				})
			}
		})
		return root ;
	}
      
    function parseHtml(template,options){
    			 // 开始标签头
		var startTagOpen = /^<([\w\-]+)/,
		// 开始标签尾
		startTagClose = /^\s*(\/?)>/, 
		// 标签属性
		attribute = /^\s*([^\s"'<>\/=]+)(?:\s*((?:=))\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/,
		// 结束标签
		endTag = /^<\/([\w\-]+)>/; 
		
    	var html = template;
    	var tagStack = [];    // 标签栈
    	var index = 0;
    	while(html){
    	   var text;
    	   start = html.indexOf('<');
    	   if(start===0){
    	   	//处理注释 <!-- -->
    	   	 var commentMatch = parseCommentsTag();
    	     if(commentMatch){ 
    	        if(options.comment){
	    	   		options.comment(commentMatch);
	    	    }
    	     	continue;
    	     }
    	     
    	     // 匹配起始标签
       		 var startTagMatch = parseStartTag();  //处理后得到match
       		 if(startTagMatch){
       		 	handleStartTag(startTagMatch);
                continue;
       		 }
                
              // 匹配结束标签
            var endTagMatch = html.match(endTag);
            if (endTagMatch) {
                var curIndex = index;
                advance(endTagMatch[0].length);
                parseEndTag(endTagMatch[1], curIndex, index);
                continue;
            }
                
                
    	   }else if(start>=0){//前面有文本
    	     text = html.slice(0,start);
    	     advance(start);
    	   }else{//没有匹配到< 是纯文本
    	     text = html;
    	     html = '';//直接字符串置空结束循环
    	   }
    	   
    	   if(options.chars && text){
    	   		options.chars(text);
    	   }
    	   
    	}
		
		function advance(n){//字符串的截取
			index += n;
			html = html.substring(n);
		}
		//处理注释标签
		function parseCommentsTag(){
			var text;
			var start = html.indexOf("<!--"); 
			if(start===0){
				var end = html.indexOf("-->"); 
				if(end>4){
					text = html.substring(start+4,end);
					advance(end+3);
				}else{
					throw new Error('注释节点有误,请检查!');
				}
			}
			return text;
		}
		
		function parseStartTag(){
			var start = html.match(startTagOpen);     // 正则匹配
			if(start){
				var match = {
					tagName:start[1],
					attrs:[],
					start:index
				}
				advance(start[0].length)
				
			}
			var end,attr;
			//当没有遇到结束标签,并且属性标签一直被匹配,这里就是处理属性内容,匹配到一个属性就处理一个
			while(!(end = html.match(startTagClose)) &&(attr = html.match(attribute)) ){
				advance(attr[0].length);
				match.attrs.push(attr);
			}
			//封装结束标签内容
			if(end){
				advance(end[0].length);
	            match.end = index;
	            return match
			}
		}
		function handleStartTag(startTagMatch){
			var tagName = startTagMatch.tagName;
			var attrs=[];
			var matchAttrs = startTagMatch.attrs;
			var len =attrs.length=matchAttrs.length,i=0;
			for(i;i<len;i++){
				attrs[i] = {
					name:matchAttrs[i][1],
					value:matchAttrs[i][3] || matchAttrs[i][4] || matchAttrs[i][5] || ''
				}
			} 
			var unary = empty(tagName);
			// 不是自闭标签
		    if (!unary) {
		        tagStack.push({ tagName: tagName, attrs: attrs});
			}
			if(options.start){
				options.start(tagName,attrs,unary);
			}
		}
		
		
		function parseEndTag (tagName, start, end) {
		  var pos;
	      if (start == null) start = index;
	      if (end == null) end = index;
	      if(tagName){
	      	pos = tagStack.length-1;
	      	for(pos;pos>=0;pos--){
	      		if(tagName===tagStack[pos].tagName){
	      			break;
	      		}
	      		
	      	}
	      }else {
		    pos = 0;
		  }
		  
	      if(pos>=0){
	      	for(var i=tagStack.length-1;i>=pos;i--){
	      		if(options.end){
	      			options.end(tagStack[i].tagName, start, end);
	      		}
	      	}
	      }
		  tagStack.length = pos;
		}
    } 
       
       
       var div=document.getElementById("app");
       console.log(parse(div.outerHTML))
        
    </script>
</html>

运行一下,就能把指定区域的html内容转换成语法树,很方便好用!

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

编程界小明哥

请博主喝瓶水,博主持续输出!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值