template 是什么?

<template> 标签是特殊的一个标签,template里面的内容浏览器不会解析. 主要用于临时保存html代码.

如下代码,template里面的标签不会展示,JS,css都不会生效,不会出现语法错误.

例:

<template id="tpl">
  <p>这里内容不会显示</p>
  <img src="/logo.png" />
  <style>
    p{ font-size:16px;}
   </style> 
   <script>
     console.log("这段JS不会被执行")
   </script>
</template>

打印出来看一下里面的结构是什么样的.

可以看来和普通标签有区别,多了一层 document-fragment

如何获取这个 document-fragment呢?
template元素多出一个属性:

通过 content 属性就可以操作里面的内容了.

实操示例

一个模板示例:

<body>
    <button id="load-tpl">加载模板</button>
    <div id="app">
       
    </div>
   
    <template id="tpl">
       <div id="container">
            <h1>{{title}}</h1>
            <div>
                <p class="name">姓名:{{name}}</p>
                <p class="age">年龄:{{age}}</p>
                <p class="img"><img src="{{img}}" width="32" height="32" /></p>
                <p>时间:{{time}}</p>
            </div>
            <div>
                <button id="btn">提交</button>
            </div>
        </div>
        <style>
          .name{font-size:18px;color:red;}
          .age{font-size: 14px;color:green;}
        </style> 
        <script>
           console.log("模板加载.")
        </script>
        <link rel="stylesheet" href="style.css">
        <script src="index.js"></script>
      </template>
    <script>

        function renderTpl(data){

            const tpl=document.querySelector("#tpl");
            /** content可以操作里面的dom节点 */
            const fragment=tpl.content;
            /** append节点被删除,所以这里使用clone */
            const cloneFragment=fragment.cloneNode(true);

            // 替换内容
            let main=cloneFragment.querySelector("#container");
            const reg = /{{(.*?)}}/g;
            main.innerHTML=main.innerHTML.replace(reg,function(item,key){
                return data[key] || "";
            });
            
            // 修改script内容
            cloneFragment.querySelector("script").innerText=`
                document.querySelector("#btn").addEventListener("click",function(){
                    console.log("提交")
                });
                console.log("加载:",${JSON.stringify(data)});
            `
            // 加载
            const app=document.querySelector("#app");
            
            app.appendChild(cloneFragment);
        }

        document.querySelector("#load-tpl")
        .addEventListener("click",function(){
            renderTpl({
                "title":"个人信息",
                "name":"张三",
                "age":"18",
                "img":"https://placehold.jp/48/999999/ff4400/128*128.png?text=%E5%BC%A0%E4%B8%89",
                "time":(new Date).toLocaleTimeString()
            });
        })
    </script>
    
    
</body>

上面示例,temaplte 是一个隐藏dom节点,图片,css,js都没有被执行,网络文件都没有被加载. 但可以像正常dom一样操作,修改js内容. 只有插入到文档中才被执行.

没有template之前,要实现就很复杂. 经常通过 <textarea style="display:none"><script type="text/x-template" > 来存储大量html代码. 动态执行大量script,css也很麻烦. 如果嵌入script或textarea就会造成混乱.

  • template的样式display默认中"none",可以设置为"block",但内容同样不显示.

  • 动态执行JS,可以生成大量JS.
   <body>
        
        <!-- 代码预加 -->
        <template id="tpl">
            <script></script>
        </template>
        <!-- 执行代码 -->
        <div id="runner"></div>

        <textarea id="code" placeholder="编写JS代码" cols="50" rows="5"></textarea>
        <p><button id="run-btn" type="button">执行代码</button></p>

        <script>
            document.querySelector("#run-btn").addEventListener("click",function(){
                let code =document.querySelector("#code").value;
                const tpl=document.querySelector("#tpl").content;
                const clone=document.importNode(tpl,true);
                const script=clone.querySelector("script");
                script.innerHTML=code;
                
                const runner=document.querySelector("#runner");
                runner.innerHTML="";
                runner.appendChild(clone);
            })
        </script>
    </body>
    

效果:

同理,可以生成html,css.

上一篇,html转dom节点

        <!-- 代码预加 -->
        <template id="tpl"></template>
       
        <textarea id="code" placeholder="编写html代码" cols="50" rows="5"></textarea>
        <p><button id="run-btn" type="button">执行代码</button></p>

        <hr/>
        <!-- 执行代码 -->
        <div id="runner"></div>

        <script>
            document.querySelector("#run-btn").addEventListener("click",function(){
                let code =document.querySelector("#code").value;
                const tpl=document.querySelector("#tpl");
                const clone=document.importNode(tpl,true);
                clone.innerHTML=code;

                const runner=document.querySelector("#runner");
                runner.innerHTML="";
                runner.appendChild(clone.content);
            })
        </script>

注意: 处于安全考虑,浏览器innerHTML方式不执行script脚本.

不能动态创建script. 只能通过
document.createElement('script')创建再执行.

用新标签替换旧标签.

newJS=document.createElement("script");
newJS.innerHTML=oldJS.innerHTML;
if(oldJS.src){
    newJS.setAttribute("src",oldJS.src);
}
frag.replaceChild(newJS,oldJS)

注意: 别想着直接克隆,克隆也不能生效. 必须新建一个.

完整代码

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
   
</head>
    <body>
        
        <!-- 代码预加 -->
        <template id="tpl"></template>
       
        <textarea id="code" placeholder="编写html代码" cols="50" rows="5"></textarea>
        <p><button id="run-btn" type="button">执行代码</button></p>

        <hr/>
        <!-- 执行代码 -->
        <div id="runner"></div>

        <script>
            document.querySelector("#run-btn").addEventListener("click",function(){
                let code =document.querySelector("#code").value;
                const tpl=document.querySelector("#tpl");
                const clone=document.importNode(tpl,true);
                clone.innerHTML=code;
                const frag=clone.content;

                // 重新替换JS标签
                const scripts=frag.querySelectorAll("script");
                scripts.forEach(function(oldJS){
                    let newJS=document.createElement("script");
                    newJS.innerHTML=oldJS.innerHTML;
                    if(oldJS.src){
                        newJS.setAttribute("src",oldJS.src);
                    }
                    frag.replaceChild(newJS,oldJS)
                })

                const runner=document.querySelector("#runner");
                runner.innerHTML="";
                runner.appendChild(frag);
            })
        </script>

    </body>
    
</html>

结果:

原作者:阿金
本文地址:https://hi-arkin.com/archives/html-template.html

标签: template html content fragment

(本篇完)

评论