1. 4.12 脚本
      1. 4.12.1 script 元素
        1. 4.12.1.1 处理模型
        2. 4.12.1.2 脚本语言
        3. 4.12.1.3 script 元素内容的限制
        4. 4.12.1.4 外部脚本的内联文档
        5. 4.12.1.5 script 元素与 XSLT 的交互
      2. 4.12.2 noscript 元素
      3. 4.12.3 template 元素
        1. 4.12.3.1 template 元素与 XSLT 和 XPath 的交互
      4. 4.12.4 slot 元素

4.12 脚本

脚本允许作者向其文档添加交互性。

鼓励作者尽可能使用声明式替代脚本,因为声明式机制通常更易于维护,并且许多用户禁用了脚本。

例如,与其使用脚本显示或隐藏某个部分以显示更多详细信息,不如使用 details 元素。

还鼓励作者在没有脚本支持的情况下使他们的应用程序优雅降级。

例如,如果作者在表格标题中提供一个链接来动态重新排序表格,则该链接也可以在没有脚本的情况下起作用,方法是从服务器请求排序后的表格。

4.12.1 script 元素

元素/脚本

所有当前引擎都支持。

Firefox1+Safari3+Chrome1+
Opera12.1+Edge79+
Edge (Legacy)12+Internet ExplorerYes
Firefox Android4+Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android12.1+

HTMLScriptElement

所有当前引擎都支持。

Firefox1+Safari3+Chrome1+
Opera12.1+Edge79+
Edge (Legacy)12+Internet Explorer5.5+
Firefox Android?Safari iOS1+Chrome Android?WebView Android37+Samsung Internet?Opera Android12.1+
类别:
元数据内容.
流内容.
短语内容.
支持脚本的元素.
可以使用此元素的上下文:
在预期 元数据内容 的地方。
在预期 短语内容 的地方。
在预期 支持脚本的元素 的地方。
内容模型:
如果没有 src 属性,则取决于 type 属性的值,但必须与 脚本内容限制 匹配。
如果存在 src 属性,则该元素必须为空或仅包含也与 脚本内容限制 匹配的 脚本文档
在 text/html 中的标签省略:
两个标签都不能省略。
内容属性:
全局属性
src — 资源的地址
type — 脚本的类型
nomodule — 阻止支持 模块脚本 的用户代理执行脚本
async — 在可用时执行脚本,而不会在获取时阻塞
defer — 延迟脚本执行
crossorigin — 元素如何处理跨源请求
integrity — 用于 子资源完整性 检查中的完整性元数据 [SRI]
referrerpolicy — 元素发起的 获取推荐者策略
blocking — 元素是否 可能阻塞渲染
fetchpriority — 设置元素发起的 获取优先级
可访问性注意事项:
针对作者.
针对实现者.
DOM 接口:
[Exposed=Window]
interface HTMLScriptElement : HTMLElement {
  [HTMLConstructor] constructor();

  [CEReactions] attribute USVString src;
  [CEReactions] attribute DOMString type;
  [CEReactions] attribute boolean noModule;
  [CEReactions] attribute boolean async;
  [CEReactions] attribute boolean defer;
  [CEReactions] attribute DOMString? crossOrigin;
  [CEReactions] attribute DOMString text;
  [CEReactions] attribute DOMString integrity;
  [CEReactions] attribute DOMString referrerPolicy;
  [SameObject, PutForwards=value] readonly attribute DOMTokenList blocking;
  [CEReactions] attribute DOMString fetchPriority;

  static boolean supports(DOMString type);

  // also has obsolete members
};

script 元素允许作者在其文档中包含动态脚本和数据块。该元素不 代表 用户的内容。

元素/脚本#attr-type

所有当前引擎都支持。

Firefox1+Safari≤4+Chrome1+
Opera?Edge79+
Edge (Legacy)12+Internet ExplorerYes
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?

type 属性允许自定义表示的脚本类型

使用 有效的 MIME 类型字符串 来表示 数据块 的要求是为了避免潜在的未来冲突。如果此规范将来添加其他类型的 脚本,则可以通过将 type 属性设置为非 MIME 类型的值来触发它们,例如 "module" 值如何表示 模块脚本。通过现在使用有效的 MIME 类型字符串,您可以确保您的数据块永远不会被重新解释为不同的脚本类型,即使在未来的用户代理中也是如此。

经典脚本JavaScript 模块脚本 可以嵌入内联,也可以使用 src 属性从外部文件导入,如果指定了该属性,则它将给出要使用的外部脚本资源的 URL。如果指定了 src,则它必须是 有效的非空 URL,可能用空格包围

内联 script 元素的内容或外部脚本资源必须符合 JavaScript 规范中 ScriptModule 生成(分别针对 经典脚本JavaScript 模块脚本)的要求。 [JAVASCRIPT]

CSS 模块脚本 的外部脚本资源的内容必须符合 CSS 规范的要求。 [CSS]

用于JSON 模块脚本 的外部脚本资源内容必须符合 JSON 规范的要求。[JSON]

用于导入映射 的内联script元素的内容必须符合导入映射编写要求

对于导入映射script元素,不得指定srcasyncnomoduledefercrossoriginintegrityreferrerpolicy属性。

文档不得包含多个导入映射script元素。

当用于包含数据块时,数据必须内联嵌入,数据格式必须使用type属性给出,并且script元素的内容必须符合为所用格式定义的要求。srcasyncnomoduledefercrossoriginintegrityreferrerpolicyfetchpriority属性不得指定。

nomodule属性是一个布尔属性,它可以阻止脚本在支持模块脚本的用户代理中执行。这允许在现代用户代理中选择性地执行模块脚本,并在较旧的用户代理中执行经典脚本如下所示nomodule属性不得在模块脚本上指定(如果指定则会被忽略)。

元素/脚本#attr-async

所有当前引擎都支持。

Firefox1+Safari≤4+Chrome1+
Opera?Edge79+
Edge (Legacy)12+Internet ExplorerYes
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?

元素/脚本#attr-defer

所有当前引擎都支持。

Firefox3.5+Safari3+Chrome1+
Opera?Edge79+
Edge(旧版)12+Internet Explorer10+
Firefox Android4+Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?

asyncdefer属性是布尔属性,指示脚本应如何评估。经典脚本可以指定deferasync,但除非存在src属性,否则不得指定两者。模块脚本可以指定async属性,但不得指定defer属性。

可以使用这些属性选择几种可能的模式,具体取决于脚本的类型。

对于经典脚本,如果存在async属性,则经典脚本将与解析并行获取,并在可用时立即评估(可能在解析完成之前)。如果不存在async属性但存在defer属性,则经典脚本将与解析并行获取,并在页面完成解析后进行评估。如果两个属性都不存在,则脚本将立即获取和评估,阻止解析直到两者都完成。

对于模块脚本,如果存在async属性,则模块脚本及其所有依赖项将与解析并行获取,并且模块脚本将在可用时立即评估(可能在解析完成之前)。否则,模块脚本及其依赖项将与解析并行获取,并在页面完成解析后进行评估。(defer属性对模块脚本没有影响。)

所有这些都在以下示意图中进行了总结

With <script>, parsing is interrupted by fetching and execution. With <script defer>, fetching is parallel to parsing and execution takes place after all parsing has finished. And with <script async>, fetching is parallel to parsing but once it finishes parsing is interrupted to execute the script. The story for <script type="module"> is similar to <script defer>, but the dependencies will be fetched as well, and the story for <script type="module" async> is similar to <script async> with the extra dependency fetching.

