摘要:用行代码画出树状结构这两天写了这样一个小玩具,是一个可以把的树状结构解析,并且画出来的东西,把代码写到左边,右边就会自动生成啦。绘图部分依赖了百度开源的,核心功能的实现只有行代码。如果是或者标签,那么进入相应的状态
用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...
阅读 2526·2021-11-16 11:44
阅读 977·2021-09-10 11:16
阅读 2299·2019-08-30 15:54
阅读 1258·2019-08-30 15:53
阅读 1982·2019-08-30 13:00
阅读 686·2019-08-29 17:07
阅读 3584·2019-08-29 16:39
阅读 3222·2019-08-29 13:30