根据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>
推荐几个自己开发的小工具:
RepeatJs js插件, 给定模板和json, 可以循环渲染模板
CornerPHP 一款简洁的PHP框架, 支持多种方式渲染HTML, 自带注册登录功能, 自带redis队里功能, 自带前端单页面应用(SPA)
zbJSTool 好用的js库集合, 单页面路由框架, 前端生成分享图等等
microStore 单页面应用, 小巧, 功能丰富, 适用移动端的个人商店系统