出于历史原因,这些属性的确切处理细节有些复杂,涉及 HTML 的多个方面。因此,实现要求必然分散在规范的各个部分。下面的算法(在本节中)描述了此处理的核心,但这些算法引用并被 HTML 中script开始结束标记的解析规则、外部内容中的规则和XML中的规则、document.write()方法的规则、脚本的处理等引用和参考。

当使用document.write()方法插入时,script元素通常会执行(通常会阻止进一步的脚本执行或 HTML 解析)。当使用innerHTMLouterHTML属性插入时,它们根本不会执行。

即使指定了async属性,也可以指定defer属性,以使仅支持defer(而不支持async)的旧版网络浏览器回退到defer行为,而不是默认的阻塞行为。

crossorigin属性是CORS 设置属性。对于经典脚本,它控制何时从其他来源获取脚本时是否会公开错误信息。对于模块脚本,它控制用于跨源请求的凭据模式

经典脚本不同,模块脚本要求使用CORS 协议进行跨源获取。

integrity属性表示此元素负责的请求的完整性元数据。其值为文本。integrity属性不得在未指定src属性时指定。[SRI]

referrerpolicy属性是引用者策略属性。其目的是设置引用者策略,用于获取脚本以及从中导入的任何脚本。[REFERRERPOLICY]

一个script元素的引用者策略在获取导入的脚本但不是其他子资源时被使用的示例

<script referrerpolicy="origin">
  fetch('/api/data');    // not fetched with <script>'s referrer policy
  import('./utils.mjs'); // is fetched with <script>'s referrer policy ("origin" in this case)
</script>

blocking属性是一个阻塞属性

fetchpriority属性是获取优先级属性。其目的是设置优先级,用于获取脚本。

动态更改srctypenomoduleasyncdefercrossoriginintegrityreferrerpolicyfetchpriority属性没有直接影响;这些属性仅在下面描述的特定时间使用。

IDL 属性 srctypedeferintegrityblocking 必须分别 反映 同名内容属性。

HTMLScriptElement/referrerPolicy

所有当前引擎都支持。

Firefox65+Safari14+Chrome70+
Opera?Edge79+
Edge (Legacy)?Internet Explorer不支持
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?

IDL 属性 referrerPolicy 必须 反映 referrerpolicy 内容属性,仅限于已知的值

IDL 属性 fetchPriority 必须 反映 fetchpriority 内容属性,仅限于已知的值

IDL 属性 crossOrigin 必须 反映 crossorigin 内容属性,仅限于已知的值

IDL 属性 noModule 必须 反映 nomodule 内容属性。

async 获取器的步骤如下:

  1. 如果 thisforce async 为 true,则返回 true。

  2. 如果 thisasync 内容属性存在,则返回 true。

  3. 返回 false。

async 设置器的步骤如下:

  1. thisforce async 设置为 false。

  2. 如果给定值为 true,则将 thisasync 内容属性设置为空字符串。

  3. 否则,移除 thisasync 内容属性。

script.text [ = value ]

返回元素的 子文本内容

可以设置,以使用给定值替换元素的子元素。

HTMLScriptElement.supports(type)

如果给定的 type 是用户代理支持的脚本类型,则返回 true。本规范中可能的脚本类型为“classic”、“module”和“importmap”,但将来可能会添加其他类型。

text 属性的获取器必须返回此 script 元素的 子文本内容

text 属性的设置器必须使用给定值 替换script 元素中的所有内容。

HTMLScriptElement/supports_static

所有当前引擎都支持。

Firefox94+Safari16+Chrome96+
Opera?Edge96+
Edge (Legacy)?Internet Explorer不支持
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?

方法 supports(type) 的步骤如下:

  1. 如果 type classic”,则返回 true。

  2. 如果 type module”,则返回 true。

  3. 如果 type importmap”,则返回 true。

  4. 返回 false。

type 参数必须与这些值完全匹配;我们不执行 ASCII 不区分大小写 匹配。这与 type 内容属性值的处理方式以及 DOMTokenListsupports() 方法的工作方式不同,但它与 WorkerType 枚举(在 Worker() 构造函数中使用)保持一致。

在此示例中,使用了两个 script 元素。一个嵌入外部 经典脚本,另一个包含一些数据作为 数据块

<script src="game-engine.js"></script>
<script type="text/x-game-map">
........U.........e
o............A....e
.....A.....AAA....e
.A..AAA...AAAAA...e
</script>

在这种情况下,数据可能被脚本用于生成视频游戏的映射。不过,数据不必以这种方式使用;也许地图数据实际上嵌入在页面标记的其他部分,而此处的 data 块仅供网站的搜索引擎使用,以帮助用户查找其游戏地图中的特定功能。

以下示例展示了如何使用 script 元素定义一个函数,然后作为 经典脚本的一部分由文档的其他部分使用。它还展示了如何在解析文档时使用 script 元素调用脚本,在本例中用于初始化表单的输出。

<script>
 function calculate(form) {
   var price = 52000;
   if (form.elements.brakes.checked)
     price += 1000;
   if (form.elements.radio.checked)
     price += 2500;
   if (form.elements.turbo.checked)
     price += 5000;
   if (form.elements.sticker.checked)
     price += 250;
   form.elements.result.value = price;
 }
</script>
<form name="pricecalc" onsubmit="return false" onchange="calculate(this)">
 <fieldset>
  <legend>Work out the price of your car</legend>
  <p>Base cost: £52000.</p>
  <p>Select additional options:</p>
  <ul>
   <li><label><input type=checkbox name=brakes> Ceramic brakes (£1000)</label></li>
   <li><label><input type=checkbox name=radio> Satellite radio (£2500)</label></li>
   <li><label><input type=checkbox name=turbo> Turbo charger (£5000)</label></li>
   <li><label><input type=checkbox name=sticker> "XZ" sticker (£250)</label></li>
  </ul>
  <p>Total: £<output name=result></output></p>
 </fieldset>
 <script>
  calculate(document.forms.pricecalc);
 </script>
</form>

以下示例展示了如何使用 script 元素包含外部 JavaScript 模块脚本

<script type="module" src="app.mjs"></script>

此模块及其所有依赖项(通过源文件中的 JavaScript import 语句表示)都将被获取。一旦整个生成的模块图被导入,并且文档解析完成,app.mjs 的内容将被执行。

此外,如果来自同一 Window 中另一个 script 元素的代码导入来自 app.mjs 的模块(例如,通过 import "./app.mjs";),则将导入由前一个 script 元素创建的相同 JavaScript 模块脚本

此示例展示了如何为现代用户代理包含 JavaScript 模块脚本,以及为旧版用户代理包含 经典脚本

<script type="module" src="app.mjs"></script>
<script nomodule defer src="classic-app-bundle.js"></script>

在支持 JavaScript 模块脚本 的现代用户代理中,具有 nomodule 属性的 script 元素将被忽略,并且 type 为“module”的 script 元素将被获取和执行(作为 JavaScript 模块脚本)。相反,旧版用户代理将忽略 type 为“module”的 script 元素,因为这对它们来说是一种未知的脚本类型——但它们在获取和执行另一个 script 元素(作为 经典脚本)方面不会有任何问题,因为它们没有实现 nomodule 属性。

以下示例展示了如何使用 script 元素编写内联 JavaScript 模块脚本,该脚本对文档的文本执行许多替换,以获得更有趣的阅读体验(例如,在新闻网站上):[XKCD1288]

