根据json数据和HTML模板,渲染嵌套的HTML

2020-12-22 11:53:23 星期二

场景, HTML模板是多个div嵌套, 里边有列表, 也有键值对, 与之匹配的有一个json数据, 需要根据json去渲染这个HTML DOM

示例截图:

 

 

 

1. HTML模板

 1     <div id="test">
 2         <div>{aa}</div>
 3         <div>{bb}</div>
 4         <div>{mm}</div>
 5         <div>
 6             <div class="cc">{n} : {v} : {mm}</div>
 7         </div>
 8         <div class="dd">
 9             <div>{d1}</div>
10             <div>{d2}</div>
11             <div>{mm}</div>
12             <div class="d3">{x} : {y} : {mm}</div>
13         </div>
14     </div>

2. json数据 (注意mm, 这个键在内外层都有)

 1 {
 2     'aa' : 'aa',
 3     'bb' : 'bb',
 4     'mm' : '1',
 5     'cc' : [
 6         {'n': 'n1', 'v' : 'v1', 'mm' : '2' },
 7         {'n': 'n2', 'v' : 'v2', 'mm' : '2' },
 8         {'n': 'n3', 'v' : 'v3', 'mm' : '2' },
 9     ],
10     'dd' : [
11         'd1' : 'd1',
12         'd2' : 'd2',
13         'd3' : [
14             {'x' : 'x1', 'y': 'y1', 'mm': '4'},
15             {'x' : 'x2', 'y': 'y2', 'mm': '4'},
16             {'x' : 'x3', 'y': 'y3', 'mm': '4'},
17         ],
18         'mm' : '3'
19     ]
20 };

 

3. 渲染方法 (注意, 适用了递归, 先渲染内层的数据, 再渲染外层数据, 防止内外层有相同的键导致覆盖)

 1     /**
 2         * @param node HTML DOM节点, 注意不是string
 3         * @param arr json数组 注意是数组类型
 4         * @return string 返回HTML字符串, 注意不是DOM节点
 5         */
 6         function repeatNode(node, arr) {
 7 
 8             let out = [];
 9             for (let i=0; i<arr.length; i++) {
10                 let tmp = node.outerHTML;
11                 console.log(tmp);
12                 tmp = tmp.replace(/\s/g, ' '); //去掉回车换行, 减少空白符
13 
14                 let map = arr[i];
15                 console.log(map);
16 
17                 //先渲染内层的数组
18                 for (let j in map) {
19                     if (map[j] instanceof Array) { //数组, 递归替换
20                         let subNode = node.querySelector('.'+j);
21                         let subHtml = repeatNode(subNode, map[j]); //递归
22                         let subTpl = subNode.outerHTML.replace(/\s/g, ' ');
23                         tmp = tmp.replace(subTpl, subHtml);
24 
25                     }
26                 }
27 
28                 //再渲染内层的对象
29                 for (let j in map) {
30 
31                     if (map[j] instanceof Object && !(map[j] instanceof Array)) { //对象, 递归替换
32                         
33                         let subNode = node.querySelector('.'+j);
34                         let subHtml = repeatNode(subNode, [map[j]]); //递归
35                         let subTpl = subNode.outerHTML.replace(/\s/g, ' ')
36                         tmp = tmp.replace(subTpl, subHtml);
37 
38                     }
39                 }
40 
41                 //最后渲染外层的键值对/字符串
42                 for (let j in map) {
43                     if (typeof map[j] === 'string') { //字符串, 直接替换
44                         let re = new RegExp('{' + j + '}', 'g');
45                         tmp = tmp.replace(re, map[j]);
46 
47                     }
48                 }
49 
50                 out.push(tmp);
51             }
52 
53             return out.join('');
54         }

 

