上一篇文章写的是获取页面的html处理下供使用,但是在实际操作中,html的字符串不好做diff的,如果能转成json或者Array那就可以了。在转的时候需要解决的问题:
nodeType
大家写习惯了react,vue后对于原生js的一些属性可能比较生疏,咱们看看
只读属性 Node.nodeType 表示的是该节点的类型
节点类型常量
从上面的表格我们可以看到,我们如果说对页面进行转换,其实只需要考虑nodeType值为1
和3
的类型,其余不会在页面中表现出来可以不考虑,比如Comment
节点。
空标签
为什么要考虑这个呢?其实在html2json的时候是可以不考虑的,但是反序列json2html的时候就需要使用到了,看下例子:
1 2 3
| <hr/> <div>内容</div> <link href="xxxx.css"/>
|
我们在反序列的时候如果不区分就写成这样
1 2 3
| <hr></hr> <div>内容</div> <link href="xxxx.css"></link>
|
这样显然是不正确的,我们大致枚举下空标签的情况
“area”,
“base”,
“basefont”,
“br”,
“col”,
“frame”,
“hr”,
“img”,
“input”,
“isindex”,
“link”,
“meta”,
“param”,
“embed”
如何获取当前标签上的所有属性
咱们知道的是getAttribute
,但是这个需要知道属性名字,今天咱们需要使用attributes
这个api,咱们先看看官方文档
Element.attributes 属性返回该元素所有属性节点的一个实时集合。该集合是一个 NamedNodeMap 对象,不是一个数组,所以它没有 数组 的方法,其包含的 属性 节点的索引顺序随浏览器不同而不同。更确切地说,attributes 是字符串形式的名/值对,每一对名/值对对应一个属性节点。
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| <!DOCTYPE html> <html> <head> <title>Attributes example</title> <script type="text/javascript"> function listAttributes() { var paragraph= document.getElementById("paragraph"); var result= document.getElementById("result"); if (paragraph.hasAttributes()) { var attrs = paragraph.attributes; var output= ""; for(var i=attrs.length-1; i>=0; i--) { output+= attrs[i].name + "->" + attrs[i].value; } result.value = output; } else { result.value = "没有属性可显示" } } </script> </head>
<body> <p id="paragraph" style="color: green;">Sample Paragraph</p> <form action=""> <p> <input type="button" value="显示属性及其值" onclick="listAttributes();"> <input id="result" type="text" value=""> </p> </form> </body> </html>
|
code
这个知识点都搞定了,下面就开始写工具方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
| const utils = { div: null, empty: [ "area", "base", "basefont", "br", "col", "frame", "hr", "img", "input", "isindex", "link", "meta", "param", "embed" ], attr2json: (node) => { const attrs = node.attributes; const obj = {}; for (let i = 0; i < attrs.length; i++) { obj[attrs[i].name] = attrs[i].value; } return obj; }, json2ArrString: (json) => { if (!json) return ""; const arr = []; for (let i in json) { arr.push(`${i}="${json[i]}"`); } return arr.join(" "); }, getJson: function (childNodes, attrs, length) { const result = []; for (let i = 0, len = childNodes.length; i < len; i++) { let item = childNodes[i]; if (item.nodeType === 3) { item.nodeValue.trim() !== "" && result.push({ node: "text", text: item.nodeValue.trim() }); } else if (item.nodeType === 1) { let obj = { node: "element", tag: item.nodeName.toLowerCase(), attr: {} }; let flag = false; if (item.attributes.length) { for (let n = 0, l = item.attributes.length; n < l; n++) { let value = item.attributes[n].value; if (value) { flag = true; if ( item.attributes[n].name === "src" || item.attributes[n].name === "href" ) { obj.attr[item.attributes[n].name] = item.src || item.href; } else { obj.attr[item.attributes[n].name] = value; } } } } if (!flag) { delete obj.attr; } if (item.childNodes.length < 1) { let text = item.innerText; if (text) { obj.text = text; } } else { obj.child = utils.getJson(item.childNodes, attrs, length); } result.push(obj); } } return result; } };
|
html2json
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
|
const html2json = function (text, hasDoc = false) { if (!utils.div) { utils.div = document.createElement("div"); } utils.div.innerHTML = text; let allAttrs, len; let result = utils.getJson(utils.div.childNodes, allAttrs, len); return hasDoc ? { node: "root", tag: "html", attr: utils.attr2json(document.documentElement), child: result } : { node: "root", child: result }; };
|
下面也实现下json2html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| const json2string = function (json) { const empty = utils.empty;
let child = ""; if (json.child) { child = json.child .map(function (c) { return json2html(c); }) .join(""); }
let attr = ""; if (json.attr) { attr = utils.json2ArrString(json.attr); }
if (json.node === "element") { var tag = json.tag; if (empty.indexOf(tag) !== -1) { return `<${json.tag} ${attr}/>`; } return `<${json.tag} ${attr}>${child}</${json.tag}>`; } if (json.node === "text") { return json.text; } if (json.node === "root") { return json.tag ? `<${json.tag} ${attr}>${child}</${json.tag}>` : child; } };
const json2html = (json, fullDoc = false, delScript = false) => { const resHtml = json2string(json); if (fullDoc) { const newDoc = new DOMParser().parseFromString(resHtml, "text/html"); if (delScript) { newDoc.querySelectorAll("script").forEach((el) => { el.remove(); }); } return newDoc.documentElement.outerHTML; } else { return resHtml; } };
|