<script type="module">
 import { walkAllTextNodeDescendants } from "./dom-utils.mjs";

 const substitutions = new Map([
   ["witnesses", "these dudes I know"]
   ["allegedly", "kinda probably"]
   ["new study", "Tumblr post"]
   ["rebuild", "avenge"]
   ["space", "spaaace"]
   ["Google glass", "Virtual Boy"]
   ["smartphone", "Pokédex"]
   ["electric", "atomic"]
   ["Senator", "Elf-Lord"]
   ["car", "cat"]
   ["election", "eating contest"]
   ["Congressional leaders", "river spirits"]
   ["homeland security", "Homestar Runner"]
   ["could not be reached for comment", "is guilty and everyone knows it"]
 ]);

 function substitute(textNode) {
   for (const [before, after] of substitutions.entries()) {
     textNode.data = textNode.data.replace(new RegExp(`\\b${before}\\b`, "ig"), after);
   }
 }

 walkAllTextNodeDescendants(document.body, substitute);
</script>

使用 JavaScript 模块脚本获得的一些显著功能包括:能够从其他 JavaScript 模块导入函数、默认启用严格模式以及顶级声明不会在 全局对象 上引入新属性。另请注意,无论此 script 元素出现在文档中的什么位置,它都将在文档解析完成并且其依赖项 (dom-utils.mjs) 已被获取和执行后才会执行。

以下示例展示了如何从 JavaScript 模块脚本 内部导入 JSON 模块脚本

<script type="module">
 import peopleInSpace from "http://api.open-notify.org/astros.json" with { type: "json" };

 const list = document.querySelector("#people-in-space");
 for (const { craft, name } of peopleInSpace.people) {
   const li = document.createElement("li");
   li.textContent = `${name} / ${craft}`;
   list.append(li);
 }
</script>

模块脚本的 MIME 类型检查非常严格。为了使 JSON 模块脚本 的获取成功,HTTP 响应必须具有 JSON MIME 类型,例如 Content-Type: text/json。另一方面,如果省略语句中的 with { type: "json" } 部分,则假定目的是导入 JavaScript 模块脚本,如果 HTTP 响应的 MIME 类型不是 JavaScript MIME 类型,则获取将失败。

4.12.1.1 处理模型

script 元素具有几个相关的状态片段。

script 元素具有一个 解析器文档,该文档要么为 null,要么为 Document,最初为 null。它由 HTML 解析器XML 解析器 在其插入的 script 元素上设置,并影响这些元素的处理。具有非 null 解析器文档script 元素被称为 解析器插入

script 元素具有一个 准备时间文档,该文档要么为 null,要么为 Document,最初为 null。它用于防止在 准备 期间在文档之间移动的脚本 执行

一个 `script` 元素有一个 `force async` 布尔值,初始值为 true。它由 `HTML 解析器` 和 `XML 解析器` 在它们插入的 `script` 元素上设置为 false,以及当元素添加了 `async` 内容属性时。

一个 `script` 元素有一个 `来自外部文件` 布尔值,初始值为 false。它在脚本被 `准备` 时确定,基于该元素在那个时间点的 `src` 属性。

一个 `script` 元素有一个 `准备由解析器执行` 布尔值,初始值为 false。这仅用于也为 `解析器插入` 的元素,让解析器知道何时执行脚本。

一个 `script` 元素有一个 `已开始` 布尔值,初始值为 false。

一个 `script` 元素有一个 `延迟加载事件` 布尔值,初始值为 false。

一个 `script` 元素有一个 `类型`,它可以是 null、"classic"、"module" 或 "importmap",初始值为 null。它在元素被 `准备` 时确定,基于该元素在那个时间点的 `type` 属性。

一个 `script` 元素有一个 `结果`,它可以是 "uninitialized"、null(表示错误)、一个 `脚本` 或一个 `导入映射解析结果`。它最初是 "uninitialized"。

一个 `script` 元素有 `结果准备好时要运行的步骤`,它们是一系列步骤或 null,初始值为 null。要 `标记为已准备` 一个给定 `脚本`、`导入映射解析结果` 或 null `result` 的 `script` 元素 `el`

  1. 将 `el` 的 `结果` 设置为 `result`。

  2. 如果 `el` 的 `结果准备好时要运行的步骤` 不为 null,则运行它们。

  3. 将 `el` 的 `结果准备好时要运行的步骤` 设置为 null。

  4. 将 `el` 的 `延迟加载事件` 设置为 false。


一个 `script` 元素 `el` 是 `隐式潜在渲染阻塞` 的,如果 `el` 的 `类型` 是 "classic",`el` 是 `解析器插入` 的,并且 `el` 没有 `async` 或 `defer` 属性。

一个 `script` 元素 `el` 被克隆到副本 `copy` 的 `克隆步骤` 是将 `copy` 的 `已开始` 设置为 `el` 的 `已开始`。

当 `async` 属性被添加到 `script` 元素 `el` 时,用户代理必须将 `el` 的 `force async` 设置为 false。

无论何时 `script` 元素 `el` 的 `延迟加载事件` 为 true,用户代理必须 `延迟` `el` 的 `准备时间文档` 的加载事件。


给定 `insertedNode` 的 `script` `HTML 元素连接后步骤` 是

  1. 如果 `insertedNode` 未 `连接`,则返回。

    这可能发生在较早插入的 `script` 移除较晚插入的 `script` 的情况下。例如

    <script>
    const script1 = document.createElement('script');
    script1.innerText = `
      document.querySelector('#script2').remove();
    `;
    
    const script2 = document.createElement('script');
    script2.id = 'script2';
    script2.textContent = `console.log('script#2 running')`;
    
    document.body.append(script1, script2);
    </script>

    在本例中,没有任何内容打印到控制台。当 `HTML 元素连接后步骤` 为由 `append()` 原子插入的第一个 `script` 运行时,它可以观察到第二个 `script` 已经 `连接` 到 DOM。它移除第二个 `script`,因此,当 `HTML 元素连接后步骤` 运行时,它不再 `连接`,并且不会被 `准备`。

  2. 如果 `insertedNode` 是 `解析器插入` 的,则返回。

  3. 给定 `insertedNode` `准备脚本元素`。

`script` `子节点更改步骤` 是

  1. 运行给定 `script` 元素的 `script` `HTML 元素连接后步骤`。

这对 `script` 元素和任何新插入的子 `script` 元素的执行顺序有有趣的影响。考虑以下代码段

<script id=outer-script></script>

<script>
  const outerScript = document.querySelector('#outer-script');

  const start = new Text('console.log(1);');
  const innerScript = document.createElement('script');
  innerScript.textContent = `console.log('inner script executing')`;
  const end = new Text('console.log(2);');

  outerScript.append(start, innerScript, end);

  // Logs:
  // 1
  // 2
  // inner script executing
</script>

当第二个脚本块执行时,outer-script 已经 `准备` 好了,但由于它是空的,它没有执行,因此没有被标记为 `已开始`。`Text` 节点和嵌套 `script` 元素的原子插入具有以下效果

  1. 所有三个子节点都作为 outer-script 的子节点原子插入;所有它们的 `插入步骤` 都运行,在本例中没有可观察到的后果。

  2. outer-script 的 `子节点更改步骤` 运行,这 `准备` 了该脚本;因为它的主体现在非空,这将按顺序执行两个 `Text` 节点的内容。

  3. innerScript 的 `script` `HTML 元素连接后步骤` 最终运行,导致其主体执行。

以下给定 `element`、`localName`、`oldValue`、`value` 和 `namespace` 的 `属性更改步骤` 用于所有 `script` 元素

  1. 如果 `namespace` 不为 null,则返回。

  2. 如果 `localName` 是 `src`,则运行给定 `element` 的 `script` `HTML 元素连接后步骤`。

