1. 4.12 脚本
      1. 4.12.1 script 元素
        1. 4.12.1.1 脚本语言
        2. 4.12.1.2 script 元素内容的限制
        3. 4.12.1.3 外部脚本的内联文档
      2. 4.12.2 noscript 元素
      3. 4.12.3 template 元素
      4. 4.12.4 slot 元素

4.12 脚本

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

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

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

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

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

4.12.1 script 元素

元素/脚本

所有当前引擎都支持。

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

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

元素/脚本#attr-type

所有当前引擎都支持。

Firefox1+Safari≤4+Chrome1+
Opera?Edge79+
Edge(旧版)12+Internet Explorer
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属性不得在模块脚本上指定(如果指定,则会被忽略)。

Element/script#attr-async

所有当前引擎都支持。

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

Element/script#attr-defer

所有当前引擎都支持。

Firefox3.5+Safari3+Chrome1+
Opera?Edge79+
Edge (Legacy)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)的旧版 Web 浏览器回退到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属性没有直接影响;这些属性仅在下面描述的特定时间使用。

script.text [ = value ]

返回元素的子文本内容

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

HTMLScriptElement.supports(type)

HTMLScriptElement/supports_static

所有当前引擎都支持。

Firefox94+Safari16+Chrome96+
Opera?Edge96+
Edge (Legacy)?Internet ExplorerNo
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?

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

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

在此示例中,使用了两个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>

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

以下示例演示了如何使用`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 脚本语言

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

4.12.1.2 `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.3 `外部脚本的内联文档`

如果`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.2 `noscript` 元素

元素/noscript

所有当前引擎都支持。

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

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

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

在`head` 元素中,如果`noscript` 元素的`脚本已禁用`

noscript 元素只能包含 linkstylemeta 元素。

head 元素中,如果为 noscript 元素启用了脚本

noscript 元素只能包含文本,但使用 noscript 元素作为 context 元素,文本内容作为 input 调用 HTML 片段解析算法必须生成一个节点列表,该列表仅包含 linkstylemeta 元素,如果它们是 noscript 元素的子元素,则这些元素必须符合规范,并且没有解析错误。

head 元素之外,如果为 noscript 元素禁用了脚本

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

head 元素之外,如果为 noscript 元素启用了脚本

noscript 元素只能包含文本,但文本必须满足以下条件:运行以下算法必须生成一个符合规范的文档,其中不包含 noscript 元素和 script 元素,并且算法中的任何步骤都不会引发异常或导致 HTML 解析器标记解析错误。

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

所有这些曲折都是必需的,因为由于历史原因,HTML 解析器根据解析器被调用时是否启用了脚本以不同的方式处理 noscript 元素。

noscript 元素不得用于 XML 文档

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

在以下示例中,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>

当脚本被禁用时,将显示一个按钮以在服务器端进行计算。当脚本启用时,将即时计算该值。

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 元素

元素/模板

所有当前引擎都支持。

Firefox22+Safari8+Chrome26+
Opera?Edge79+
Edge (Legacy)13+Internet ExplorerNo
Firefox Android?Safari iOS?Chrome Android?WebView AndroidYesSamsung Internet?Opera Android?
类别:
元数据内容.
流内容.
短语内容.
支持脚本的元素.
可以使用此元素的上下文:
在预期 元数据内容 的地方。
在预期 短语内容 的地方。
在预期 支持脚本的元素 的地方。
作为不具有 span 属性的 colgroup 元素的子元素。
内容模型:
(为了清楚起见,请参阅示例)。
text/html 中的标签省略:
两个标签都不能省略。
内容属性:
全局属性
shadowrootmode — 启用流式声明式阴影根
shadowrootdelegatesfocus — 在声明式阴影根上设置 委托焦点
shadowrootclonable — 在声明式阴影根上设置 可克隆
shadowrootserializable — 在声明式阴影根上设置可序列化
可访问性注意事项:
针对作者.
针对实现者.
DOM 接口:
使用 HTMLTemplateElement

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

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

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

关键字状态简要说明
open open 模板元素表示一个开放的声明式阴影根。
closed closed 模板元素表示一个封闭的声明式阴影根。

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

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

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

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

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

由于 DOM 操作,template 元素也可能包含 Text 节点和元素节点;但是,存在任何节点都违反了 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>

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

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

template.content

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

在此示例中,脚本使用来自数据结构的数据填充一个四列表,使用 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>

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

4.12.4 slot 元素

元素/插槽

所有当前引擎都支持。

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

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

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

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

slot.name
可用于获取和设置slot名称
slot.assignedNodes()
返回slot分配的节点
slot.assignedNodes({ flatten: true })
返回slot分配的节点(如果有)以及否则slot的子节点,并对其中遇到的任何slot 元素递归执行相同的操作,直到没有剩余的slot 元素。
slot.assignedElements()
返回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 ExplorerNo
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手动插槽分配设置为this

    3. 追加nodenodesSet

  4. this手动分配的节点设置为nodesSet

  5. this运行为树分配可插槽