如何把一段规范的样式文本,转为真实dom?

这篇文章主要探讨解决问题的过程,而非结果

我们的目标是写一个方法,把一段有固定格式的样式文本,转换成一个真实的dom。
我们采取从简入繁的方式,先实现功能,然后一步步完善和优化。
先列出几个有代表性的测试用例:

let test1 = "p";  //双标签
let test2 = "img";  //单标签
let test3 = ".footer"; //class
let test4 = "h2.footer"; //标签带class
let test5 = "h2.footer.center"; //标签带两个class
let test6 = "#name"; //id
let test7 = "input#name"; //标签带id
let test8 = "input#name.name"; //标签带id再加个class
let test9 = "h1#name.name.red"; //标签带id再加两个class

我们给目标方法起一个有意义的名字,并在后文里称为主函数

function cssToDom(str){
  let dom;
  /**
  * 此处经过一系列转换逻辑
  */
  return dom;
}

1.纯标签文本转dom

最简单的村标签文本,直接用 document.createElement() 方法创建就行了。
为了严谨,我们先对文本进行判断,校验文本是否一个正确的html标签。
我们采用一个看起来很笨但比较有效的方法来做校验,那就是枚举。
先写一个简单的标签检测方法,传入字符串,输出是否标签的布尔值。

function checkTag(str){
  const tags = `
    a,area,abbr,article,aside,address,audio,
    b,basefont,base,blockquote,br,big,button,body,
    center,command,canvas,col,colgroup,caption,
    em,strong,code,samp,cite,
    ul,dl,del,dd,details,dev,embed,form,figure,footer,fieldset,
    hgroup,hr,h1,h2,h3,h4,h5,h6,header,html,head,i,iframe,
    input,img,ins,keygen,link,li,legend,label,map,
    menu,meter,mark,nav,noscript,ol,object,option,output,optgroup,
    p,pre,param,prgoress,q,rp,rt,ruby,s,sub,sup,span,small,
    select,source,section,summary,td,th,tt,tr,table,
    title,tfoot,thead,time,tbody,textarea
  `;
  const tagArr = tags.replace(/\n|\s+/g,'').split(','); 
  return tagArr.includes(str);
}

带入测试用例,测试函数是否可行:

console.log(checkTag(test1));	//输出:true
console.log(checkTag(test2));	//输出:true
console.log(checkTag(test3));	//输出:false
console.log(checkTag(test4));	//输出:false
console.log(checkTag(test5));	//输出:false

如果检测到文本刚好就是一个html标签,那就直接用标签创建就行了。
主函数迭代1:

function cssToDom(str){
  let dom;
  if(checkTag(str)){
    dom = document.createElement(str);
  }
  return dom;
}

// 测试
console.log(cssToDom(test1));  //输出:<p></p>
console.log(cssToDom(test2));  //输出:<img>

2.带class的文本转dom

对于纯class转成dom,我们应该默认它是div标签,然后加一个class属性。
对于带标签的class,我们应该用它的标签去生成dom,然后加一个class属性。
观察测试用例test3,test4,test5不难发现,可以用过“.”来切割字符串,然后判断第一个数组元素是否为空,
如果不为空就可以认定为这是个标签,空的话就用div作为标签。
除了第一个数组元素为标签外,其他数组元素被认定为class,所以可以用空格拼接成多个class。
根据以上思路,主函数迭代2:

function cssToDom(str){
  let dom;
  if(checkTag(str)){
    dom = document.createElement(str);
  }else{
    let strArr = str.split('.');
    if(strArr[0]===''){
      dom = document.createElement("div");
    }else{
      dom = document.createElement(strArr[0]);
    }
    dom.classList = strArr.splice(1).join(' ');
  }
  return dom;
}

// 测试
console.log(cssToDom(test3));  //输出:<div class="footer"></div>
console.log(cssToDom(test4));  //输出:<h2 class="footer"></h2>
console.log(cssToDom(test5));  //输出:<h2 class="footer center"></h2>

3.带id的文本转dom

观察测试用例test6,test7,不难发现,可以通过“#”来切割字符串,跟上面思路一样。
按这思路的话,需要先判断字符里是不是只有“.”,或者只有“#”。
当观察到test8和test9的时候,就会发现两种符号混合起来的情况,用这种思路写起来就很难受。

4.从复杂的情况反推思路

我们就以test9为例去思考,其实不难发现,test9里包含3种类型的片段。
第一种是纯标签,第二种是前面带#号的id属性,第三种是前面带.号的class属性。
只要我们把字符串切割后,再根据类型判断做出对应的操作,就能正确生成dom了。
用什么方式切割最好呢,这是根据情况来定的,当前情况最好是用正则大法。
主函数迭代3:

function cssToDom(str){
  let dom;
  if(checkTag(str)){
    dom = document.createElement(str);
  }else{
    let tag = "div"; //标签,默认div
      let id = ""; //id属性,默认空
      let reg = /([\.#][a-zA-Z0-9_-]+)/g; //匹配出id和class
      let strArr = str.match(reg);
      let classList = []; //class,可能存在多个,所以用数组装
      strArr.forEach((item) => {
        if (item.indexOf(".") > -1) {
          //如果是class
          classList.push(item.replaceAll(".", ""));
        } else if (item.indexOf("#") > -1) {
          //如果是id
          id = item.replace("#", "");
        }
      });
      //如果开头不是.或者#
      if(/^([a-zA-Z0-9_-])[\.#]/.test(str)){
        let tmpTag = RegExp.$1;
        //如果是在枚举的标签里的,则赋值
        if (checkTag(tmpTag)) {
          tag = tmpTag;
        }
      }
    dom = document.createElement(tag);
    if(id!==""){
      dom.id = id;
    }
    if(classList.length>0){
      dom.classList = classList.join(' ');
    }
  }
  return dom;
}

// 检查所有测试用例是否满足
console.log(cssToDom(test1));   //输出:<p></p>
console.log(cssToDom(test2));   //输出:<img>
console.log(cssToDom(test3));   //输出:<div class="footer"></div>
console.log(cssToDom(test4));   //输出:<h2 class="footer"></h2>
console.log(cssToDom(test5));   //输出:<h2 class="footer center"></h2>
console.log(cssToDom(test6));   //输出:<div id="name"></div>
console.log(cssToDom(test7));   //输出:<input id="name">
console.log(cssToDom(test8));   //输出:<input id="name" class="name">
console.log(cssToDom(test9));   //输出:<h1 id="name" class="name red"></h1>

至此,测试用例全部通过

有空再探讨复杂的情况。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值