要给定 `script` 元素 `el` `准备脚本元素`

  1. 如果 `el` 的 `已开始` 为 true,则返回。

  2. 令 `parser document` 为 `el` 的 `解析器文档`。

  3. 将 `el` 的 `解析器文档` 设置为 null。

    这样做是为了,如果解析器插入的 `script` 元素在解析器尝试运行它们时未能运行,例如因为它们为空或指定了不受支持的脚本语言,则另一个脚本可以在稍后修改它们并导致它们再次运行。

  4. 如果 `parser document` 不为 null 并且 `el` 没有 `async` 属性,则将 `el` 的 `force async` 设置为 true。

    这样做是为了,如果解析器插入的 `script` 元素在解析器尝试运行它时未能运行,但它在脚本动态更新它后被稍后执行,它将以异步方式执行,即使 `async` 属性未设置。

  5. 令 `source text` 为 `el` 的 `子节点文本内容`。

  6. 如果 `el` 没有 `src` 属性,并且 `source text` 是空字符串,则返回。

  7. 如果 `el` 未 `连接`,则返回。

  8. 如果以下任何一项为真

    则令此 `script` 元素的 `the script block's type string` 为 "text/javascript"。

    否则,如果`el`具有`type` 属性,则令`脚本块的类型字符串`为该属性的值,并去除前导和尾随的ASCII空格

    否则,`el`具有一个非空的`language` 属性;令`脚本块的类型字符串`为“text/”与`el`的`language` 属性值的连接。

    `language` 属性从不符合规范,并且如果存在`type` 属性,则始终会被忽略。

  9. 如果`脚本块的类型字符串`是JavaScript MIME 类型本质匹配,则将`el`的type设置为“classic”。

  10. 否则,如果`脚本块的类型字符串`与字符串“module”进行ASCII 不区分大小写匹配,则将`el`的type设置为“module”。

  11. 否则,如果`脚本块的类型字符串`与字符串“importmap”进行ASCII 不区分大小写匹配,则将`el`的type设置为“importmap”。

  12. 否则,返回。(不执行任何脚本,并且`el`的type保持为 null。)

  13. 如果`解析器文档`非空,则将`el`的解析器文档恢复为`解析器文档`,并将`el`的强制异步设置为 false。

  14. 将`el`的已启动设置为 true。

  15. 将`el`的准备时间文档设置为其节点文档

  16. 如果`解析器文档`非空,并且`解析器文档`不等于`el`的准备时间文档,则返回。

  17. 如果对于`el`禁用了脚本,则返回。

    禁用了脚本”的定义意味着,除其他外,以下脚本将不会执行:`XMLHttpRequest` 的 `responseXML` 文档中的脚本、`DOMParser` 创建的文档中的脚本、`XSLTProcessor` 的 `transformToDocument` 功能创建的文档中的脚本,以及最初由脚本插入到使用`createDocument()` API 创建的`Document` 中的脚本。[XHR] [DOMPARSING] [XSLTP] [DOM]

  18. 如果`el`具有`nomodule` 内容属性且其type为“classic”,则返回。

    这意味着在模块脚本上指定`nomodule` 没有效果;算法继续执行。

  19. 如果`el`没有`src` 内容属性,并且当给定`el`、“script”和`源文本`时,内容安全策略是否应阻止元素的内联行为?算法返回“Blocked”,则返回。[CSP]

  20. 如果`el`具有`event` 属性和`for` 属性,并且`el`的type为“classic”,则

    1. 令`for`为`el`的`for` 属性的值。

    2. 令`event`为`el`的`event` 属性的值。

    3. 去除 `event` 和 `for` 的前导和尾随的ASCII空格。

    4. 如果`for`与字符串“window”不进行ASCII 不区分大小写匹配,则返回。

    5. 如果`event`与字符串“onload”或字符串“onload()”都不进行ASCII 不区分大小写匹配,则返回。

  21. 如果`el`具有`charset` 属性,则令`编码`为从`charset` 属性的值获取编码的结果。

    如果`el`没有`charset` 属性,或者如果获取编码失败,则令`编码`为`el`的节点文档编码

    如果`el`的type为“module”,则此编码将被忽略。

  22. 令`经典脚本 CORS 设置`为`el`的`crossorigin` 内容属性的当前状态。

  23. 令`模块脚本凭据模式`为`el`的`crossorigin` 内容属性的CORS 设置属性凭据模式

  24. 令`加密 nonce`为`el`的[[CryptographicNonce]] 内部槽的值。

  25. 如果`el`具有`integrity` 属性,则令`完整性元数据`为该属性的值。

    否则,令`完整性元数据`为空字符串。

  26. 令`推荐人策略`为`el`的`referrerpolicy` 内容属性的当前状态。

  27. 令`获取优先级`为`el`的`fetchpriority` 内容属性的当前状态。

  28. 令`解析器元数据`为“parser-inserted”(如果`el`是解析器插入的),“not-parser-inserted”(否则)。

  29. 令`选项`为一个脚本获取选项,其加密 nonce为`加密 nonce`、完整性元数据为`完整性元数据`、解析器元数据为`解析器元数据`、凭据模式为`模块脚本凭据模式`、推荐人策略为`推荐人策略`,以及获取优先级为`获取优先级`。

  30. 令`设置对象`为`el`的节点文档相关设置对象

  31. 如果`el`具有`src` 内容属性,则

    1. 如果`el`的type为“importmap”,则在给定`el`的DOM 操作任务源排队一个元素任务,以在`el`上触发名为`error` 的事件,并返回。

      目前不支持外部导入映射脚本。有关添加支持的讨论,请参阅WICG/import-maps 问题 #235

    2. 令`src`为`el`的`src` 属性的值。

    3. 如果`src`为空字符串,则在给定`el`的DOM 操作任务源排队一个元素任务,以在`el`上触发名为`error` 的事件,并返回。

    4. 将`el`的来自外部文件设置为 true。

    5. 令`url`为给定`src`相对于`el`的节点文档编码解析 URL的结果。

    6. 如果`url`为失败,则在给定`el`的DOM 操作任务源排队一个元素任务,以在`el`上触发名为`error` 的事件,并返回。

    7. 如果`el`是可能阻塞渲染的,则在`el`上阻止渲染

    8. 将`el`的延迟加载事件设置为 true。

    9. 如果 el 当前是 渲染阻塞 的,则将 options渲染阻塞 设置为 true。

    10. 令给定 resultonComplete 为以下步骤

      1. 标记为就绪 给定 resultel

    11. 根据 el类型 进行切换

      "classic"

      获取经典脚本,给定 url设置对象options经典脚本 CORS 设置编码onComplete

      "module"

      如果 el 没有 integrity 属性,则将 options完整性元数据 设置为使用 url设置对象 解析模块完整性元数据 的结果。

      获取外部模块脚本图,给定 url设置对象optionsonComplete

      出于性能原因,用户代理可能会在设置 src 属性后立即开始获取经典脚本或模块图(如上所述),希望 el 会连接(并且 crossorigin 属性在此期间不会更改其值)。无论哪种方式,一旦 el 连接,加载必须已如本步骤所述开始。如果 UA 执行此类预取,但 el 从未连接,或 src 属性发生动态更改,或 crossorigin 属性发生动态更改,则用户代理将不会执行获取的脚本,并且获取过程将被视为浪费。

  32. 如果 el 没有 src 内容属性

    1. 基本 URLel节点文档文档基本 URL

    2. 根据 el类型 进行切换

      "classic"
      1. script 为使用 源文本设置对象基本 URLoptions 创建经典脚本 的结果。

      2. 标记为就绪 给定 scriptel

      "module"
      1. el延迟加载事件 设置为 true。

      2. 如果 el潜在渲染阻塞 的,则

        1. 阻止渲染 el

        2. options渲染阻塞 设置为 true。

      3. 获取内联模块脚本图,给定 源文本基本 URL设置对象options,并使用以下步骤给定 result

        1. 在给定 el 的网络任务源 上排队一个元素任务 以执行以下步骤

          1. 标记为就绪 给定 resultel

          在这里排队任务意味着,即使内联模块脚本没有依赖项或同步导致解析错误,我们也不会同步继续执行 脚本元素

      "importmap"
      1. 如果 el相关全局对象允许导入映射 为 false,则在给定 elDOM 操作任务源 上排队一个元素任务 以在 el触发名为 error 的事件,并返回。

      2. el相关全局对象允许导入映射 设置为 false。

      3. result 为给定 源文本基本 URL 创建导入映射解析结果 的结果。

      4. 标记为就绪 给定 resultel

  33. 如果 el类型 为 "classic" 且 elsrc 属性,或 el类型 为 "module"

    1. 断言el结果 为 "uninitialized"。

    2. 如果 elasync 属性或 el强制异步 为 true

      1. scriptsel准备时间文档将尽快执行的脚本集

      2. el 追加到 scripts

      3. el结果就绪时要运行的步骤 设置为以下内容

        1. 执行脚本元素 el

        2. scripts 移除 el

    3. 否则,如果 el 不是 解析器插入

      1. scriptsel准备时间文档将尽快按顺序执行的脚本列表

      2. el 追加到 scripts

      3. el结果就绪时要运行的步骤 设置为以下内容

        1. 如果 scripts[0] 不是 el,则中止这些步骤。

        2. scripts 不为空,且 scripts[0] 的 结果 不是 "uninitialized" 时

          1. 执行脚本元素 scripts[0]。

          2. scripts 移除 scripts[0]。

    4. 否则,如果 eldefer 属性或 el类型 为 "module"

      1. el 追加到解析器文档文档解析完成后要执行的脚本列表

      2. el结果就绪时要运行的步骤 设置为以下内容:将 el准备由解析器执行 设置为 true。(解析器将处理脚本执行。)

    5. 否则

      1. el解析器文档挂起的解析阻塞脚本 设置为 el

      2. 阻止渲染 el

      3. el结果就绪时要运行的步骤 设置为以下内容:将 el准备由解析器执行 设置为 true。(解析器将处理脚本执行。)

  34. 否则

    1. 断言el结果 *不*为 "uninitialized"。

    2. 如果以下所有条件都为真

      1. el解析器文档挂起的解析阻塞脚本 设置为 el

      2. el准备由解析器执行 设置为 true。(解析器将处理脚本执行。)

    3. 否则,立即 执行脚本元素 el,即使其他脚本已在执行。