4. 全部代码

  1 <!DOCTYPE html>
  2 <html lang="en">
  3 <head>
  4     <meta charset="UTF-8">
  5     <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=yes">
  6     <title>Title</title>
  7     <script src="/static/jquery.min.js"></script>
  8     <style type="text/css">
  9         div {margin:8px; margin-left: 10px; padding: 3px; border-radius: 5px; width: 80%;}
 10         #test {background-color: #eee;}
 11         .cc {background-color: #ccc;}
 12         .dd {background-color: #aaa;}
 13         .d3 {background-color: #888;}
 14     </style>
 15 </head>
 16 <body>
 17     <div id="target"></div>
 18 
 19 <!-- 注意{mm}是内外层都有 -->
 20 <template id="tpl">
 21     <div id="test">
 22         <div>{aa}</div>
 23         <div>{bb}</div>
 24         <div>{mm}</div>
 25         <div>
 26             <div class="cc">{n} : {v} : {mm}</div>
 27         </div>
 28         <div class="dd">
 29             <div>{d1}</div>
 30             <div>{d2}</div>
 31             <div>{mm}</div>
 32             <div class="d3">{x} : {y} : {mm}</div>
 33         </div>
 34     </div>
 35 </template>
 36    
 37     <script type="text/javascript">
 38         let arr = [{"aa":"aa","bb":"bb","mm":"1","cc":[{"n":"n1","v":"v1","mm":"2"},{"n":"n2","v":"v2","mm":"2"},{"n":"n3","v":"v3","mm":"2"}],"dd":{"d1":"d1","d2":"d2","d3":[{"x":"x1","y":"y1","mm":"4"},{"x":"x2","y":"y2","mm":"4"},{"x":"x3","y":"y3","mm":"4"}],"mm":"3"}}];
 39 
 40         let tpl = document.getElementById('tpl').innerHTML;
 41         let node = htmlToNode(tpl);
 42         //console.log(node);
 43 
 44         document.getElementById('target').innerHTML = repeatNode(node, arr);
 45 
 46         /**
 47         * @param node HTML DOM节点, 注意不是string
 48         * @param arr json数组 注意是数组类型
 49         * @return string 返回HTML字符串, 注意不是DOM节点
 50         */
 51         function repeatNode(node, arr) {
 52 
 53             let out = [];
 54             for (let i=0; i<arr.length; i++) {
 55                 let tmp = node.outerHTML;
 56                 console.log(tmp);
 57                 tmp = tmp.replace(/\s/g, ' '); //去掉回车换行, 减少空白符
 58 
 59                 let map = arr[i];
 60                 console.log(map);
 61 
 62                 //先渲染内层的数组
 63                 for (let j in map) {
 64                     if (map[j] instanceof Array) { //数组, 递归替换
 65                         let subNode = node.querySelector('.'+j);
 66                         let subHtml = repeatNode(subNode, map[j]); //递归
 67                         let subTpl = subNode.outerHTML.replace(/\s/g, ' ');
 68                         tmp = tmp.replace(subTpl, subHtml);
 69 
 70                     }
 71                 }
 72 
 73                 //再渲染内层的对象
 74                 for (let j in map) {
 75 
 76                     if (map[j] instanceof Object && !(map[j] instanceof Array)) { //对象, 递归替换
 77                         
 78                         let subNode = node.querySelector('.'+j);
 79                         let subHtml = repeatNode(subNode, [map[j]]); //递归
 80                         let subTpl = subNode.outerHTML.replace(/\s/g, ' ')
 81                         tmp = tmp.replace(subTpl, subHtml);
 82 
 83                     }
 84                 }
 85 
 86                 //最后渲染外层的键值对/字符串
 87                 for (let j in map) {
 88                     if (typeof map[j] === 'string') { //字符串, 直接替换
 89                         let re = new RegExp('{' + j + '}', 'g');
 90                         tmp = tmp.replace(re, map[j]);
 91 
 92                     }
 93                 }
 94 
 95                 out.push(tmp);
 96             }
 97 
 98             return out.join('');
 99         }
100 
101         function htmlToNode(html) {
102             let div = document.createElement('div');
103             let pos = html.indexOf('<'); //避免开头有空白
104             div.innerHTML = html.substring(pos);
105             return div.firstChild;
106         }
107 
108                
109     </script>
110 
111 </body>
112 </html>
View Code

 

推荐几个自己开发的小工具:

RepeatJs js插件, 给定模板和json, 可以循环渲染模板

CornerPHP 一款简洁的PHP框架, 支持多种方式渲染HTML, 自带注册登录功能, 自带redis队里功能, 自带前端单页面应用(SPA)

zbJSTool 好用的js库集合, 单页面路由框架, 前端生成分享图等等

microStore 单页面应用, 小巧, 功能丰富, 适用移动端的个人商店系统

posted @ 2020-12-22 12:03  myD  阅读(994)  评论(0编辑  收藏  举报