摘要:用行代碼畫出樹狀結(jié)構(gòu)這兩天寫了這樣一個(gè)小玩具,是一個(gè)可以把的樹狀結(jié)構(gòu)解析,并且畫出來的東西,把代碼寫到左邊,右邊就會(huì)自動(dòng)生成啦。繪圖部分依賴了百度開源的,核心功能的實(shí)現(xiàn)只有行代碼。如果是或者標(biāo)簽,那么進(jìn)入相應(yīng)的狀態(tài)
用100行代碼畫出DOM樹狀結(jié)構(gòu)
這兩天寫了這樣一個(gè)小玩具,是一個(gè)可以把DOM的樹狀結(jié)構(gòu)解析,并且畫出來的東西,把HTML代碼寫到左邊,右邊就會(huì)自動(dòng)生成啦。
點(diǎn)這里看DEMO
源碼在github · starkwang/DOM-Drawer,使用webpack打了個(gè)包。繪圖部分依賴了百度開源的 ECharts,核心功能的實(shí)現(xiàn)只有100行代碼。
核心代碼解讀核心代碼分成兩部分,tokenizer 和 parser,流程的本質(zhì)上是一個(gè)最最最最簡(jiǎn)單的編譯器前端。
我們期望是把類似這樣的HTML字符串:
解析成這樣的對(duì)象:
{ name : "div", children : [ { name : "p", childern : [] }, { name : "img", childern : [] }, { name : "a", childern : [] }, ] }Tokenizer
tokenizer 負(fù)責(zé)把 HTML 字符串分割成一個(gè)由單詞、特殊符號(hào)組成的數(shù)組(去掉空格、換行符、縮進(jìn)),最后返回這個(gè)數(shù)組給 parser 進(jìn)行解析。
module.exports = tokenizer; function tokenizer(content) { //結(jié)果數(shù)組 var result = []; //特殊符號(hào)的集合 var symbol = ["{", "}", ":", ";", ",", "(", ")", ".", "#", "~", , "<", ">", "*", "+", "[", "]", "=", "|", "^"]; //是否在字符串中,如果是的話,要保留換行、縮進(jìn)、空格 var isInString = false; //當(dāng)前的單詞棧 var tmpString = ""; for (var i = 0; i < content.length; i++) { //逐個(gè)讀取字符 var t = content[i]; //當(dāng)讀取到引號(hào)時(shí),進(jìn)入字符串狀態(tài) if (t == """ || t == """) { if (isInString) { tmpString += t; isInString = false; result.push(tmpString); tmpString = ""; } else { tmpString += t; isInString = true; } continue; } if (isInString) { //字符串狀態(tài) tmpString += t; } else { //非字符串狀態(tài) if (t == " " || t == " " || t == " ") { //如果讀到了換行、空格或者tab,那么把當(dāng)前單詞棧中的字符作為一個(gè)單詞push到結(jié)果數(shù)組中,并清零單詞棧 if (tmpString.length != 0) { result.push(tmpString); tmpString = ""; } continue; } if (symbol.indexOf(t) != -1) { //如果讀到了特殊符號(hào),那么把當(dāng)前單詞棧中的字符作為一個(gè)單詞push到結(jié)果數(shù)組中,清零單詞棧,再把這個(gè)特殊符號(hào)放進(jìn)結(jié)果數(shù)組 if (tmpString.length != 0) { result.push(tmpString); tmpString = ""; } result.push(t); continue; } //否則把字符推入單詞棧中 tmpString += t; } } return result; }Parser
parser負(fù)責(zé)逐個(gè)讀取 tokenizer 生成的單詞序列,并且解析成一個(gè)樹形結(jié)構(gòu),這里用到了類似狀態(tài)機(jī)的思想。
module.exports = parser; function parser(tokenArray) { //等下我們要從單詞序列中過濾出HTML標(biāo)簽 var tagArray = []; //節(jié)點(diǎn)組成的棧,用于記錄狀態(tài) var nodeStack = []; //根節(jié)點(diǎn) var nodeTree = { name: "root", children: [] }; //是否在script、style標(biāo)簽內(nèi)部 var isInScript = false, isInStyle = false; //先把根節(jié)點(diǎn)推入節(jié)點(diǎn)棧 nodeStack.push(nodeTree); //一大堆單詞序列中過濾出HTML標(biāo)簽,注意這里沒有考慮到script、style中的特殊字符 tokenArray.forEach(function(item, index) { if (item == "<") { tagArray.push(tokenArray[index + 1]); } }) //HTML標(biāo)準(zhǔn)中自封閉的標(biāo)簽 var selfEndTags = ["img", "br", "hr", "col", "area", "link", "meta", "frame", "input", "param"]; tagArray.forEach(function(item, index) { //逐個(gè)讀取標(biāo)簽 if (item[0] == "!" || selfEndTags.indexOf(item) != -1) { //自封閉標(biāo)簽、注釋、!DOCTYPE nodeStack[nodeStack.length - 1].children.push({ name: item[0] == "!" && item[1] == "-" && item[2] == "-" ? "" : item, children: [] }); } else { //普通標(biāo)簽 if (item[0] != "/") { //普通標(biāo)簽頭 if (!isInScript && !isInStyle) { //如果不在script或者style標(biāo)簽中,向節(jié)點(diǎn)棧尾部的children中加入這個(gè)節(jié)點(diǎn),并推入這個(gè)節(jié)點(diǎn),讓它成為節(jié)點(diǎn)棧的尾部 var newNode = { name: item, children: [] } nodeStack[nodeStack.length - 1].children.push(newNode); nodeStack.push(newNode); } //如果是script或者style標(biāo)簽,那么進(jìn)入相應(yīng)的狀態(tài) if (item == "script") { isInScript = true; } if (item == "style") { isInStyle = true; } } else { //普通標(biāo)簽尾 if (item.split("/")[1] == nodeStack[nodeStack.length - 1].name) { //如果這個(gè)標(biāo)簽和節(jié)點(diǎn)棧尾部的標(biāo)簽相同,那么認(rèn)為這個(gè)節(jié)點(diǎn)終止,節(jié)點(diǎn)棧推出。 nodeStack.pop(); } //如果是script或者style標(biāo)簽,那么進(jìn)入相應(yīng)的狀態(tài) if (item.split("/")[1] == "script") { isInScript = false; } if (item.split("/")[1] == "style") { isInStyle = false; } } } }) return nodeTree; }
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/86131.html
摘要:效果預(yù)覽按下右側(cè)的點(diǎn)擊預(yù)覽按鈕可以在當(dāng)前頁面預(yù)覽,點(diǎn)擊鏈接可以全屏預(yù)覽。可交互視頻此視頻是可以交互的,你可以隨時(shí)暫停視頻,編輯視頻中的代碼。 showImg(https://segmentfault.com/img/bVbhqjK?w=400&h=300); 效果預(yù)覽 按下右側(cè)的點(diǎn)擊預(yù)覽按鈕可以在當(dāng)前頁面預(yù)覽,點(diǎn)擊鏈接可以全屏預(yù)覽。 https://codepen.io/comehop...
摘要:效果預(yù)覽按下右側(cè)的點(diǎn)擊預(yù)覽按鈕可以在當(dāng)前頁面預(yù)覽,點(diǎn)擊鏈接可以全屏預(yù)覽。可交互視頻此視頻是可以交互的,你可以隨時(shí)暫停視頻,編輯視頻中的代碼。 showImg(https://segmentfault.com/img/bVbhqjK?w=400&h=300); 效果預(yù)覽 按下右側(cè)的點(diǎn)擊預(yù)覽按鈕可以在當(dāng)前頁面預(yù)覽,點(diǎn)擊鏈接可以全屏預(yù)覽。 https://codepen.io/comehop...
摘要:貝塞爾曲線方法可以繪制一種類似的曲線。不同的是貝塞爾曲線需要兩個(gè)控制點(diǎn)而不是一個(gè),線段的每一個(gè)端點(diǎn)都需要一個(gè)控制點(diǎn)。下面是描述貝塞爾曲線的簡(jiǎn)單示例。 來源:ApacheCN『JavaScript 編程精解 中文第三版』翻譯項(xiàng)目原文:Drawing on Canvas 譯者:飛龍 協(xié)議:CC BY-NC-SA 4.0 自豪地采用谷歌翻譯 部分參考了《JavaScript 編程精解(第 2...
摘要:它實(shí)際上等于清除當(dāng)前文檔流,重新寫入內(nèi)容方法用于關(guān)閉方法所新建的文檔。如果頁面已經(jīng)渲染完成關(guān)閉了,再調(diào)用方法,它會(huì)先調(diào)用方法,擦除當(dāng)前文檔所有內(nèi)容,然后再寫入我們的頁面渲染的時(shí)候就會(huì)去打開一個(gè)文檔流,當(dāng)渲染繪制結(jié)束,就關(guān)閉這個(gè)文檔流。 一、DOM簡(jiǎn)介 1、定義: DOM 是 JavaScript 操作網(wǎng)頁的接口,全稱為文檔對(duì)象模型(Document Object Model)。 2、作...
閱讀 2418·2021-11-16 11:44
閱讀 852·2021-09-10 11:16
閱讀 2228·2019-08-30 15:54
閱讀 1057·2019-08-30 15:53
閱讀 1900·2019-08-30 13:00
閱讀 620·2019-08-29 17:07
閱讀 3513·2019-08-29 16:39
閱讀 3138·2019-08-29 13:30