每个 文档 都有一个 挂起的解析阻塞脚本,它是一个 script 元素或 null,最初为 null。

每个 文档 都有一个 将尽快执行的脚本集,它是一个 集合,包含 script 元素,最初为空。

每个 Document 都有一个 尽快按顺序执行的脚本列表,它是一个 列表,包含 script 元素,初始为空。

每个 Document 都有一个 文档解析完成后执行的脚本列表,它是一个 列表,包含 script 元素,初始为空。

如果一个阻塞解析器的 script 元素在通常情况下停止阻塞解析器之前被移动到另一个 Document,它仍然会继续阻塞该解析器,直到导致它阻塞解析器的条件不再适用(例如,如果脚本是 挂起的阻塞解析脚本,因为原始的 Document 在解析时 有阻塞脚本的样式表,但随后脚本在阻塞样式表加载之前被移动到另一个 Document,则脚本仍然会阻塞解析器,直到所有样式表都加载完成,此时脚本执行并且解析器被解除阻塞)。

执行脚本元素,给定一个 script 元素 el

  1. documentel节点文档

  2. 如果 el准备时间文档 不等于 document,则返回。

  3. 解除渲染阻塞el 上。

  4. 如果 el结果 为空,则 触发一个名为 error 的事件在 el 上,并返回。

  5. 如果 el来自外部文件 为真,或者 el类型 为 "module",则增加 document忽略破坏性写入计数器

  6. 根据 el类型 切换

    "classic"
    1. oldCurrentScriptdocumentcurrentScript 对象最近设置的值。

    2. 如果 el *不是* 阴影根,则将 documentcurrentScript 属性设置为 el。否则,将其设置为 null。

      这不会使用 在文档树中 检查,因为 el 可以在执行之前从文档中删除,在这种情况下 currentScript 仍然需要指向它。

    3. 运行经典脚本el结果 给出。

    4. documentcurrentScript 属性设置为 oldCurrentScript

    "module"
    1. 断言documentcurrentScript 属性为 null。

    2. 运行模块脚本el结果 给出。

    "importmap"
    1. 注册导入映射 给定 el相关全局对象el结果

  7. 如果在前面的步骤中增加了 document忽略破坏性写入计数器,则递减它。

  8. 如果 el来自外部文件 为真,则 触发一个名为 load 的事件在 el 上。

4.12.1.2 脚本语言

用户代理不需要支持 JavaScript。如果除了 JavaScript 之外的其他语言出现并被 Web 浏览器广泛采用,则需要更新此标准。在此之前,实现其他语言与本标准相冲突,因为 script 元素定义了处理模型。

服务器应根据 ECMAScript 媒体类型更新 使用 text/javascript 用于 JavaScript 资源。服务器不应为 JavaScript 资源使用其他 JavaScript MIME 类型,并且不得使用非 JavaScript MIME 类型[RFC9239]

对于外部 JavaScript 资源,`Content-Type` 标头中的 MIME 类型参数通常会被忽略。(在某些情况下,`charset` 参数会产生影响。)但是,对于 script 元素的 type 属性,它们是重要的;它使用了 JavaScript MIME 类型本质匹配 概念。

例如,type 属性设置为 "text/javascript; charset=utf-8" 的脚本将不会被评估,即使在解析时这是一个有效的 JavaScript MIME 类型

此外,对于外部 JavaScript 资源,`Content-Type` 标头处理周围存在特殊考虑因素,如 准备脚本元素 算法和 Fetch 中所述。 [FETCH]

4.12.1.3 script 元素内容的限制

避免本节中描述的相当奇怪的限制的最简单和最安全的方法是始终将 ASCII 不区分大小写的 "<!--" 匹配转义为 "\x3C!--","<script" 转义为 "\x3Cscript",以及 "</script" 转义为 "\x3C/script",当这些序列出现在脚本的文字中时(例如在字符串、正则表达式或注释中),并避免编写使用此类构造的代码。这样做可以避免本节中限制容易触发的陷阱:即,由于历史原因,HTML 中 script 块的解析是一种奇怪而奇特的做法,在面对这些序列时会表现出不直观的行为。

script 元素的 后代文本内容 必须匹配以下 ABNF 中的 script 产生式,其字符集为 Unicode。 [ABNF]

script        = outer *( comment-open inner comment-close outer )

outer         = < any string that doesn't contain a substring that matches not-in-outer >
not-in-outer  = comment-open
inner         = < any string that doesn't contain a substring that matches not-in-inner >
not-in-inner  = comment-close / script-open

