This specification is like no other — it has been processed with you, the humble web developer, in mind.
The focus of this specification is readability and ease of access. Unlike the full HTML Standard, this "developer's edition" removes information that only browser vendors need know. It is automatically produced from the full specification by our build tooling, and thus always in sync with the latest developments in HTML.
To read about its conception, construction, and future, read the original press release, and the blog post about its relaunch.
Finally, feel free to contribute on GitHub to make this edition better for everyone!
Scripts allow authors to add interactivity to their documents.
Authors are encouraged to use declarative alternatives to scripting where possible, as declarative mechanisms are often more maintainable, and many users disable scripting.
For example, instead of using script to show or hide a section to show more details, the
details element could be used.
Authors are also encouraged to make their applications degrade gracefully in the absence of scripting support.
For example, if an author provides a link in a table header to dynamically resort the table, the link could also be made to function without scripts by requesting the sorted table from the server.
script elementsrc
attribute, depends on the value of the type attribute, but must match
script content restrictions.src
attribute, the element must be either empty or contain only
script documentation that also matches script
content restrictions.src — Address of the resourcetype — Type of scriptnomodule — Prevents execution in user agents that support module scriptscharset — Character encoding of the external script resourceasync — Execute script when available, without blockingdefer — Defer script executioncrossorigin — How the element handles crossorigin requestsnonce — Cryptographic nonce used in Content Security Policy checks [CSP]integrity — Integrity metadata used in Subresource Integrity checks [SRI]HTMLScriptElement.The script element allows authors to include dynamic script and data blocks in
their documents. The element does not represent content for the
user.
The type attribute allows customization of
the type of script represented:
Omitting the attribute, or setting it to a JavaScript MIME type, means that
the script is a classic script, to be interpreted according to the JavaScript Script top-level production. Classic scripts are affected by the
charset, async, and defer
attributes. Authors should omit the attribute, instead of redundantly giving a JavaScript
MIME type.
Setting the attribute to an ASCII case-insensitive match for the string
"module" means that the script is a module script, to be
interpreted according to the JavaScript Module top-level
production. Module scripts are not affected by the charset and defer
attributes.
Setting the attribute to any other value means that the script is a data
block, which is not processed. None of the script attributes (except type itself) have any effect on data blocks. Authors must use
a valid MIME type that is not a JavaScript MIME type to denote data
blocks.
The requirement that data blocks
must be denoted using a valid MIME type is in place to avoid
potential future collisions. If this specification ever adds additional types of
script, they will be triggered by setting the type attribute to something which is not a MIME type, like how
the "module" value denotes module
scripts. By using a valid MIME type now, you ensure that your data block will not ever be
reinterpreted as a different script type, even in future user agents.
Classic scripts and module
scripts may either be embedded inline or may be imported from an external file using the
src attribute, which if specified gives the URL
of the external script resource to use. If src is specified,
it must be a valid non-empty URL potentially surrounded by spaces. The contents of
inline script elements, or the external script resource, must conform with the
requirements of the JavaScript specification's Script or Module productions, for classic
scripts and module scripts respectively. [JAVASCRIPT]
When used to include data blocks, the data must be embedded
inline, the format of the data must be given using the type attribute, and the contents of the script
element must conform to the requirements defined for the format used. The src, charset, async, nomodule,
defer, crossorigin, nonce
and integrity attributes must not be specified.
The nomodule attribute is a boolean
attribute that prevents a script from being executed in user agents that support
module scripts. This allows selective execution of module scripts in modern user agents and classic scripts in older user agents, as shown
below. The nomodule attribute must not be
specified on module scripts (and will be ignored if it
is).
The charset attribute gives the character
encoding of the external script resource. The attribute must not be specified if the src attribute is not present, or if the script is not a
classic script. (Module scripts are always
interpreted as UTF-8.) If the attribute is set, its value must be an ASCII
case-insensitive match for one of the labels of an
encoding, and must specify the same encoding as the charset parameter of the Content-Type
metadata of the external file, if any. [ENCODING]
The async and defer attributes are boolean attributes that indicate how the script should be evaluated. Classic scripts may specify defer or async; module scripts may specify async. The defer and
async attributes must not be specified if the src attribute is not present.
There are several possible modes that can be selected using these attributes, and depending on the script's type.
For classic scripts, if the async attribute is present, then the classic script will be
fetched in parallel to parsing and evaluated as soon as it is available (potentially
before parsing completes). If the async attribute is not
present but the defer attribute is present, then the
classic script will be fetched in parallel and evaluated when the page has finished
parsing. If neither attribute is present, then the script is fetched and evaluated immediately,
blocking parsing until these are both complete.
For module scripts, if the async attribute is present, then the module script and all its
dependencies will be fetched in parallel to parsing, and the module script will
be evaluated as soon as it is available (potentially before parsing completes). Otherwise, the
module script and its dependencies will be fetched in parallel to parsing and
evaluated when the page has finished parsing. (The defer
attribute has no effect on module scripts.)
This is all summarized in the following schematic diagram:
The exact processing details for these attributes are, for mostly historical
reasons, somewhat non-trivial, involving a number of aspects of HTML. The implementation
requirements are therefore by necessity scattered throughout the specification. The algorithms
below (in this section) describe the core of this processing, but these algorithms reference and
are referenced by the parsing rules for script start and end tags in HTML, in foreign content,
and in XML, the rules for the document.write() method, the handling of scripting, etc.
The defer attribute may be specified even if the async attribute is specified, to cause legacy Web browsers that
only support defer (and not async) to fall back to the defer behavior instead of the blocking behavior that
is the default.
The crossorigin attribute is a
CORS settings attribute. For classic scripts,
it controls whether error information will be exposed, when the script is obtained from other origins. For module scripts, it
controls the credentials mode used for
cross-origin requests.
Unlike classic scripts, module scripts require the use of the CORS protocol for cross-origin fetching.
The nonce attribute represents a cryptographic nonce ("number
used once") which can be used by Content Security Policy to determine whether or not
the script specified by an element will be executed. The value is text. [CSP]
The integrity attribute represents the integrity metadata for requests which this
element is responsible for. The value is text. The integrity attribute must not be specified when embedding a
module script or when the src attribute is not
specified. [SRI]
Changing the src, type, charset, nomodule, async, defer, crossorigin, nonce
and integrity attributes dynamically has no direct
effect; these attributes are only used at specific times described below.
text [ = value ]Returns the child text content of the element.
Can be set, to replace the element's children with the given value.
When inserted using the document.write()
method, script elements usually
execute (typically blocking further script execution or HTML parsing). When inserted using the
innerHTML and outerHTML
attributes, they do not execute at all.
In this example, two script elements are used. One embeds an external
classic script, and the other includes some data as a data block.
<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>
The data in this case might be used by the script to generate the map of a video game. The data doesn't have to be used that way, though; maybe the map data is actually embedded in other parts of the page's markup, and the data block here is just used by the site's search engine to help users who are looking for particular features in their game maps.
The following sample shows how a script element can be used to define a function
that is then used by other parts of the document, as part of a classic script. It
also shows how a script element can be used to invoke script while the document is
being parsed, in this case to initialize the form's output.
<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>
The following sample shows how a script element can be used to include an
external module script.
<script type="module" src="app.js"></script>
This module, and all its dependencies (expressed through JavaScript import statements in the source file), will be fetched. Once the entire
resulting module graph has been imported, and the document has finished parsing, the contents of
app.js will be evaluated.
Additionally, if code from another script element in the same Window
imports the module from app.js (e.g. via import
"./app.js";), then the same module script created by the
former script element will be imported.
This example shows how to include a module script for modern user agents, and a classic script for older user agents:
<script type="module" src="app.js"></script> <script nomodule src="classic-app-bundle.js"></script>
In modern user agents that support module scripts, the
script element with the nomodule attribute
will be ignored, and the script element with a type of "module" will be fetched and
evaluated (as a module script). Conversely, older user agents will ignore the
script element with a type of "module", as that is an unknown script type for them — but they will have no
problem fetching and evaluating the other script element (as a classic
script), since they do not implement the nomodule attribute.
The following sample shows how a script element can be used to write an inline
module script that performs a number of substitutions on the document's text, in
order to make for a more interesting reading experience (e.g. on a news site): [XKCD1288]
<script type="module">
import { walkAllTextNodeDescendants } from "./dom-utils.js";
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>
Some notable features gained by using a module script include the ability to import functions
from other JavaScript modules, strict mode by default, and how top-level declarations do not
introduce new properties onto the global object. Also note that no matter where
this script element appears in the document, it will not be evaluated until both
document parsing has complete and its dependency (dom-utils.js) has been
fetched and evaluated.
A JavaScript MIME type is a MIME type string that is one of the following and refers to JavaScript: [JAVASCRIPT]
application/ecmascript
application/javascript
application/x-ecmascript
application/x-javascript
text/ecmascript
text/javascript
text/javascript1.0
text/javascript1.1
text/javascript1.2
text/javascript1.3
text/javascript1.4
text/javascript1.5
text/jscript
text/livescript
text/x-ecmascript
text/x-javascript
User agents must recognize all JavaScript MIME types.
script elementsThe easiest and safest way to avoid the rather strange restrictions described in
this section is to always escape "<!--" as "<\!--", "<script" as "<\script", and "</script" as "<\/script" when these sequences appear in literals in scripts (e.g. in
strings, regular expressions, or comments), and to avoid writing code that uses such constructs in
expressions. Doing so avoids the pitfalls that the restrictions in this section are prone to
triggering: namely, that, for historical reasons, parsing of script blocks in HTML is
a strange and exotic practice that acts unintuitively in the face of these sequences.
The textContent of a script element must match the script production in the following ABNF, the character set for which is 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 (>)
When a script element contains script documentation, there are
further restrictions on the contents of the element, as described in the section below.
The following script illustrates this issue. Suppose you have a script that contains a string, as in:
var example = 'Consider this string: <!-- <script>'; console.log(example);
If one were to put this string directly in a script block, it would violate the
restrictions above:
<script> var example = 'Consider this string: <!-- <script>'; console.log(example); </script>
The bigger problem, though, and the reason why it would violate those restrictions, is that
actually the script would get parsed weirdly: the script block above is not terminated.
That is, what looks like a "</script>" end tag in this snippet is
actually still part of the script block. The script doesn't execute (since it's not
terminated); if it somehow were to execute, as it might if the markup looked as follows, it would
fail because the script (highlighted here) is not valid JavaScript:
<script> var 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>
What is going on here is that for legacy reasons, "<!--" and "<script" strings in script elements in HTML need to be balanced
in order for the parser to consider closing the block.
By escaping the problematic strings as mentioned at the top of this section, the problem is avoided entirely:
<script> var example = 'Consider this string: <\!-- <\script>'; console.log(example); </script> <!-- this is just a comment between script blocks --> <script> ... // this is a new script block </script>
It is possible for these sequences to naturally occur in script expressions, as in the following examples:
if (x<!--y) { ... }
if ( player<script ) { ... }
In such cases the characters cannot be escaped, but the expressions can be rewritten so that the sequences don't occur, as in:
if (x < !--y) { ... }
if (!--y > x) { ... }
if (!(--y) > x) { ... }
if (player < script) { ... }
if (script > player) { ... }
Doing this also avoids a different pitfall as well: for related historical reasons, the string "<!--" in classic scripts is actually treated as a line comment start, just like "//".
If a script element's src attribute is
specified, then the contents of the script element, if any, must be such that the
value of the text IDL attribute, which is derived from the
element's contents, matches the documentation production in the following
ABNF, the character set for which is 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 (/)
This corresponds to putting the contents of the element in JavaScript comments.
This requirement is in addition to the earlier restrictions on the syntax of
contents of script elements.
This allows authors to include documentation, such as license information or API information,
inside their documents while still referring to external script files. The syntax is constrained
so that authors don't accidentally include what looks like valid script while also providing a
src attribute.
<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>
noscript elementhead element of an HTML document, if there are no ancestor noscript elements.noscript elements.head element: in any order, zero or more link elements, zero or more style elements, and zero or more meta elements.head element: transparent, but there must be no noscript element descendants.HTMLElement.The noscript element represents nothing if scripting is enabled, and represents its children if
scripting is disabled. It is used to present different
markup to user agents that support scripting and those that don't support scripting, by affecting
how the document is parsed.
When used in HTML documents, the allowed content model is as follows:
head element, if scripting is
disabled for the noscript elementThe noscript element must contain only link, style,
and meta elements.
head element, if scripting is enabled
for the noscript elementThe noscript element must contain only text, except that invoking the
HTML fragment parsing algorithm with
the noscript element as the context
element and the text contents as the input must result in a list of nodes
that consists only of link, style, and meta elements that
would be conforming if they were children of the noscript element, and no parse errors.
head elements, if scripting is
disabled for the noscript elementThe noscript element's content model is transparent, with the
additional restriction that a noscript element must not have a noscript
element as an ancestor (that is, noscript can't be nested).
head elements, if scripting is
enabled for the noscript elementThe noscript element must contain only text, except that the text must be such
that running the following algorithm results in a conforming document with no
noscript elements and no script elements, and such that no step in the
algorithm throws an exception or causes an HTML parser to flag a parse
error:
script element from the document.noscript element in the document. For every
noscript element in that list, perform the following steps:
noscript
element.outerHTML attribute of the
noscript element to the value of s. (This, as a
side-effect, causes the noscript element to be removed from the document.) [DOMPARSING]All these contortions are required because, for historical reasons, the
noscript element is handled differently by the HTML parser based on
whether scripting was enabled or not when the parser was
invoked.
The noscript element must not be used in XML documents.
The noscript element is only effective in the HTML
syntax, it has no effect in the XML syntax. This is because the way it works
is by essentially "turning off" the parser when scripts are enabled, so that the contents of the
element are treated as pure text and not as real elements. XML does not define a mechanism by
which to do this.
In the following example, a noscript element is
used to provide fallback for a script.
<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>
When script is disabled, a button appears to do the calculation on the server side. When script is enabled, the value is computed on-the-fly instead.
The noscript element is a blunt instrument. Sometimes, scripts might be enabled,
but for some reason the page's script might fail. For this reason, it's generally better to avoid
using noscript, and to instead design the script to change the page from being a
scriptless page to a scripted page on the fly, as in the next example:
<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>
The above technique is also useful in XML documents, since noscript
is not allowed there.
template elementcolgroup element that doesn't have a span attribute.HTMLTemplateElement.The template element is used to declare fragments of HTML that can be cloned and
inserted in the document by script.
In a rendering, the template element represents nothing.
The template contents of a template element are not children of the
element itself. Instead, they are stored in a DocumentFragment associated with a
different Document without a browsing context so as to avoid the
template contents interfering with the main Document. (For example, this
avoids form controls from being submitted, scripts from executing, and so forth.) The
template contents have no conformance
requirements.
For example, consider the following document:
<!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>
The p element in the template is not a child of the
template in the DOM; it is a child of the DocumentFragment returned by
the template element's content IDL
attribute.
If the script were to call appendChild() on the
template element, that would add a child to the template element (as
for any other element); however, doing so is a violation of the template element's
content model.
contentReturns the template contents (a DocumentFragment).
In this example, a script populates a table four-column with data from a data structure, using
a template to provide the element structure instead of manually generating the
structure from markup.
<!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>
This example uses cloneNode() on the
template's contents; it could equivalently have used document.importNode(), which does the same thing. The
only difference between these two APIs is when the node document is updated: with
cloneNode() it is updated when the nodes are appended
with appendChild(), with document.importNode() it is updated when the nodes are
cloned.
slot elementname — Name of shadow tree slotHTMLSlotElement.The slot element defines a slot. It is
typically used in a shadow tree. A slot element represents
its assigned nodes, if any, and its contents otherwise.
The name content attribute may contain any
string value. It represents a slot's name.
The name attribute is used to assign slots to other elements: a slot element with a
name attribute creates a named slot to which any element is assigned if that element has a slot attribute whose
value matches that name attribute's value, and the
slot element is a child of the shadow tree whose root's
host has that corresponding slot attribute value.
nameassignedNodes()assignedNodes({ flatten: true })slot elements encountered therein, recursively,
until there are no slot elements left.The name IDL attribute must reflect
the content attribute of the same name.
The assignedNodes(options)
method, when invoked, must run these steps:
If the value of options's flatten member is false, then
return this element's assigned nodes.
Return the result of finding flattened slotables with this element.