摘要:用行代码画出树状结构这两天写了这样一个小玩具,是一个可以把的树状结构解析,并且画出来的东西,把代码写到左边,右边就会自动生成啦。绘图部分依赖了百度开源的,核心功能的实现只有行代码。如果是或者标签,那么进入相应的状态
用100行代码画出DOM树状结构
这两天写了这样一个小玩具,是一个可以把DOM的树状结构解析,并且画出来的东西,把HTML代码写到左边,右边就会自动生成啦。
点这里看DEMO
源码在github · starkwang/DOM-Drawer,使用webpack打了个包。绘图部分依赖了百度开源的 ECharts,核心功能的实现只有100行代码。
核心代码解读核心代码分成两部分,tokenizer 和 parser,流程的本质上是一个最最最最简单的编译器前端。
我们期望是把类似这样的HTML字符串:
解析成这样的对象:
{ name : "div", children : [ { name : "p", childern : [] }, { name : "img", childern : [] }, { name : "a", childern : [] }, ] }Tokenizer
tokenizer 负责把 HTML 字符串分割成一个由单词、特殊符号组成的数组(去掉空格、换行符、缩进),最后返回这个数组给 parser 进行解析。
module.exports = tokenizer; function tokenizer(content) { //结果数组 var result = []; //特殊符号的集合 var symbol = ["{", "}", ":", ";", ",", "(", ")", ".", "#", "~", , "<", ">", "*", "+", "[", "]", "=", "|", "^"]; //是否在字符串中,如果是的话,要保留换行、缩进、空格 var isInString = false; //当前的单词栈 var tmpString = ""; for (var i = 0; i < content.length; i++) { //逐个读取字符 var t = content[i]; //当读取到引号时,进入字符串状态 if (t == """ || t == """) { if (isInString) { tmpString += t; isInString = false; result.push(tmpString); tmpString = ""; } else { tmpString += t; isInString = true; } continue; } if (isInString) { //字符串状态 tmpString += t; } else { //非字符串状态 if (t == " " || t == " " || t == " ") { //如果读到了换行、空格或者tab,那么把当前单词栈中的字符作为一个单词push到结果数组中,并清零单词栈 if (tmpString.length != 0) { result.push(tmpString); tmpString = ""; } continue; } if (symbol.indexOf(t) != -1) { //如果读到了特殊符号,那么把当前单词栈中的字符作为一个单词push到结果数组中,清零单词栈,再把这个特殊符号放进结果数组 if (tmpString.length != 0) { result.push(tmpString); tmpString = ""; } result.push(t); continue; } //否则把字符推入单词栈中 tmpString += t; } } return result; }Parser
parser负责逐个读取 tokenizer 生成的单词序列,并且解析成一个树形结构,这里用到了类似状态机的思想。
module.exports = parser; function parser(tokenArray) { //等下我们要从单词序列中过滤出HTML标签 var tagArray = []; //节点组成的栈,用于记录状态 var nodeStack = []; //根节点 var nodeTree = { name: "root", children: [] }; //是否在script、style标签内部 var isInScript = false, isInStyle = false; //先把根节点推入节点栈 nodeStack.push(nodeTree); //一大堆单词序列中过滤出HTML标签,注意这里没有考虑到script、style中的特殊字符 tokenArray.forEach(function(item, index) { if (item == "<") { tagArray.push(tokenArray[index + 1]); } }) //HTML标准中自封闭的标签 var selfEndTags = ["img", "br", "hr", "col", "area", "link", "meta", "frame", "input", "param"]; tagArray.forEach(function(item, index) { //逐个读取标签 if (item[0] == "!" || selfEndTags.indexOf(item) != -1) { //自封闭标签、注释、!DOCTYPE nodeStack[nodeStack.length - 1].children.push({ name: item[0] == "!" && item[1] == "-" && item[2] == "-" ? "" : item, children: [] }); } else { //普通标签 if (item[0] != "/") { //普通标签头 if (!isInScript && !isInStyle) { //如果不在script或者style标签中,向节点栈尾部的children中加入这个节点,并推入这个节点,让它成为节点栈的尾部 var newNode = { name: item, children: [] } nodeStack[nodeStack.length - 1].children.push(newNode); nodeStack.push(newNode); } //如果是script或者style标签,那么进入相应的状态 if (item == "script") { isInScript = true; } if (item == "style") { isInStyle = true; } } else { //普通标签尾 if (item.split("/")[1] == nodeStack[nodeStack.length - 1].name) { //如果这个标签和节点栈尾部的标签相同,那么认为这个节点终止,节点栈推出。 nodeStack.pop(); } //如果是script或者style标签,那么进入相应的状态 if (item.split("/")[1] == "script") { isInScript = false; } if (item.split("/")[1] == "style") { isInStyle = false; } } } }) return nodeTree; }
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/86131.html
摘要:效果预览按下右侧的点击预览按钮可以在当前页面预览,点击链接可以全屏预览。可交互视频此视频是可以交互的,你可以随时暂停视频,编辑视频中的代码。 showImg(https://segmentfault.com/img/bVbhqjK?w=400&h=300); 效果预览 按下右侧的点击预览按钮可以在当前页面预览,点击链接可以全屏预览。 https://codepen.io/comehop...
摘要:效果预览按下右侧的点击预览按钮可以在当前页面预览,点击链接可以全屏预览。可交互视频此视频是可以交互的,你可以随时暂停视频,编辑视频中的代码。 showImg(https://segmentfault.com/img/bVbhqjK?w=400&h=300); 效果预览 按下右侧的点击预览按钮可以在当前页面预览,点击链接可以全屏预览。 https://codepen.io/comehop...
摘要:贝塞尔曲线方法可以绘制一种类似的曲线。不同的是贝塞尔曲线需要两个控制点而不是一个,线段的每一个端点都需要一个控制点。下面是描述贝塞尔曲线的简单示例。 来源:ApacheCN『JavaScript 编程精解 中文第三版』翻译项目原文:Drawing on Canvas 译者:飞龙 协议:CC BY-NC-SA 4.0 自豪地采用谷歌翻译 部分参考了《JavaScript 编程精解(第 2...
阅读 2366·2021-11-16 11:44
阅读 829·2021-09-10 11:16
阅读 2205·2019-08-30 15:54
阅读 935·2019-08-30 15:53
阅读 1868·2019-08-30 13:00
阅读 594·2019-08-29 17:07
阅读 3493·2019-08-29 16:39
阅读 3116·2019-08-29 13:30