comment-open  = "<!--"
comment-close = "-->"
script-open   = "<" s c r i p t tag-end

s             =  %x0053 ; U+0053 LATIN CAPITAL LETTER S
s             =/ %x0073 ; U+0073 LATIN SMALL LETTER S
c             =  %x0043 ; U+0043 LATIN CAPITAL LETTER C
c             =/ %x0063 ; U+0063 LATIN SMALL LETTER C
r             =  %x0052 ; U+0052 LATIN CAPITAL LETTER R
r             =/ %x0072 ; U+0072 LATIN SMALL LETTER R
i             =  %x0049 ; U+0049 LATIN CAPITAL LETTER I
i             =/ %x0069 ; U+0069 LATIN SMALL LETTER I
p             =  %x0050 ; U+0050 LATIN CAPITAL LETTER P
p             =/ %x0070 ; U+0070 LATIN SMALL LETTER P
t             =  %x0054 ; U+0054 LATIN CAPITAL LETTER T
t             =/ %x0074 ; U+0074 LATIN SMALL LETTER T

tag-end       =  %x0009 ; U+0009 CHARACTER TABULATION (tab)
tag-end       =/ %x000A ; U+000A LINE FEED (LF)
tag-end       =/ %x000C ; U+000C FORM FEED (FF)
tag-end       =/ %x0020 ; U+0020 SPACE
tag-end       =/ %x002F ; U+002F SOLIDUS (/)
tag-end       =/ %x003E ; U+003E GREATER-THAN SIGN (>)

script 元素包含 脚本文档 时,元素的内容还有其他限制,如下面的部分所述。

以下脚本说明了此问题。假设您有一个包含字符串的脚本,如下所示

const example = 'Consider this string: <!-- <script>';
console.log(example);

如果将此字符串直接放入 script 块中,则会违反上述限制

<script>
  const example = 'Consider this string: <!-- <script>';
  console.log(example);
</script>

不过,更大的问题,也是它违反这些限制的原因在于,实际上脚本的解析方式很奇怪:*上面的脚本块没有终止*。也就是说,在此代码段中看起来像 "</script>" 结束标记的实际上仍然是 script 块的一部分。脚本不会执行(因为它没有终止);如果它以某种方式执行,就像以下标记所示,它将失败,因为脚本(此处突出显示)不是有效的 JavaScript

<script>
  const example = 'Consider this string: <!-- <script>';
  console.log(example);
</script>
<!-- despite appearances, this is actually part of the script still! -->
<script>
 ... // this is the same script block still...
</script>

这里发生的事情是,由于遗留原因,HTML 中 script 元素中的 "<!--" 和 "<script" 字符串需要保持平衡才能使解析器考虑关闭该块。

通过像本节开头提到的那样转义有问题的字符串,可以完全避免此问题

<script>
  // Note: `\x3C` is an escape sequence for `<`.
  const example = 'Consider this string: \x3C!-- \x3Cscript>';
  console.log(example);
</script>
<!-- this is just a comment between script blocks -->
<script>
 ... // this is a new script block
</script>

这些序列可能自然地出现在脚本表达式中,例如以下示例

if (x<!--y) { ... }
if ( player<script ) { ... }

在这种情况下,无法转义字符,但可以重写表达式以使序列不出现,例如

if (x < !--y) { ... }
if (!--y > x) { ... }
if (!(--y) > x) { ... }
if (player < script) { ... }
if (script > player) { ... }

这样做还可以避免另一个陷阱:由于相关的历史原因,经典脚本 中的字符串 "<!--" 实际上被视为行注释的开始,就像 "//" 一样。

4.12.1.4 外部脚本的内联文档

如果 script 元素的 src 属性已指定,则 script 元素的内容(如果有)必须使得 text IDL 属性的值(源自元素的内容)匹配以下 ABNF 中的 documentation 产生式,其字符集为 Unicode。 [ABNF]

documentation = *( *( space / tab / comment ) [ line-comment ] newline )
comment       = slash star *( not-star / star not-slash ) 1*star slash
line-comment  = slash slash *not-newline

; characters
tab           = %x0009 ; U+0009 CHARACTER TABULATION (tab)
newline       = %x000A ; U+000A LINE FEED (LF)
space         = %x0020 ; U+0020 SPACE
star          = %x002A ; U+002A ASTERISK (*)
slash         = %x002F ; U+002F SOLIDUS (/)
not-newline   = %x0000-0009 / %x000B-10FFFF
                ; a scalar value other than U+000A LINE FEED (LF)
not-star      = %x0000-0029 / %x002B-10FFFF
                ; a scalar value other than U+002A ASTERISK (*)
not-slash     = %x0000-002E / %x0030-10FFFF
                ; a scalar value other than U+002F SOLIDUS (/)

这对应于将元素的内容放在 JavaScript 注释中。

此要求是对之前关于 script 元素内容语法的限制的补充。

这允许作者在文档中包含文档,例如许可证信息或 API 信息,同时仍然引用外部脚本文件。语法受到限制,以防止作者意外包含看起来像有效脚本的内容,同时还提供了一个src 属性。

<script src="cool-effects.js">
 // create new instances using:
 //    var e = new Effect();
 // start the effect using .play, stop using .stop:
 //    e.play();
 //    e.stop();
</script>
4.12.1.5 script 元素与 XSLT 的交互

本节是非规范性的。

本规范未定义 XSLT 如何与script 元素交互。但是,在没有其他规范实际定义此内容的情况下,以下是一些针对实现者的指南,这些指南基于现有实现。

前两种情况和最后一种情况之间的主要区别在于,前两种情况对Document 进行操作,而最后一种情况对片段进行操作。

4.12.2 noscript 元素

元素/noscript

所有当前引擎都支持。

Firefox1+Safari3+Chrome1+
Opera?Edge79+
Edge (Legacy)12+Internet ExplorerYes
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?
类别:
元数据内容.
流内容.
短语内容.
可以使用此元素的上下文:
HTML 文档head 元素中,如果没有祖先noscript 元素。
HTML 文档中预期短语内容的位置,如果没有祖先noscript 元素。
内容模型:
脚本被禁用时,在head 元素中:以任意顺序,零个或多个link 元素,零个或多个style 元素,以及零个或多个meta 元素。
脚本被禁用时,不在head 元素中:透明,但不能有noscript 元素后代。
否则:符合散文给出的要求的文本。
在 text/html 中的标签省略:
两个标签都不能省略。
内容属性:
全局属性
可访问性注意事项:
针对作者.
针对实现者.
DOM 接口:
使用HTMLElement

如果脚本已启用,则noscript 元素表示无,如果脚本已禁用,则表示其子元素。它用于通过影响文档的解析方式,向支持脚本的用户代理和不支持脚本的用户代理呈现不同的标记。

当在HTML 文档中使用时,允许的内容模型如下所示

head 元素中,如果脚本已禁用(针对noscript 元素)

The noscript 元素必须仅包含linkstylemeta 元素。

head 元素中,如果脚本已启用(针对noscript 元素)

The noscript 元素必须仅包含文本,但使用noscript 元素作为上下文元素并将文本内容作为input调用HTML 片段解析算法必须导致节点列表,该列表仅由linkstylemeta 元素组成,如果它们是noscript 元素的子元素,则这些元素将符合规范,并且没有解析错误

head 元素之外,如果脚本已禁用(针对noscript 元素)

The noscript 元素的内容模型为透明,并附加以下限制:noscript 元素不能具有noscript 元素作为祖先(也就是说,noscript 不能嵌套)。

