初次接触HTML标签-template

765 阅读4分钟

MDN对于template的解释如下:

<template>: 内容模板元素

HTML内容模板(<template>)元素是一种用于保存客户端内容机制,该内容在加载页面时不会呈现,但随后可以(原文为 may be)在运行时使用JavaScript实例化。

将模板视为一个可存储在文档中以便后续使用的内容片段。虽然解析器在加载页面时确实会处理<template>元素的内容,但这样做只是为了确保这些内容有效;但元素内容不会被渲染。

举个🌰

是不是看的有点迷糊,没关系,我们稍微写点代码。

如下,你们觉得真正在浏览器中run起来,文字是否可见呢?

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>template</title>
</head>
<body>
    <div>这是一个茶杯</div>
</body>
<script>
</script>
</html>

毫无疑问,文字可见

好,接下来我们使用template标签,看看是什么情况

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>template</title>
</head>
<body>
  <template>
    <div>这是一个茶杯_template</div>
  </template>
</body>
<script>
</script>
</html>

大家不妨想下,现在文字是否可见呢?

结果很显然,当然是不可见啦。。。不然还怎么做对照实验(PS:手动狗头)?

那么为什么会出现这种情况呢?我们可以思考下,节点不可见,要么是没渲染出来,要么是样式问题。现在可以看到节点是渲染出来了,那我们去看下样式。

发现template的display属性已经被浏览器默认设为none,当然不可见啦。好,我们现在将它设为block,再思考下,可见吗?

如下,发现文字仍然不可见

查了下样式,发现是因为高度为0。好,继续设置高度100px。结果有点奇怪,发现文字仍然不可见,这很显然不符合常用标签的使用习惯。很显然,浏览器不希望我们按照常规标签去使用template。

那我们换种想法,通过操作JS的方式来显示这串文本。如下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>template</title>
</head>
<body>
  <template id="temp">
    <div>这是一个茶杯_template</div>
  </template>
</body>
<script>
  const temp = document.getElementById('temp')
  console.log('temp.innerText = ', temp.innerText)
  console.log('temp.innerHTML = ', temp.innerHTML)
  document.body.innerHTML = temp.innerHTML
</script>
</html>

很显然,这就已经显示出来了,DOM结构就比较符合我们的常用书写方式。

但这种使用 DOMNode 或者 DOMString 的操作,不太符合前端人的写法,而且也容易造成一些安全隐患,如XSS。

既然如此,我们回头去看下template的MDN文档, 发现了 content 这么一个属性。我们将其打印出来,得到的便是一个文档片段(fragment),如下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>template</title>
</head>
<body>
  <template id="temp">
    <div>这是一个茶杯_template</div>
  </template>
</body>
<script>
  const temp = document.getElementById('temp')
  console.log('temp.content = ', temp.content)
</script>
</html>

从MDN中,可以看到这么一句话:content属性包含了模板所表示的DOM树。既然是一颗DOM树,那么DOM常用的API自然也能使用啦(如:getElementById,querySelector...),有兴趣的小伙伴不妨验证下。

继续看MDN文档,发现用到了一个API:document.importNode

该方法接收两个参数,第一个参数类型是HTMLNode(HTML节点,只能是单个节点,不能是类数组的写法),第二个参数类型是boolean(是否深度克隆,默认为true)。如下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>template</title>
</head>
<body>
  <div id="pvue"></div>
  <template id="temp">
    <span id="span">标题</span>
    <div>这是一个茶杯_template</div>
  </template>
</body>
<script>
  const root = document.getElementById('pvue')
  const temp = document.getElementById('temp')
  const realNode = document.importNode(temp.content.querySelector('div'), true)
  root.appendChild(realNode)
</script>
</html>

关于 template 标签介绍就暂时到这里了。关于他的一些特性,如下所示

  • 它的内容在激活之前一直处于惰性状态。本质上,这些标记就是隐藏的 DOM,它们不会被渲染。
  • 处于模板中的内容不会有副作用。脚本不会运行,图片不会加载,音频不会播放,…直到模板被使用。
  • 模板能够被放置在任何位置,包括 <head>、<body> 或 <frameset>,并且任何能够出现在以上元素中的内容都可以放到模板中。 注意,”任何位置”意味着 <template> 能够安全的出现在 HTML 解析器不允许出现的位置…几乎可以作为任何内容模型的子节点。

初心

其实,最开始的本意是想将template和vue相结合起来,但随着对vue的理解,发现vue是将template编译成AST,再去做静态分析。所以后续和vue的对比,也就不会往下进行。

以后

但是,我设想了下,template和shadow dom确实是可以作为未来的组件化方向去研究。毕竟天然的隔离性摆在这儿,避免了classname的冗余。

引用

感谢张鑫旭大佬和其他我不认识的大佬

  1. zhyihui.github.io/2017/04/12/…
  2. developer.mozilla.org/zh-CN/docs/…
  3. developer.mozilla.org/zh-CN/docs/…
  4. www.zhangxinxu.com/wordpress/2…