const domTags=['div','section','img','p','span'] //深度遍历html节点 function depthSearch(node,childProp='children'){ const nodeList=[] const depthEach=function(item){ nodeList.push(item); if(item[childProp]){ for(let k in item[childProp]){ depthEach(item[childProp][k]); } } } depthEach(node); return nodeList; } //模板转语法树 function templateToAstData(template) { console.log(template) let deep=0; const astTree={ node:{ id:0, deep:0, child:[], }, id:0, idMap:{}, getNode(deep) { let node=this.node for(let i=1;i<deep+1;i++){ node=node.child[node.child.length-1] } return node; }, createNode(deep,propStr) { let node=this.node //创建空元素 for(let i=1;i<deep+1;i++){ if(!node.child){node.child=[]} if(i===deep){ const nNode={ id:++this.id, deep:deep, } this.idMap[nNode.id]=nNode node.child.push(nNode) } node=node.child[node.child.length-1] } const props={md5_id:node.id} if(propStr){ propStr.replace(/(\w+)=(['"])(.+?)\2/g,function (m,p1,p2,p3) { props[p1]=p3 }) } node.props=props; return node; }, createText(deep,text){ const node=this.createNode(deep) node.tag='_text'; node.text=text; return node; } }; let pathArr=[] let preRange=[0,0]; template.replace(/<([\d\D]+?)>/gi,function (m,p1,p2) { let curDeep=deep; //开头<div> if(/^([a-z]\w*)/i.test(p1)){ const tag=RegExp.$1.toLocaleLowerCase(); let last=p1.replace(tag,'') if(tag==='br'||last[last.length-1]==='/'){ if(pathArr[deep]===undefined){ pathArr[deep]={ startRange:[p2,p2+m.length], endRange:[p2,p2+m.length], isEnd:1, tag:tag, }; } }else{ pathArr[deep]={ startRange:[p2,p2+m.length], isEnd:0, tag:tag, } deep++; } }else if(/^\/([a-z]\w*)$/i.test(p1)){ //结尾 </div> const tag=RegExp.$1.toLocaleLowerCase(); if(pathArr[deep-1]&&tag===pathArr[deep-1].tag){ deep--; curDeep=deep if(tag===pathArr[curDeep].tag){ pathArr[curDeep].isEnd=2; pathArr[curDeep].endRange=[p2,p2+m.length]; } }else{ pathArr[deep]={ startRange:[p2,p2+m.length], endRange:[p2,p2+m.length], isEnd:-1, }; } } const isEnd=pathArr[curDeep].isEnd; const tag=pathArr[curDeep].tag; if(isEnd===0){ if(preRange[1]<p2){ const text=template.substring(preRange[1],p2) if(/\S/.test(text)){ //创建文字元素 astTree.createText(curDeep,text) } } //创建空元素 astTree.createNode(curDeep,p1.substring(tag.length,p1.length)) }else if(isEnd===1){ if(preRange[1]<p2){ const text=template.substring(preRange[1],p2) if(/\S/.test(text)){ //创建文字元素 astTree.createText(curDeep,text) } } //创建当前元素 const node=astTree.createNode(curDeep,p1.substring(tag.length,p1.length)) Object.assign(node,pathArr[curDeep]) }else if(isEnd===2){ if(preRange[1]<p2){ const text=template.substring(preRange[1],p2) if(/\S/.test(text)){ //创建文字元素 astTree.createText(curDeep+1,text) } } const node=astTree.getNode(curDeep) Object.assign(node,pathArr[curDeep]) } preRange=[p2,p2+m.length] }) return astTree; } //语法树转可编辑模板 function astDataToEditHtml(astTree) { const astData=astTree.node function getTextByNode(node) { //dom 对应的属性空间 const props=node.props let pstr='' for(let name in props){ pstr=pstr+` ${name}="${props[name]}"`; } if(node.tag==='_text'){ return [node.text] }else if(node.isEnd===1){ return [`<${node.tag}${pstr}/>`] }else if(node.isEnd===2){ return [`<${node.tag}${pstr}>`,`</${node.tag}>`] } return [''] } const list=depthSearch(astData,'child') let preDeep=-1; const endCache=[] let html='' for(let i=0;i<list.length;i++){ const node=list[i]; const arr=getTextByNode(node) if(node.deep<=preDeep){ for(let i=preDeep;i>=node.deep;i--){ html=html+endCache[i] } } endCache[node.deep]=arr[1]||'' html=html+arr[0] preDeep=node.deep; } for(let i=preDeep;i>=0;i--){ html=html+endCache[i] } return html; } // const astData=templateToAstData('<section name="222" style="width: 100px;">这是一<section>222<br>这是一个s<section>这是一个section</section>ection</section>个section</section>'); // const editHtml=astDataToEditHtml(astData) // console.log(editHtml) module.exports={ templateToAstData, astDataToEditHtml }