head 元素之外,如果脚本已启用(针对noscript 元素)

The noscript 元素必须仅包含文本,但文本必须使得运行以下算法会导致符合规范的文档,该文档没有noscript 元素和script 元素,并且算法中的任何步骤都不会引发异常或导致HTML 解析器标记解析错误

  1. 从文档中删除每个script 元素。
  2. 列出文档中的每个noscript 元素。对于该列表中的每个noscript 元素,执行以下步骤
    1. snoscript 元素的子文本内容
    2. noscript 元素的outerHTML 属性设置为s 的值。(这作为副作用会导致noscript 元素从文档中删除。)

所有这些曲折都是必需的,因为出于历史原因,脚本是否已启用 会影响noscript 元素在HTML 解析器中的处理方式。

The noscript 元素不能在XML 文档中使用。

The noscript 元素仅在HTML 语法中有效,在XML 语法中无效。这是因为它通过在脚本启用时基本上“关闭”解析器来工作,从而使元素的内容被视为纯文本而不是实际的元素。XML 未定义执行此操作的机制。

The noscript 元素没有其他要求。特别是,即使脚本已启用(针对该元素),noscript 元素的子元素也不免于表单提交、脚本编写等。

在以下示例中,noscript 元素用于为脚本提供回退。

<form action="calcSquare.php">
 <p>
  <label for=x>Number</label>:
  <input id="x" name="x" type="number">
 </p>
 <script>
  var x = document.getElementById('x');
  var output = document.createElement('p');
  output.textContent = 'Type a number; it will be squared right then!';
  x.form.appendChild(output);
  x.form.onsubmit = function () { return false; }
  x.oninput = function () {
    var v = x.valueAsNumber;
    output.textContent = v + ' squared is ' + v * v;
  };
 </script>
 <noscript>
  <input type=submit value="Calculate Square">
 </noscript>
</form>

当脚本被禁用时,会出现一个按钮以在服务器端执行计算。当脚本启用时,值会即时计算。

The noscript 元素是一种笨拙的工具。有时,脚本可能已启用,但由于某种原因,页面的脚本可能会失败。因此,通常最好避免使用noscript,而是设计脚本以根据需要将页面从无脚本页面更改为有脚本页面,如下一个示例所示。

<form action="calcSquare.php">
 <p>
  <label for=x>Number</label>:
  <input id="x" name="x" type="number">
 </p>
 <input id="submit" type=submit value="Calculate Square">
 <script>
  var x = document.getElementById('x');
  var output = document.createElement('p');
  output.textContent = 'Type a number; it will be squared right then!';
  x.form.appendChild(output);
  x.form.onsubmit = function () { return false; }
  x.oninput = function () {
    var v = x.valueAsNumber;
    output.textContent = v + ' squared is ' + v * v;
  };
  var submit = document.getElementById('submit');
  submit.parentNode.removeChild(submit);
 </script>
</form>

上述技术在XML 文档中也很有用,因为noscript元素在其中不允许使用。

4.12.3 template 元素

元素/template

所有当前引擎都支持。

Firefox22+Safari8+Chrome26+
Opera?Edge79+
Edge (旧版)13+Internet Explorer不支持
Firefox Android?Safari iOS?Chrome Android?WebView Android支持Samsung Internet?Opera Android?

HTMLTemplateElement

所有当前引擎都支持。

Firefox22+Safari8+Chrome26+
Opera?Edge79+
Edge (旧版)13+Internet Explorer不支持
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?
类别:
元数据内容.
流内容.
短语内容.
支持脚本的元素.
可以使用此元素的上下文:
在预期元数据内容的地方。
在预期短语内容的地方。
在预期支持脚本的元素的地方。
作为colgroup 元素的子元素,且该colgroup 元素没有span 属性。
内容模型:
(为了说明,请参阅示例)。
在 text/html 中的标签省略:
两个标签都不能省略。
内容属性:
全局属性
shadowrootmode — 启用流式声明式 Shadow DOM
shadowrootdelegatesfocus — 在声明式 Shadow DOM 上设置焦点委托
shadowrootclonable — 在声明式 Shadow DOM 上设置可克隆
shadowrootserializable — 在声明式 Shadow DOM 上设置可序列化
可访问性注意事项:
针对作者.
针对实现者.
DOM 接口:
[Exposed=Window]
interface HTMLTemplateElement : HTMLElement {
  [HTMLConstructor] constructor();

  readonly attribute DocumentFragment content;
  [CEReactions] attribute DOMString shadowRootMode;
  [CEReactions] attribute boolean shadowRootDelegatesFocus;
  [CEReactions] attribute boolean shadowRootClonable;
  [CEReactions] attribute boolean shadowRootSerializable;
};

template 元素用于声明 HTML 片段,这些片段可以通过脚本克隆并插入文档中。

在渲染过程中,template 元素表示无。

shadowrootmode 内容属性是一个枚举属性,具有以下关键字和状态

关键字状态简要描述
open open template 元素表示一个开放的声明式 Shadow DOM。
closed closed template 元素表示一个封闭的声明式 Shadow DOM。

shadowrootmode 属性的无效值默认值缺失值默认值 均为none 状态。

shadowrootdelegatesfocus 内容属性是一个布尔属性

shadowrootclonable 内容属性是一个布尔属性

shadowrootserializable 内容属性是一个布尔属性

template 元素的模板内容不是元素本身的子元素

由于 DOM 操作,template 元素也可能包含文本 节点和元素节点;但是,包含任何节点都违反了template 元素的内容模型,因为其内容模型定义为

例如,考虑以下文档

<!doctype html>
<html lang="en">
 <head>
  <title>Homework</title>
 <body>
  <template id="template"><p>Smile!</p></template>
  <script>
   let num = 3;
   const fragment = document.getElementById('template').content.cloneNode(true);
   while (num-- > 1) {
     fragment.firstChild.before(fragment.firstChild.cloneNode(true));
     fragment.firstChild.textContent += fragment.lastChild.textContent;
   }
   document.body.appendChild(fragment);
  </script>
</html>

p 元素在template 中,**不是** DOM 中template 的子元素;它是DocumentFragment 的子元素,该DocumentFragmenttemplate 元素的content IDL 属性返回。

如果脚本要对template 元素调用appendChild(),则会向template 元素添加一个子元素(与任何其他元素一样);但是,这样做违反了template 元素的内容模型。

template.content

HTMLTemplateElement/content

所有当前引擎都支持。

Firefox22+Safari8+Chrome26+
Opera?Edge79+
Edge (旧版)13+Internet Explorer不支持
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?

返回模板内容(一个DocumentFragment)。

每个template 元素都与一个关联的DocumentFragment 对象相关联,该对象是其模板内容模板内容没有一致性要求。当创建template 元素时,用户代理必须运行以下步骤以建立模板内容

  1. doctemplate 元素的节点文档适当的模板内容拥有者文档

  2. 创建一个DocumentFragment 对象,其节点文档doc,且宿主template 元素。

  3. template 元素的模板内容 设置为新创建的DocumentFragment 对象。

Document doc适当的模板内容拥有者文档 是以下算法返回的Document

  1. 如果doc 不是由本算法创建的Document,则

    1. 如果doc 还没有关联的惰性模板文档,则

      1. new doc 为一个新的Document(其浏览上下文 为 null)。对于上述步骤,这被视为“由本算法创建的Document”。

      2. 如果doc 是一个HTML 文档,则将new doc 也标记为HTML 文档

      3. doc关联的惰性模板文档new doc

    2. doc 设置为doc关联的惰性模板文档

    因此,每个不是由本算法创建的Document 都会获得一个单独的Document 作为其代理,以拥有其所有template 元素的模板内容,以便它们不在浏览上下文 中,因此保持惰性(例如,脚本不运行)。同时,在由本算法创建的Document 对象内的template 元素只需为其内容重用相同的Document 拥有者。

  2. 返回doc

template 元素的采用步骤(参数为nodeoldDocument)如下

  1. docnode节点文档适当的模板内容拥有者文档

    node节点文档node 刚刚被采纳到的Document 对象。

  2. 采用node模板内容(一个DocumentFragment 对象)到doc 中。

content 获取器步骤是返回template模板内容,如果模板内容 不是ShadowRoot 节点;否则返回 null。

shadowRootMode IDL 属性必须反映shadowrootmode 内容属性,仅限于已知值

shadowRootDelegatesFocus IDL 属性必须反映shadowrootdelegatesfocus 内容属性。

shadowRootClonable IDL 属性必须反映shadowrootclonable 内容属性。

shadowRootSerializable IDL 属性必须反映shadowrootserializable 内容属性。


template 元素 node 克隆到副本 copy克隆步骤 必须执行以下步骤

  1. 如果在调用的克隆算法中未设置克隆子节点标志,则返回。

  2. 复制内容克隆 node 的所有子节点(模板内容)的结果,其中 document 设置为 copy模板内容节点文档,并且克隆子节点标志已设置。

  3. 复制内容附加到 copy模板内容

在此示例中,脚本使用 template 提供元素结构而不是手动从标记生成结构,从而使用来自数据结构的数据填充四列表格。

<!DOCTYPE html>
<html lang='en'>
<title>Cat data</title>
<script>
 // Data is hard-coded here, but could come from the server
 var data = [
   { name: 'Pillar', color: 'Ticked Tabby', sex: 'Female (neutered)', legs: 3 },
   { name: 'Hedral', color: 'Tuxedo', sex: 'Male (neutered)', legs: 4 },
 ];
</script>
<table>
 <thead>
  <tr>
   <th>Name <th>Color <th>Sex <th>Legs
 <tbody>
  <template id="row">
   <tr><td><td><td><td>
  </template>
</table>
<script>
 var template = document.querySelector('#row');
 for (var i = 0; i < data.length; i += 1) {
   var cat = data[i];
   var clone = template.content.cloneNode(true);
   var cells = clone.querySelectorAll('td');
   cells[0].textContent = cat.name;
   cells[1].textContent = cat.color;
   cells[2].textContent = cat.sex;
   cells[3].textContent = cat.legs;
   template.parentNode.appendChild(clone);
 }
</script>

此示例对 template 的内容使用cloneNode();它也可以等效地使用 document.importNode(),它执行相同操作。这两个 API 之间的唯一区别是何时更新节点文档:使用 cloneNode() 时,在使用 appendChild() 附加节点时更新;使用 document.importNode() 时,在克隆节点时更新。

4.12.3.1 template 元素与 XSLT 和 XPath 的交互

本节是非规范性的。

本规范未定义 XSLT 和 XPath 如何与 template 元素交互。但是,在没有其他规范实际定义此内容的情况下,以下是一些供实现者参考的准则,旨在与本规范中描述的其他处理保持一致。

4.12.4 slot 元素

元素/slot

所有当前引擎都支持。

Firefox63+Safari10+Chrome53+
Opera?Edge79+
Edge (Legacy)?Internet Explorer不支持
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?

HTMLSlotElement

所有当前引擎都支持。

Firefox63+Safari10+Chrome53+
Opera?Edge79+
Edge (Legacy)?Internet Explorer不支持
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?
类别:
流内容.
短语内容.
可以使用此元素的上下文:
在预期短语内容的地方。
内容模型:
透明
在 text/html 中的标签省略:
两个标签都不能省略。
内容属性:
全局属性
name — 阴影树插槽的名称
可访问性注意事项:
针对作者.
针对实现者.
DOM 接口:
[Exposed=Window]
interface HTMLSlotElement : HTMLElement {
  [HTMLConstructor] constructor();

  [CEReactions] attribute DOMString name;
  sequence<Node> assignedNodes(optional AssignedNodesOptions options = {});
  sequence<Element> assignedElements(optional AssignedNodesOptions options = {});
  undefined assign((Element or Text)... nodes);
};

dictionary AssignedNodesOptions {
  boolean flatten = false;
};

slot 元素定义了一个插槽。它通常用于阴影树中。一个slot 元素表示分配的节点(如果有)及其内容。

name 内容属性可以包含任何字符串值。它表示插槽名称

name 属性用于分配插槽给其他元素:具有name 属性的slot 元素创建一个命名插槽,如果该元素具有一个slot 属性,其值与该name 属性的值匹配,并且slot 元素是阴影树的子节点,其宿主具有该对应的slot 属性值,则任何元素都会分配到该插槽。

slot.name

HTMLSlotElement/name

所有当前引擎都支持。

Firefox63+Safari10+Chrome53+
Opera?Edge79+
Edge (Legacy)?Internet Explorer不支持
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?
可用于获取和设置 slot名称
slot.assignedNodes()

HTMLSlotElement/assignedNodes

所有当前引擎都支持。

Firefox63+Safari10+Chrome53+
Opera?Edge79+
Edge (Legacy)?Internet Explorer不支持
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?
返回 slot分配的节点
slot.assignedNodes({ flatten: true })
返回 slot分配的节点(如果有)以及 slot 的子节点,并对其中遇到的任何slot 元素递归地执行相同的操作,直到没有剩余的slot 元素。
slot.assignedElements()

HTMLSlotElement/assignedElements

所有当前引擎都支持。

Firefox66+Safari12.1+Chrome65+
Opera?Edge79+
Edge (Legacy)?Internet Explorer不支持
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?
返回 slot分配的节点,仅限于元素。
slot.assignedElements({ flatten: true })
返回与 assignedNodes({ flatten: true }) 相同的结果,仅限于元素。
slot.assign(...nodes)

slot手动分配的节点设置为给定的 nodes

name IDL 属性必须反映相同名称的内容属性。

slot 元素具有手动分配的节点,它是由assign() 设置的有序集合可插槽对象。此集合最初为空。

可以使用对可插槽对象的弱引用来实现手动分配的节点集合,因为此集合无法从脚本中直接访问。

assignedNodes(options) 方法的步骤为

  1. 如果 options["flatten"] 为 false,则返回this分配的节点

  2. 返回使用this 查找扁平化的可插槽对象的结果。

assignedElements(options) 方法的步骤为

  1. 如果 options["flatten"] 为 false,则返回this分配的节点,并过滤为仅包含Element 节点。

  2. 返回使用this 查找扁平化的可插槽对象的结果,并过滤为仅包含Element 节点。

HTMLSlotElement/assign

所有当前引擎都支持。

Firefox92+Safari16.4+Chrome86+
Opera?Edge86+
Edge (Legacy)?Internet Explorer不支持
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?

assign(...nodes) 方法的步骤为

  1. 对于this手动分配的节点的每个 node,将 node手动插槽分配设置为 null。

  2. nodesSet 为一个新的有序集合

  3. 对于 nodes 的每个 node

    1. 如果 node手动插槽分配引用了一个slot,则从该slot手动分配的节点中移除 node

    2. node手动插槽分配设置为当前对象

    3. node追加nodesSet

  4. 当前对象手动分配的节点设置为nodesSet

  5. 当前对象根节点运行为树分配可插槽节点