```
To remove a specified element when knowing its parent node:
```js
-let d = document.getElementById("top");
-let d_nested = document.getElementById("nested");
-let throwawayNode = d.removeChild(d_nested);
+const parent = document.getElementById("parent");
+const child = document.getElementById("child");
+const throwawayNode = parent.removeChild(child);
```
To remove a specified element without having to specify its parent node:
```js
-let node = document.getElementById("nested");
+const node = document.getElementById("child");
if (node.parentNode) {
node.parentNode.removeChild(node);
}
@@ -70,7 +70,7 @@ if (node.parentNode) {
To remove all children from an element:
```js
-let element = document.getElementById("idOfParent");
+const element = document.getElementById("idOfParent");
while (element.firstChild) {
element.removeChild(element.firstChild);
}
@@ -80,35 +80,35 @@ while (element.firstChild) {
```html
-
```
```js
-let top = document.getElementById("top");
-let nested = document.getElementById("nested");
+const parent = document.getElementById("parent");
+const child = document.getElementById("child");
// Throws Uncaught TypeError
-let garbage = top.removeChild(nested);
+const garbage = parent.removeChild(child);
```
### Causing a NotFoundError
```html
-
-
+
```
```js
-let top = document.getElementById("top");
-let nested = document.getElementById("nested");
+const parent = document.getElementById("parent");
+const child = document.getElementById("child");
// This first call correctly removes the node
-let garbage = top.removeChild(nested);
+const garbage = parent.removeChild(child);
// Throws NotFoundError
-garbage = top.removeChild(nested);
+garbage = parent.removeChild(child);
```
## Specifications
diff --git a/files/en-us/web/api/notifications_api/using_the_notifications_api/index.md b/files/en-us/web/api/notifications_api/using_the_notifications_api/index.md
index 75282c144fdb748..d6581d5f936f125 100644
--- a/files/en-us/web/api/notifications_api/using_the_notifications_api/index.md
+++ b/files/en-us/web/api/notifications_api/using_the_notifications_api/index.md
@@ -5,7 +5,7 @@ page-type: guide
browser-compat: api.Notification
---
-{{APIRef("Web Notifications")}}{{AvailableInWorkers}}{{securecontext_header}}
+{{DefaultAPISidebar("Web Notifications")}}{{AvailableInWorkers}}{{securecontext_header}}
The [Notifications API](/en-US/docs/Web/API/Notifications_API) lets a web page or app send notifications that are displayed outside the page at the system level; this lets web apps send information to a user even if the application is idle or in the background. This article looks at the basics of using this API in your own apps.
diff --git a/files/en-us/web/api/offscreencanvasrenderingcontext2d/commit/index.md b/files/en-us/web/api/offscreencanvasrenderingcontext2d/commit/index.md
index 009c7a9c6143c3c..3cdbba1de3e8745 100644
--- a/files/en-us/web/api/offscreencanvasrenderingcontext2d/commit/index.md
+++ b/files/en-us/web/api/offscreencanvasrenderingcontext2d/commit/index.md
@@ -21,7 +21,7 @@ commit()
## Examples
-```javascript
+```js
const placeholder = document.createElement("canvas");
const offscreen = placeholder.transferControlToOffscreen();
const ctx = offscreenCanvas.getContext("2d");
diff --git a/files/en-us/web/api/page_visibility_api/index.md b/files/en-us/web/api/page_visibility_api/index.md
index 4f99dd92b40e53c..628e1aa9d8792be 100644
--- a/files/en-us/web/api/page_visibility_api/index.md
+++ b/files/en-us/web/api/page_visibility_api/index.md
@@ -94,8 +94,8 @@ This example pauses audio when the user switches to a different tab, and plays w
const audio = document.querySelector("audio");
// Handle page visibility change:
-// - If the page is hidden, pause the video
-// - If the page is shown, play the video
+// - If the page is hidden, pause the audio
+// - If the page is shown, play the audio
document.addEventListener("visibilitychange", () => {
if (document.hidden) {
audio.pause();
diff --git a/files/en-us/web/api/performancenavigationtiming/activationstart/index.md b/files/en-us/web/api/performancenavigationtiming/activationstart/index.md
new file mode 100644
index 000000000000000..bd90ecc1a650dc5
--- /dev/null
+++ b/files/en-us/web/api/performancenavigationtiming/activationstart/index.md
@@ -0,0 +1,70 @@
+---
+title: "PerformanceNavigationTiming: activationStart property"
+short-title: activationStart
+slug: Web/API/PerformanceNavigationTiming/activationStart
+page-type: web-api-instance-property
+status:
+ - experimental
+browser-compat: api.PerformanceNavigationTiming.activationStart
+---
+
+{{APIRef("Performance API")}}{{SeeCompatTable}}
+
+The **`activationStart`** read-only property represents the time between when a document starts prerendering and when it is activated.
+
+## Value
+
+A {{domxref("DOMHighResTimeStamp")}} representing the duration between document prerendering start and activation in milliseconds.
+
+The value is `0` if the page has not prerendered or is still prerendering.
+
+## Examples
+
+### Detecting prerendered pages
+
+When a prerendered document is activated, `activationStart` is set to the current time. The following function can check whether a page is {{DOMxRef("Document.prerendering","prerendering")}} or has already prerendered:
+
+```js
+function pagePrerendered() {
+ return (
+ document.prerendering ||
+ self.performance?.getEntriesByType?.("navigation")[0]?.activationStart > 0
+ );
+}
+```
+
+### Measuring user-perceived performance milestones
+
+With prerendered pages, a page may have been created long before it was actually navigated to. When using the [Performance API](/en-US/docs/Web/API/Performance_API) on prerendered pages, it is vital to compare returned values with `activationStart` in order to avoid misleading measurements.
+
+```js
+// Time to when activation occurred
+let activationStart =
+ performance.getEntriesByType("navigation")[0].activationStart;
+
+// Time to first paint
+let firstPaint = performance.getEntriesByName("first-paint")[0].startTime;
+
+// Time to first contentful paint
+let firstContentfulPaint = performance.getEntriesByName(
+ "first-contentful-paint",
+)[0].startTime;
+
+console.log("time to first paint: " + (firstPaint - activationStart));
+console.log(
+ "time to first-contentful-paint: " + (firstContentfulPaint - activationStart),
+);
+```
+
+## Specifications
+
+{{Specifications}}
+
+## Browser compatibility
+
+{{Compat}}
+
+## See also
+
+- [Speculation Rules API](/en-US/docs/Web/API/Speculation_Rules_API)
+- [Speculative loading](/en-US/docs/Web/Performance/Speculative_loading)
diff --git a/files/en-us/web/api/performancenavigationtiming/index.md b/files/en-us/web/api/performancenavigationtiming/index.md
index 6b398d713d67ea1..808b88cbcb51c76 100644
--- a/files/en-us/web/api/performancenavigationtiming/index.md
+++ b/files/en-us/web/api/performancenavigationtiming/index.md
@@ -37,6 +37,8 @@ This interface also extends the following {{domxref('PerformanceResourceTiming')
The interface also supports the following properties:
+- {{domxref('PerformanceNavigationTiming.activationStart')}} {{ReadOnlyInline}} {{experimental_inline}}
+ - : A {{domxref("DOMHighResTimeStamp")}} representing the time between when a document starts prerendering and when it is activated.
- {{domxref('PerformanceNavigationTiming.domComplete')}} {{ReadOnlyInline}}
- : A {{domxref("DOMHighResTimeStamp")}} representing the time immediately before the user agent sets the document's [`readyState`](/en-US/docs/Web/API/Document/readyState) to `"complete"`.
- {{domxref('PerformanceNavigationTiming.domContentLoadedEventEnd')}} {{ReadOnlyInline}}
diff --git a/files/en-us/web/api/performanceresourcetiming/deliverytype/index.md b/files/en-us/web/api/performanceresourcetiming/deliverytype/index.md
index 7be8aabbb455c79..b05b8b820da353b 100644
--- a/files/en-us/web/api/performanceresourcetiming/deliverytype/index.md
+++ b/files/en-us/web/api/performanceresourcetiming/deliverytype/index.md
@@ -10,18 +10,18 @@ browser-compat: api.PerformanceResourceTiming.deliveryType
{{APIRef("Performance API")}}{{SeeCompatTable}}
-The **`deliveryType`** read-only property is a string indicating how the resource was delivered — for example, indicating whether the resource was delivered from the cache.
+The **`deliveryType`** read-only property is a string indicating how the resource was delivered — for example from the cache or from a navigational prefetch.
## Value
-The `deliveryType` property can have the following values:.
+A string, which can be one of the following values:
-- `cache`
- - : If the resource was retrieved from the cache
+- `"cache"`
+ - : The resource was retrieved from the cache.
+- `"navigational-prefetch"`
+ - : The resource was retrieved from a prefetched response stored in an in-memory cache via the [Speculation Rules API](/en-US/docs/Web/API/Speculation_Rules_API).
- `""` (empty string)
- - : If none of the other defined delivery types
-
-This set of delivery types is expected to be expanded in the future — for example, to indicate preloaded resources and to indicate prefetched navigation requests.
+ - : Returned if none of the above delivery types apply.
## Examples
diff --git a/files/en-us/web/api/performanceresourcetiming/index.md b/files/en-us/web/api/performanceresourcetiming/index.md
index b3c0e735774c1d4..aff5cfadcbc5c79 100644
--- a/files/en-us/web/api/performanceresourcetiming/index.md
+++ b/files/en-us/web/api/performanceresourcetiming/index.md
@@ -101,7 +101,7 @@ Additionally, this interface exposes the following properties containing more in
- {{domxref('PerformanceResourceTiming.serverTiming')}} {{ReadOnlyInline}}
- : An array of {{domxref("PerformanceServerTiming")}} entries containing server timing metrics.
- {{domxref("PerformanceResourceTiming.deliveryType")}} {{experimental_inline}} {{ReadOnlyInline}}
- - : Indicates how the resource was delivered; currently can either be "`cache`", or the empty string. (The set of possible values is expected to be expanded in the future.)
+ - : Indicates how the resource was delivered — for example from the cache or from a navigational prefetch.
## Instance methods
diff --git a/files/en-us/web/api/picture-in-picture_api/index.md b/files/en-us/web/api/picture-in-picture_api/index.md
index 16e77f4e13a0d9b..7ec80b2f8cb54d5 100644
--- a/files/en-us/web/api/picture-in-picture_api/index.md
+++ b/files/en-us/web/api/picture-in-picture_api/index.md
@@ -7,7 +7,9 @@ browser-compat: api.PictureInPictureWindow
{{DefaultAPISidebar("Picture-in-Picture API")}}
-The **Picture-in-Picture API** allow websites to create a floating video window always on top of other windows so that users may continue consuming media while they interact with other content sites, or applications on their device.
+The **Picture-in-Picture API** allow websites to create a floating, always-on-top video window. This allows users to continue consuming media while they interact with other sites or applications on their device.
+
+> **Note:** The [Document Picture-in-Picture API](/en-US/docs/Web/API/Document_Picture-in-Picture_API) extends the Picture-in-Picture API to allow the always-on-top window to be populated with _any_ arbitrary HTML content, not just a video.
## Interfaces
@@ -112,3 +114,4 @@ If the value is `null`, no video is in the floating window. So we can request a
- {{DOMxRef("Document.exitPictureInPicture()")}}
- {{DOMxRef("Document.pictureInPictureElement")}}
- {{CSSxRef(":picture-in-picture")}}
+- The [Document Picture-in-Picture API](/en-US/docs/Web/API/Document_Picture-in-Picture_API)
diff --git a/files/en-us/web/api/publickeycredential/index.md b/files/en-us/web/api/publickeycredential/index.md
index 2e004e37c8c5bfd..207dc3e4c783568 100644
--- a/files/en-us/web/api/publickeycredential/index.md
+++ b/files/en-us/web/api/publickeycredential/index.md
@@ -24,29 +24,29 @@ The **`PublicKeyCredential`** interface provides information about a public key
- : Inherited from {{domxref("Credential")}} and overridden to be the [base64url encoding](/en-US/docs/Glossary/Base64) of {{domxref("PublicKeyCredential.rawId")}}.
- {{domxref("PublicKeyCredential.rawId")}} {{ReadOnlyInline()}} {{securecontext_inline}}
-
- : An {{jsxref("ArrayBuffer")}} that holds the globally unique identifier for this `PublicKeyCredential`. This identifier can be used to look up credentials for future calls to {{domxref("CredentialsContainer.get()","navigator.credentials.get()")}}.
-
- {{domxref("PublicKeyCredential.response")}} {{ReadOnlyInline()}} {{securecontext_inline}}
-
- : An instance of an {{domxref("AuthenticatorResponse")}} object. It is either of type {{domxref("AuthenticatorAttestationResponse")}} if the `PublicKeyCredential` was the results of a {{domxref("CredentialsContainer.create()","navigator.credentials.create()")}} call, or of type {{domxref("AuthenticatorAssertionResponse")}} if the `PublicKeyCredential` was the result of a {{domxref("CredentialsContainer.get()","navigator.credentials.get()")}} call.
-
- `PublicKeyCredential.type` {{ReadOnlyInline()}} {{securecontext_inline}}
- : Inherited from {{domxref("Credential")}}. Always set to `public-key` for `PublicKeyCredential` instances.
## Static methods
- {{domxref("PublicKeyCredential.isConditionalMediationAvailable()")}} {{securecontext_inline}}
-
- : Returns a {{jsxref("Promise")}} which resolves to `true` if conditional mediation is available.
-
- {{domxref("PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable_static", "PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()")}} {{securecontext_inline}}
- : Returns a {{jsxref("Promise")}} which resolves to `true` if an authenticator bound to the platform is capable of _verifying_ the user.
+- {{domxref("PublicKeyCredential.parseCreationOptionsFromJSON_static", "PublicKeyCredential.parseCreationOptionsFromJSON()")}} {{experimental_inline}}{{securecontext_inline}}
+ - : Convenience method for deserializing server-sent credential registration data when [registering a user with credentials](/en-US/docs/Web/API/Web_Authentication_API#creating_a_key_pair_and_registering_a_user).
+- {{domxref("PublicKeyCredential.parseRequestOptionsFromJSON_static", "PublicKeyCredential.parseRequestOptionsFromJSON()")}} {{experimental_inline}}{{securecontext_inline}}
+ - : Convenience method for deserializing server-sent credential request data when [authenticating a (registered) user](/en-US/docs/Web/API/Web_Authentication_API#authenticating_a_user).
## Instance methods
- {{domxref("PublicKeyCredential.getClientExtensionResults()")}} {{securecontext_inline}}
- : If any extensions were requested, this method will return the results of processing those extensions.
+- {{domxref("PublicKeyCredential.toJSON()")}} {{experimental_inline}}{{securecontext_inline}}
+ - : Convenience method for creating a JSON string representation of a {{domxref("PublicKeyCredential")}} for sending to the server when [registering a user with credentials](/en-US/docs/Web/API/Web_Authentication_API#creating_a_key_pair_and_registering_a_user) and [authenticating a registered user](/en-US/docs/Web/API/Web_Authentication_API#authenticating_a_user).
## Examples
@@ -55,7 +55,7 @@ The **`PublicKeyCredential`** interface provides information about a public key
Here, we use {{domxref("CredentialsContainer.create()","navigator.credentials.create()")}} to generate a new credential.
```js
-const publicKey = {
+const createCredentialOptions = {
challenge: new Uint8Array([
21, 31, 105 /* 29 more random bytes generated by the server */,
]),
@@ -77,7 +77,7 @@ const publicKey = {
};
navigator.credentials
- .create({ publicKey })
+ .create({ createCredentialOptions })
.then((newCredentialInfo) => {
const response = newCredentialInfo.response;
const clientExtensionsResults =
@@ -93,14 +93,14 @@ navigator.credentials
Here, we fetch an existing credential from an authenticator, using {{domxref("CredentialsContainer.get()","navigator.credentials.get()")}}.
```js
-const options = {
+const requestCredentialOptions = {
challenge: new Uint8Array([
/* bytes sent from the server */
]),
};
navigator.credentials
- .get({ publicKey: options })
+ .get({ requestCredentialOptions })
.then((credentialInfoAssertion) => {
// send assertion response back to the server
// to proceed with the control of the credential
diff --git a/files/en-us/web/api/publickeycredential/parsecreationoptionsfromjson_static/index.md b/files/en-us/web/api/publickeycredential/parsecreationoptionsfromjson_static/index.md
new file mode 100644
index 000000000000000..46333873e0df9b9
--- /dev/null
+++ b/files/en-us/web/api/publickeycredential/parsecreationoptionsfromjson_static/index.md
@@ -0,0 +1,112 @@
+---
+title: "PublicKeyCredential: parseCreationOptionsFromJSON() static method"
+short-title: parseCreationOptionsFromJSON()
+slug: Web/API/PublicKeyCredential/parseCreationOptionsFromJSON_static
+page-type: web-api-static-method
+status:
+ - experimental
+browser-compat: api.PublicKeyCredential.parseCreationOptionsFromJSON_static
+---
+
+{{APIRef("Web Authentication API")}} {{SeeCompatTable}}{{securecontext_header}}
+
+The **`parseCreationOptionsFromJSON()`** static method of the {{domxref("PublicKeyCredential")}} interface converts a {{glossary("JSON type representation")}} into its corresponding [`publicKey` create credentials options object structure](/en-US/docs/Web/API/CredentialsContainer/create#publickey_object_structure).
+
+The method is a convenience function for converting credential options information provided by a relying party server to the form that a web app can use to [create a credential](/en-US/docs/Web/API/Web_Authentication_API#creating_a_key_pair_and_registering_a_user).
+
+## Syntax
+
+```js-nolint
+PublicKeyCredential.parseCreationOptionsFromJSON(options)
+```
+
+### Parameters
+
+- `options`
+
+ - : An object with the same structure as the Web Authentication API [`publicKey` create credentials options object](/en-US/docs/Web/API/CredentialsContainer/create#publickey_object_structure), but with [base64url](/en-US/docs/Glossary/Base64)-encoded strings used in place of buffer properties.
+
+### Return value
+
+An object with the Web Authentication API [`publicKey` create credentials options object structure](/en-US/docs/Web/API/CredentialsContainer/create#publickey_object_structure).
+
+### Exceptions
+
+- `EncodingError` {{domxref("DOMException")}}
+ - : Thrown if any part of the `options` object cannot be converted into the [`publicKey` create credentials options object structure](/en-US/docs/Web/API/CredentialsContainer/create#publickey_object_structure).
+
+## Description
+
+The Web Authentication process for [creating a key pair and registering a user](/en-US/docs/Web/API/Web_Authentication_API#creating_a_key_pair_and_registering_a_user) involves a relying party server sending the web app information needed to create a credential, including details about the user identity, the relying party, and a "challenge".
+The web app passes this information to an authenticator to create the credential, by calling [`navigator.credentials.create()`](/en-US/docs/Web/API/CredentialsContainer/create) with an argument that contains the server-supplied data in the [`publicKey` create credentials options object structure](/en-US/docs/Web/API/CredentialsContainer/create#publickey_object_structure).
+
+The specification does not define how the information needed for creating a credential is sent.
+A convenient approach is for the server to encapsulate the information in a {{glossary("JSON type representation")}} of the [`publicKey` create credentials options object](/en-US/docs/Web/API/CredentialsContainer/create#publickey_object_structure) that mirrors its structure but encodes buffer properties such as the `challenge` and `user.id` as [base64url](/en-US/docs/Glossary/Base64) strings.
+This object can be serialized to a [JSON](/en-US/docs/Glossary/JSON) string, sent to the web app and deserialized, and then converted to the [`publicKey` create credentials options object structure](/en-US/docs/Web/API/CredentialsContainer/create#publickey_object_structure) using **`parseCreationOptionsFromJSON()`**.
+
+## Examples
+
+When registering a new user, a relying party server will supply information about the expected credentials to the web app.
+The code below defines this information in the form described in the [`options` parameter](#options) above (taken from the ["getting an AuthenticatorAttestationResponse"](/en-US/docs/Web/API/AuthenticatorResponse#getting_an_authenticatorattestationresponse) in `AuthenticatorResponse`):
+
+```js
+const createCredentialOptionsJSON = {
+ challenge:
+ "21, 31, 105, " /* 29 more random bytes generated by the server in this string */,
+ rp: {
+ name: "Example CORP",
+ id: "login.example.com",
+ },
+ user: {
+ id: "16",
+ name: "canand@example.com",
+ displayName: "Carina Anand",
+ },
+ pubKeyCredParams: [
+ {
+ type: "public-key",
+ alg: -7,
+ },
+ ],
+};
+```
+
+Because this object only uses JSON data types, it can be be serialized to JSON using [`JSON.stringify()`](/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) and sent to the web app.
+
+```js
+JSON.stringify(createCredentialOptionsJSON);
+```
+
+The web app can deserialize the JSON string back to a `createCredentialOptionsJSON` object (not shown).
+The **`parseCreationOptionsFromJSON()`** method is used to convert that object to the form that can be used in `navigator.credentials.create()`:
+
+```js
+// Convert options to form used by create()
+const createCredentialOptions =
+ PublicKeyCredential.parseCreationOptionsFromJSON(
+ createCredentialOptionsJSON, // JSON-type representation
+ );
+
+navigator.credentials
+ .create({ createCredentialOptions })
+ .then((newCredentialInfo) => {
+ // Handle the new credential information here.
+ })
+ .catch((err) => {
+ console.error(err);
+ });
+```
+
+## Specifications
+
+{{Specifications}}
+
+## Browser compatibility
+
+{{Compat}}
+
+## See also
+
+- [Web Authentication API](/en-US/docs/Web/API/Web_Authentication_API)
+- {{domxref("PublicKeyCredential.parseRequestOptionsFromJSON_static", "PublicKeyCredential.parseRequestOptionsFromJSON()")}}
+- {{domxref("PublicKeyCredential.toJSON()")}}
diff --git a/files/en-us/web/api/publickeycredential/parserequestoptionsfromjson_static/index.md b/files/en-us/web/api/publickeycredential/parserequestoptionsfromjson_static/index.md
new file mode 100644
index 000000000000000..d57448a65b2ec78
--- /dev/null
+++ b/files/en-us/web/api/publickeycredential/parserequestoptionsfromjson_static/index.md
@@ -0,0 +1,100 @@
+---
+title: "PublicKeyCredential: parseRequestOptionsFromJSON() static method"
+short-title: parseRequestOptionsFromJSON()
+slug: Web/API/PublicKeyCredential/parseRequestOptionsFromJSON_static
+page-type: web-api-static-method
+status:
+ - experimental
+browser-compat: api.PublicKeyCredential.parseRequestOptionsFromJSON_static
+---
+
+{{APIRef("Web Authentication API")}} {{SeeCompatTable}}{{securecontext_header}}
+
+The **`parseRequestOptionsFromJSON()`** static method of the {{domxref("PublicKeyCredential")}} interface converts a {{glossary("JSON type representation")}} into its corresponding [`publicKey` request credentials options object structure](/en-US/docs/Web/API/CredentialsContainer/get#publickey_object_structure).
+
+The method is a convenience function for converting information provided by a relying server to a web app in order to request an existing credential.
+
+## Syntax
+
+```js-nolint
+PublicKeyCredential.parseRequestOptionsFromJSON(options)
+```
+
+### Parameters
+
+- `options`
+
+ - : An object with the same structure as the Web Authentication API [`publicKey` request credentials options object](/en-US/docs/Web/API/CredentialsContainer/get#publickey_object_structure), but with [base64url](/en-US/docs/Glossary/Base64)-encoded strings used in place of buffer properties.
+
+### Return value
+
+An object with the Web Authentication API [`publicKey` request credentials options object structure](/en-US/docs/Web/API/CredentialsContainer/get#publickey_object_structure).
+
+### Exceptions
+
+- `EncodingError` {{domxref("DOMException")}}
+ - : Thrown if any part of the `options` object cannot be converted into the [`publicKey` request credentials options object structure](/en-US/docs/Web/API/CredentialsContainer/get#publickey_object_structure).
+
+## Description
+
+The Web Authentication process for [authenticating a (registered) user](/en-US/docs/Web/API/Web_Authentication_API#authenticating_a_user) involves a relying party server sending the web app information needed to find an existing credential, including details about the user identity, the relying party, a "challenge", and optionally where to look for the credential: for example on a local built-in authenticator, or on an external one over USB, BLE, and so on.
+The web app passes this information to an authenticator to find the credential, by calling [`navigator.credentials.get()`](/en-US/docs/Web/API/CredentialsContainer/get) with an argument that contains the server-supplied data in the [`publicKey` request credentials options object structure](/en-US/docs/Web/API/CredentialsContainer/get#publickey_object_structure).
+
+The specification does not define how the information needed for requesting a credential is sent.
+A convenient approach is for the server to encapsulate the information in a {{glossary("JSON type representation")}} of the [`publicKey` request credentials options object](/en-US/docs/Web/API/CredentialsContainer/get#publickey_object_structure) that mirrors its structure but encodes buffer properties such as the `challenge` as [base64url](/en-US/docs/Glossary/Base64) strings.
+This object can be serialized to a [JSON](/en-US/docs/Glossary/JSON) string, sent to the web app and deserialized, and then converted to the [`publicKey` request credentials options object structure](/en-US/docs/Web/API/CredentialsContainer/get#publickey_object_structure) using **`parseRequestOptionsFromJSON()`**.
+
+## Examples
+
+When authorizing an already registered user, a relying party server will supply the web app with information about the requested credentials, the relying party, and a challenge.
+The code below defines this information in the form described in the [`options` parameter](#options) above:
+
+```js
+const requestCredentialOptionsJSON = {
+ challenge: new Uint8Array([139, 66, 181, 87, 7, 203, ...]),
+ rpId: "acme.com",
+ allowCredentials: [{
+ type: "public-key",
+ id: new Uint8Array([64, 66, 25, 78, 168, 226, 174, ...])
+ }],
+ userVerification: "required",
+ }
+```
+
+Because this object only uses JSON data types, it can be be serialized to JSON using [`JSON.stringify()`](/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) and sent to the web app.
+
+```js
+JSON.stringify(requestCredentialOptionsJSON);
+```
+
+The web app can deserialize the JSON string back to a `requestCredentialOptionsJSON` object (not shown).
+The **`parseRequestOptionsFromJSON()`** method is used to convert that object to the form that can be used in `navigator.credentials.get()`:
+
+```js
+// Convert options to form used by get()
+const publicKey = PublicKeyCredential.parseRequestOptionsFromJSON(
+ requestCredentialOptionsJSON, // JSON-type representation
+);
+
+navigator.credentials
+ .get({ publicKey })
+ .then((returnedCredentialInfo) => {
+ // Handle the returned credential information here.
+ })
+ .catch((err) => {
+ console.error(err);
+ });
+```
+
+## Specifications
+
+{{Specifications}}
+
+## Browser compatibility
+
+{{Compat}}
+
+## See also
+
+- [Web Authentication API](/en-US/docs/Web/API/Web_Authentication_API)
+- {{domxref("PublicKeyCredential.parseCreationOptionsFromJSON_static", "PublicKeyCredential.parseCreationOptionsFromJSON()")}}
diff --git a/files/en-us/web/api/publickeycredential/tojson/index.md b/files/en-us/web/api/publickeycredential/tojson/index.md
new file mode 100644
index 000000000000000..afbbde0c0f7930a
--- /dev/null
+++ b/files/en-us/web/api/publickeycredential/tojson/index.md
@@ -0,0 +1,91 @@
+---
+title: "PublicKeyCredential: toJSON() method"
+short-title: toJSON()
+slug: Web/API/PublicKeyCredential/toJSON
+page-type: web-api-instance-method
+status:
+ - experimental
+browser-compat: api.PublicKeyCredential.toJSON
+---
+
+{{APIRef("Web Authentication API")}} {{SeeCompatTable}}{{securecontext_header}}
+
+The **`toJSON()`** method of the {{domxref("PublicKeyCredential")}} interface returns a {{glossary("JSON type representation")}} of a {{domxref("PublicKeyCredential")}}.
+
+The properties of the returned object depend on whether the credential is returned by [`navigator.credentials.create()`](/en-US/docs/Web/API/CredentialsContainer/create) when [creating a key pair and registering a user](/en-US/docs/Web/API/Web_Authentication_API#creating_a_key_pair_and_registering_a_user), or [`navigator.credentials.get()`](/en-US/docs/Web/API/CredentialsContainer/get) when [authenticating a user](/en-US/docs/Web/API/Web_Authentication_API#authenticating_a_user).
+
+This method is automatically invoked when web app code calls [`JSON.stringify()`](/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) to serialize a {{domxref("PublicKeyCredential")}} so that it can be sent to relying party server when registering or authenticating a user.
+It not intended to be called directly in web app code.
+
+## Syntax
+
+```js-nolint
+toJSON()
+```
+
+### Parameters
+
+None.
+
+### Return value
+
+A {{glossary("JSON type representation")}} of a [`PublicKeyCredential`](/en-US/docs/Web/API/PublicKeyCredential) object.
+
+The included properties depend on whether the credential was returned by [`navigator.credentials.create()`](/en-US/docs/Web/API/CredentialsContainer/create) on registration, or [`navigator.credentials.get()`](/en-US/docs/Web/API/CredentialsContainer/get) when authenticating a user.
+The values and types of included properties are the same as for [`PublicKeyCredential`](/en-US/docs/Web/API/PublicKeyCredential), with the exception that [base64url](/en-US/docs/Glossary/Base64)-encoded strings are used in place of buffer properties.
+
+The object properties are:
+
+- `id`
+ - : The value returned by {{domxref("PublicKeyCredential.id")}}.
+- `rawId`
+ - : A [base64url](/en-US/docs/Glossary/Base64)-encoded version of {{domxref("PublicKeyCredential.rawId")}}.
+- `authenticatorAttachment` {{optional_inline}}
+ - : The value returned by {{domxref("PublicKeyCredential.authenticatorAttachment")}}.
+- `type`
+ - : The string `"public-key"`.
+- `clientExtensionResults`
+ - : An array contaning [base64url](/en-US/docs/Glossary/Base64)-encoded versions of the values returned by {{domxref("PublicKeyCredential.getClientExtensionResults()")}}.
+- `response`
+
+ - : The response property object depends on whether the credentials are returned following a registration or authentication operation.
+
+ - When registering a new user `response` will be a JSON-type representation of {{domxref("AuthenticatorAttestationResponse")}} where buffer values have been [base64url](/en-US/docs/Glossary/Base64) encoded.
+
+ - When authenticating a user the returned value will be a JSON-type representation version of {{domxref("AuthenticatorAssertionResponse")}} where buffer values have been [base64url](/en-US/docs/Glossary/Base64) encoded.
+
+## Examples
+
+When registering a new user, a relying party server will supply information about the expected credentials to the web app.
+The web app calls [`navigator.credentials.create()`](/en-US/docs/Web/API/CredentialsContainer/create) with the received information (`createCredentialOptions` below), which returns a promise that fulfills with the new credential (a {{domxref("PublicKeyCredential")}}).
+
+```js
+const newCredentialInfo = await navigator.credentials.create({
+ createCredentialOptions,
+});
+```
+
+The web app then serializes the returned credential using `JSON.stringify()` (which in turn calls `toJSON()`) and posts it back to the server.
+
+```js
+const registration_url = "https://example.com/registration";
+const apiRegOptsResp = await fetch(registration_url, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(newCredentialInfo), //Calls newCredentialInfo.toJSON
+});
+```
+
+## Specifications
+
+{{Specifications}}
+
+## Browser compatibility
+
+{{Compat}}
+
+## See also
+
+- [Web Authentication API](/en-US/docs/Web/API/Web_Authentication_API)
+- {{domxref("PublicKeyCredential.parseCreationOptionsFromJSON_static", "PublicKeyCredential.parseCreationOptionsFromJSON()")}}
+- {{domxref("PublicKeyCredential.parseRequestOptionsFromJSON_static", "PublicKeyCredential.parseRequestOptionsFromJSON()")}}
diff --git a/files/en-us/web/api/push_api/index.md b/files/en-us/web/api/push_api/index.md
index aa12b7e4060fb59..1ccfed48c94ee0c 100644
--- a/files/en-us/web/api/push_api/index.md
+++ b/files/en-us/web/api/push_api/index.md
@@ -69,5 +69,3 @@ Mozilla's [ServiceWorker Cookbook](https://github.com/mdn/serviceworker-cookbook
- [Sending VAPID identified WebPush Notifications via Mozilla's Push Service](https://blog.mozilla.org/services/2016/08/23/sending-vapid-identified-webpush-notifications-via-mozillas-push-service/)
- [Push notifications overview](https://web.dev/push-notifications-overview/)
- [Service Worker API](/en-US/docs/Web/API/Service_Worker_API)
-
-{{DefaultAPISidebar("Push API")}}
diff --git a/files/en-us/web/api/pushevent/data/index.md b/files/en-us/web/api/pushevent/data/index.md
index 07168959ef13c28..c14545436ac3ac0 100644
--- a/files/en-us/web/api/pushevent/data/index.md
+++ b/files/en-us/web/api/pushevent/data/index.md
@@ -12,7 +12,7 @@ The `data` read-only property of the **`PushEvent`** interface returns a referen
## Value
-A {{domxref("PushMessageData")}} object.
+A {{domxref("PushMessageData")}} object or `null` if no `data` member is passed when the event instance initialized.
## Examples
diff --git a/files/en-us/web/api/pushsubscription/expirationtime/index.md b/files/en-us/web/api/pushsubscription/expirationtime/index.md
index 8882d505736df3d..112f2fdcfcf6104 100644
--- a/files/en-us/web/api/pushsubscription/expirationtime/index.md
+++ b/files/en-us/web/api/pushsubscription/expirationtime/index.md
@@ -11,11 +11,11 @@ browser-compat: api.PushSubscription.expirationTime
The **`expirationTime`** read-only property of the
{{domxref("PushSubscription")}} interface returns a {{domxref("DOMHighResTimeStamp")}}
of the subscription expiration time associated with the push subscription, if there is
-one, or null otherwise.
+one, or `null` otherwise.
## Value
-A {{domxref("DOMHighResTimeStamp")}}.
+A {{domxref("DOMHighResTimeStamp")}} or `null`.
## Specifications
diff --git a/files/en-us/web/api/pushsubscription/getkey/index.md b/files/en-us/web/api/pushsubscription/getkey/index.md
index 41d31e65565fbcb..fb4d44dce42696b 100644
--- a/files/en-us/web/api/pushsubscription/getkey/index.md
+++ b/files/en-us/web/api/pushsubscription/getkey/index.md
@@ -34,7 +34,7 @@ getKey(name)
### Return value
-An {{jsxref("ArrayBuffer")}}.
+An {{jsxref("ArrayBuffer")}} or `null` if no public key can be found.
## Examples
diff --git a/files/en-us/web/api/pushsubscription/tojson/index.md b/files/en-us/web/api/pushsubscription/tojson/index.md
index 2490aa7b63190fc..62e63271360d3fe 100644
--- a/files/en-us/web/api/pushsubscription/tojson/index.md
+++ b/files/en-us/web/api/pushsubscription/tojson/index.md
@@ -24,8 +24,8 @@ None.
### Return value
-A JSON object. It currently only contains the subscription endpoint, as an
-`endpoint` member.
+A JSON object. It contains the subscription endpoint, `expirationTime` and public keys, as an
+`endpoint` member, an `expirationTime` member and a `keys` member.
## Examples
diff --git a/files/en-us/web/api/pushsubscriptionoptions/applicationserverkey/index.md b/files/en-us/web/api/pushsubscriptionoptions/applicationserverkey/index.md
index 4247b5ccd9d68f4..f465ce947d5c19d 100644
--- a/files/en-us/web/api/pushsubscriptionoptions/applicationserverkey/index.md
+++ b/files/en-us/web/api/pushsubscriptionoptions/applicationserverkey/index.md
@@ -12,7 +12,7 @@ The **`applicationServerKey`** read-only property of the {{domxref("PushSubscrip
## Value
-A public key your push server uses to send messages to client apps via a push server. This value is part of a signing key pair generated by your application server, and usable with elliptic curve digital signature (ECDSA), over the P-256 curve.
+A public key your push server uses to send messages to client apps via a push server. This value is part of a signing key pair generated by your application server, and usable with elliptic curve digital signature (ECDSA), over the P-256 curve. If no `applicationServerKey` member is passed when initialized, it will be set to `null`.
## Examples
diff --git a/files/en-us/web/api/readablestreambyobreader/index.md b/files/en-us/web/api/readablestreambyobreader/index.md
index 414f5cf0c84c49c..9e0de40c1868798 100644
--- a/files/en-us/web/api/readablestreambyobreader/index.md
+++ b/files/en-us/web/api/readablestreambyobreader/index.md
@@ -47,7 +47,7 @@ As this is a "Bring Your Own Buffer" reader, we also need to create an `ArrayBuf
```js
const reader = stream.getReader({ mode: "byob" });
-let buffer = new ArrayBuffer(4000);
+let buffer = new ArrayBuffer(200);
```
A function that uses the reader is shown below.
@@ -62,30 +62,33 @@ function readStream(reader) {
let bytesReceived = 0;
let offset = 0;
- while (offset < buffer.byteLength) {
- // read() returns a promise that resolves when a value has been received
- reader
- .read(new Uint8Array(buffer, offset, buffer.byteLength - offset))
- .then(function processBytes({ done, value }) {
- // Result objects contain two properties:
- // done - true if the stream has already given all its data.
- // value - some data. Always undefined when done is true.
-
- if (done) {
- // There is no more data in the stream
- return;
- }
-
- buffer = value.buffer;
- offset += value.byteLength;
- bytesReceived += value.byteLength;
-
- // Read some more, and call this function again
- return reader
- .read(new Uint8Array(buffer, offset, buffer.byteLength - offset))
- .then(processBytes);
- });
- }
+ // read() returns a promise that resolves when a value has been received
+ reader
+ .read(new Uint8Array(buffer, offset, buffer.byteLength - offset))
+ .then(function processText({ done, value }) {
+ // Result objects contain two properties:
+ // done - true if the stream has already given all its data.
+ // value - some data. Always undefined when done is true.
+
+ if (done) {
+ logConsumer(`readStream() complete. Total bytes: ${bytesReceived}`);
+ return;
+ }
+
+ buffer = value.buffer;
+ offset += value.byteLength;
+ bytesReceived += value.byteLength;
+
+ logConsumer(
+ `Read ${value.byteLength} (${bytesReceived}) bytes: ${value}`,
+ );
+ result += value;
+
+ // Read some more, and call this function again
+ return reader
+ .read(new Uint8Array(buffer, offset, buffer.byteLength - offset))
+ .then(processText);
+ });
}
```
diff --git a/files/en-us/web/api/request/cache/index.md b/files/en-us/web/api/request/cache/index.md
index 53a63d1309764e4..32fb8232ae28c16 100644
--- a/files/en-us/web/api/request/cache/index.md
+++ b/files/en-us/web/api/request/cache/index.md
@@ -118,7 +118,7 @@ fetch("some.json", {
/* consume the (possibly stale) response */
})
.catch((error) => {
- /* Can be an AbortError/DOMError or a TypeError */
+ /* Can be an AbortError/DOMException or a TypeError */
});
```
diff --git a/files/en-us/web/api/request/index.md b/files/en-us/web/api/request/index.md
index d796ade220bd1a9..102c29c8c8c5c94 100644
--- a/files/en-us/web/api/request/index.md
+++ b/files/en-us/web/api/request/index.md
@@ -27,7 +27,7 @@ You can create a new `Request` object using the {{domxref("Request.Request","Req
- {{domxref("Request.credentials")}} {{ReadOnlyInline}}
- : Contains the credentials of the request (e.g., `omit`, `same-origin`, `include`). The default is `same-origin`.
- {{domxref("Request.destination")}} {{ReadOnlyInline}}
- - : Returns a string describing the request's destination. This is a string indicating the type of content being requested.
+ - : A string describing the type of content being requested.
- {{domxref("Request.headers")}} {{ReadOnlyInline}}
- : Contains the associated {{domxref("Headers")}} object of the request.
- {{domxref("Request.integrity")}} {{ReadOnlyInline}}
diff --git a/files/en-us/web/api/request/mode/index.md b/files/en-us/web/api/request/mode/index.md
index 8918a1e4f71d457..f4c55c56cfb3cdb 100644
--- a/files/en-us/web/api/request/mode/index.md
+++ b/files/en-us/web/api/request/mode/index.md
@@ -24,15 +24,10 @@ to determine if cross-origin requests lead to valid responses, and which propert
set, the result is an error. You could use this to ensure that a request is always
being made to your origin.
- `no-cors`
- - : Prevents the method from being anything other than
- `HEAD`, `GET` or `POST`, and the headers from
- being anything other than [simple headers](https://fetch.spec.whatwg.org/#simple-header). If any
- ServiceWorkers intercept these requests, they may not add or override any headers
- except for those that are [simple headers](https://fetch.spec.whatwg.org/#simple-header). In
- addition, JavaScript may not access any properties of the resulting
- {{domxref("Response")}}. This ensures that ServiceWorkers do not affect the
- semantics of the Web and prevents security and privacy issues arising from leaking
- data across domains.
+ - : Prevents the method from being anything other than `HEAD`, `GET` or `POST`, and the headers from being anything other than {{Glossary("CORS-safelisted request header", "CORS-safelisted request headers")}}.
+ If any ServiceWorkers intercept these requests, they may not add or override any headers except for those that are {{Glossary("CORS-safelisted request header", "CORS-safelisted request headers")}}.
+ In addition, JavaScript may not access any properties of the resulting {{domxref("Response")}}.
+ This ensures that ServiceWorkers do not affect the semantics of the Web and prevents security and privacy issues arising from leaking data across domains.
- `cors`
- : Allows cross-origin requests, for example to access various
APIs offered by 3rd party vendors. These are expected to adhere to the [CORS protocol](/en-US/docs/Web/HTTP/CORS). Only a [limited set](https://fetch.spec.whatwg.org/#concept-filtered-response-cors) of headers are exposed in the {{domxref("Response")}}, but the body is
diff --git a/files/en-us/web/api/resizeobserverentry/contentboxsize/index.md b/files/en-us/web/api/resizeobserverentry/contentboxsize/index.md
index 03cc884cda35628..97b6d6c91770316 100644
--- a/files/en-us/web/api/resizeobserverentry/contentboxsize/index.md
+++ b/files/en-us/web/api/resizeobserverentry/contentboxsize/index.md
@@ -41,20 +41,31 @@ size.
```js
const resizeObserver = new ResizeObserver((entries) => {
- const calcBorderRadius = (size1, size2) =>
- `${Math.min(100, size1 / 10 + size2 / 10)}px`;
-
- for (const entry of entries) {
- if (entry.borderBoxSize?.length > 0) {
- entry.target.style.borderRadius = calcBorderRadius(
- entry.borderBoxSize[0].inlineSize,
- entry.borderBoxSize[0].blockSize,
- );
+ for (let entry of entries) {
+ if (entry.contentBoxSize) {
+ // The standard makes contentBoxSize an array...
+ if (entry.contentBoxSize[0]) {
+ entry.target.style.borderRadius =
+ Math.min(
+ 100,
+ entry.contentBoxSize[0].inlineSize / 10 +
+ entry.contentBoxSize[0].blockSize / 10,
+ ) + "px";
+ } else {
+ // ...but old versions of Firefox treat it as a single item
+ entry.target.style.borderRadius =
+ Math.min(
+ 100,
+ entry.contentBoxSize.inlineSize / 10 +
+ entry.contentBoxSize.blockSize / 10,
+ ) + "px";
+ }
} else {
- entry.target.style.borderRadius = calcBorderRadius(
- entry.contentRect.width,
- entry.contentRect.height,
- );
+ entry.target.style.borderRadius =
+ Math.min(
+ 100,
+ entry.contentRect.width / 10 + entry.contentRect.height / 10,
+ ) + "px";
}
}
});
diff --git a/files/en-us/web/api/response/body/index.md b/files/en-us/web/api/response/body/index.md
index b5590df77307cac..3c7fda2a0c6d419 100644
--- a/files/en-us/web/api/response/body/index.md
+++ b/files/en-us/web/api/response/body/index.md
@@ -14,10 +14,14 @@ The **`body`** read-only property of the {{domxref("Response")}} interface is a
A {{domxref("ReadableStream")}}, or else [`null`](/en-US/docs/Web/JavaScript/Reference/Operators/null) for any `Response` object [constructed](/en-US/docs/Web/API/Response/Response) with a null [`body`](/en-US/docs/Web/API/Response/Response#body) property, or for any actual [HTTP response](/en-US/docs/Web/HTTP/Messages#http_responses) that has no [body](/en-US/docs/Web/HTTP/Messages#body_2).
+The stream is a [readable byte stream](/en-US/docs/Web/API/Streams_API/Using_readable_byte_streams), which supports zero-copy reading using a {{domxref("ReadableStreamBYOBReader")}}.
+
> **Note:** Current browsers don't actually conform to the spec requirement to set the `body` property to `null` for responses with no body (for example, responses to [`HEAD`](/en-US/docs/Web/HTTP/Methods/HEAD) requests, or [`204 No Content`](/en-US/docs/Web/HTTP/Status/204) responses).
## Examples
+### Copying an image
+
In our [simple stream pump](https://mdn.github.io/dom-examples/streams/simple-pump/) example we fetch an image,
expose the response's stream using `response.body`, create a reader using {{domxref("ReadableStream.getReader()", "ReadableStream.getReader()")}},
then enqueue that stream's chunks into a second, custom readable stream — effectively creating an identical copy of the image.
@@ -59,6 +63,22 @@ fetch("./tortoise.png")
.catch((err) => console.error(err));
```
+### Creating a BYOB reader
+
+In this example we construct a {{domxref("ReadableStreamBYOBReader")}} from the body using {{domxref("ReadableStream.getReader()", "ReadableStream.getReader({mode: 'byob'})")}}. We can then use this reader to implement zero copy transfer of the response data.
+
+```js
+async function getProducts(url) {
+ const response = await fetch(url);
+ const reader = response.body.getReader({ mode: "byob" });
+ // read the response
+}
+
+getProducts(
+ "https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json",
+);
+```
+
## Specifications
{{Specifications}}
diff --git a/files/en-us/web/api/response/headers/index.md b/files/en-us/web/api/response/headers/index.md
index 71c3ee082493102..eea726a761effb6 100644
--- a/files/en-us/web/api/response/headers/index.md
+++ b/files/en-us/web/api/response/headers/index.md
@@ -30,14 +30,15 @@ const myImage = document.querySelector("img");
const myRequest = new Request("flowers.jpg");
-fetch(myRequest).then((response) => {
- // for each response header, log an array with header name as key
- console.log(...response.headers);
- response.blob().then((myBlob) => {
+fetch(myRequest)
+ .then((response) => {
+ console.log("response.headers =", response.headers);
+ return response.blob();
+ })
+ .then((myBlob) => {
const objectURL = URL.createObjectURL(myBlob);
myImage.src = objectURL;
});
-});
```
## Specifications
diff --git a/files/en-us/web/api/response/status/index.md b/files/en-us/web/api/response/status/index.md
index d2d3a18429e94b8..1ee2c19f4a909aa 100644
--- a/files/en-us/web/api/response/status/index.md
+++ b/files/en-us/web/api/response/status/index.md
@@ -30,13 +30,15 @@ const myImage = document.querySelector("img");
const myRequest = new Request("flowers.jpg");
-fetch(myRequest).then((response) => {
- console.log(response.status); // returns 200
- response.blob().then((myBlob) => {
+fetch(myRequest)
+ .then((response) => {
+ console.log("response.status =", response.status); // response.status = 200
+ return response.blob();
+ })
+ .then((myBlob) => {
const objectURL = URL.createObjectURL(myBlob);
myImage.src = objectURL;
});
-});
```
## Specifications
diff --git a/files/en-us/web/api/response/statustext/index.md b/files/en-us/web/api/response/statustext/index.md
index 0c80860d665dc80..c1a566dcd53ac7a 100644
--- a/files/en-us/web/api/response/statustext/index.md
+++ b/files/en-us/web/api/response/statustext/index.md
@@ -32,13 +32,15 @@ const myImage = document.querySelector("img");
const myRequest = new Request("flowers.jpg");
-fetch(myRequest).then((response) => {
- console.log(response.statusText); // returns "OK" if the response returned successfully
- response.blob().then((myBlob) => {
+fetch(myRequest)
+ .then((response) => {
+ console.log("response.statusText =", response.statusText); // response.statusText = "OK"
+ return response.blob();
+ })
+ .then((myBlob) => {
const objectURL = URL.createObjectURL(myBlob);
myImage.src = objectURL;
});
-});
```
## Specifications
diff --git a/files/en-us/web/api/response/type/index.md b/files/en-us/web/api/response/type/index.md
index 656b68d7fb31cb0..8c38782b59620dc 100644
--- a/files/en-us/web/api/response/type/index.md
+++ b/files/en-us/web/api/response/type/index.md
@@ -40,13 +40,15 @@ const myImage = document.querySelector("img");
const myRequest = new Request("flowers.jpg");
-fetch(myRequest).then((response) => {
- console.log(response.type); // returns basic by default
- response.blob().then((myBlob) => {
+fetch(myRequest)
+ .then((response) => {
+ console.log("response.type =", response.type); // response.type = 'basic'
+ return response.blob();
+ })
+ .then((myBlob) => {
const objectURL = URL.createObjectURL(myBlob);
myImage.src = objectURL;
});
-});
```
## Specifications
diff --git a/files/en-us/web/api/response/url/index.md b/files/en-us/web/api/response/url/index.md
index f693013dc510e0d..d9d28d0ec72d6d8 100644
--- a/files/en-us/web/api/response/url/index.md
+++ b/files/en-us/web/api/response/url/index.md
@@ -27,13 +27,15 @@ const myImage = document.querySelector("img");
const myRequest = new Request("flowers.jpg");
-fetch(myRequest).then((response) => {
- console.log(response.url); // returns https://developer.mozilla.org/en-US/docs/Web/API/Response/flowers.jpg
- response.blob().then((myBlob) => {
+fetch(myRequest)
+ .then((response) => {
+ console.log("response.url =", response.url); // response.url = https://mdn.github.io/dom-examples/fetch/fetch-response/flowers.jpg
+ return response.blob();
+ })
+ .then((myBlob) => {
const objectURL = URL.createObjectURL(myBlob);
myImage.src = objectURL;
});
-});
```
## Specifications
diff --git a/files/en-us/web/api/rtcoutboundrtpstreamstats/index.md b/files/en-us/web/api/rtcoutboundrtpstreamstats/index.md
index b3c1c09dec5da90..62b966d8b6b773c 100644
--- a/files/en-us/web/api/rtcoutboundrtpstreamstats/index.md
+++ b/files/en-us/web/api/rtcoutboundrtpstreamstats/index.md
@@ -21,9 +21,6 @@ The statistics can be obtained by iterating the {{domxref("RTCStatsReport")}} re
- : An integer value which indicates the total number of Full Intra Request (FIR) packets which this {{domxref("RTCRtpSender")}} has sent to the remote {{domxref("RTCRtpReceiver")}}. This is an indicator of how often the stream has lagged, requiring frames to be skipped in order to catch up. _Valid only for video streams._
- {{domxref("RTCOutboundRtpStreamStats.framesEncoded", "framesEncoded")}}
- : The number of frames that have been successfully encoded so far for sending on this RTP stream. _Only valid for video streams._
-- {{domxref("RTCOutboundRtpStreamStats.lastPacketSentTimestamp", "lastPacketSentTimestamp")}}
- - : A {{domxref("DOMHighResTimeStamp")}} indicating the time at which the last packet was sent for this SSRC.
- The {{domxref("RTCOutboundRtpStreamStats.timestamp", "timestamp")}} property, on the other hand, indicates the time at which the `RTCOutboundRtpStreamStats` object was generated.
- {{domxref("RTCOutboundRtpStreamStats.nackCount", "nackCount")}}
- : An integer value indicating the total number of Negative ACKnolwedgement (NACK) packets this `RTCRtpSender` has received from the remote {{domxref("RTCRtpReceiver")}}.
- {{domxref("RTCOutboundRtpStreamStats.perDscpPacketsSent", "perDscpPacketsSent")}}
diff --git a/files/en-us/web/api/rtcoutboundrtpstreamstats/lastpacketsenttimestamp/index.md b/files/en-us/web/api/rtcoutboundrtpstreamstats/lastpacketsenttimestamp/index.md
deleted file mode 100644
index c6667506a36a981..000000000000000
--- a/files/en-us/web/api/rtcoutboundrtpstreamstats/lastpacketsenttimestamp/index.md
+++ /dev/null
@@ -1,29 +0,0 @@
----
-title: "RTCOutboundRtpStreamStats: lastPacketSentTimestamp property"
-short-title: lastPacketSentTimestamp
-slug: Web/API/RTCOutboundRtpStreamStats/lastPacketSentTimestamp
-page-type: web-api-instance-property
-browser-compat: api.RTCStatsReport.type_outbound-rtp.lastPacketSentTimestamp
----
-
-{{APIRef("WebRTC")}}
-
-The **`lastPacketSentTimestamp`**
-property of the {{domxref("RTCOutboundRtpStreamStats")}} dictionary indicates the time
-at which the {{domxref("RTCRtpSender")}} described by this
-{{domxref("RTCOutboundRtpStreamStats")}} object last transmitted a packet to the
-remote receiver.
-
-## Value
-
-A {{domxref("DOMHighResTimeStamp")}} which specifies the time at which the most recently received packet arrived on this RTP stream.
-
-> **Note:** This value differs from the {{domxref("RTCOutboundRtpStreamStats.timestamp", "timestamp")}}, which represents the time at which the statistics object was created.
-
-## Specifications
-
-{{Specifications}}
-
-## Browser compatibility
-
-{{Compat}}
diff --git a/files/en-us/web/api/rtcpeerconnection/addtrack/index.md b/files/en-us/web/api/rtcpeerconnection/addtrack/index.md
index a0b2903adaed94f..33154d449901a4c 100644
--- a/files/en-us/web/api/rtcpeerconnection/addtrack/index.md
+++ b/files/en-us/web/api/rtcpeerconnection/addtrack/index.md
@@ -29,26 +29,21 @@ addTrack(track, stream1, stream2, /* …, */ streamN)
- `stream1`, …, `streamN` {{optional_inline}}
- : One or more local {{domxref("MediaStream")}} objects to which the track should be added.
-The specified `track` doesn't necessarily have to already be part of any of
-the specified `stream`s. Instead, the `stream`s are a way to group
-tracks together on the receiving end of the connection, making sure they are
-synchronized. Any tracks that are added to the same stream on the local end of the
-connection will be on the same stream on the remote end.
+The specified `track` doesn't necessarily have to already be part of any of the specified `stream`s.
+Instead, the `stream`s are a way to group tracks together on the receiving end of the connection, making sure they are synchronized.
+Any tracks that are added to the same stream on the local end of the connection will be on the same stream on the remote end.
### Return value
The {{domxref("RTCRtpSender")}} object which will be used to transmit the media data.
-> **Note:** Every `RTCRtpSender` is paired with an
-> {{domxref("RTCRtpReceiver")}} to make up an {{domxref("RTCRtpTransceiver")}}. The
-> associated receiver is muted (indicating that it is not able to deliver packets) until
-> and unless one or more streams are added to the receiver by the remote peer.
+> **Note:** Every `RTCRtpSender` is paired with an {{domxref("RTCRtpReceiver")}} to make up an {{domxref("RTCRtpTransceiver")}}.
+> The associated receiver is muted (indicating that it is not able to deliver packets) until and unless one or more streams are added to the receiver by the remote peer.
### Exceptions
- `InvalidAccessError` {{domxref("DOMException")}}
- - : Thrown if the specified track (or all of its underlying streams) is already part of the
- {{domxref("RTCPeerConnection")}}.
+ - : Thrown if the specified track (or all of its underlying streams) is already part of the {{domxref("RTCPeerConnection")}}.
- `InvalidStateError` {{domxref("DOMException")}}
- : Thrown if the {{domxref("RTCPeerConnection")}} is closed.
@@ -56,27 +51,18 @@ The {{domxref("RTCRtpSender")}} object which will be used to transmit the media
### Adding tracks to multiple streams
-After the `track` parameter, you can optionally specify one or more
-{{domxref("MediaStream")}} objects to add the track to. Only tracks are sent from one
-peer to another, not streams. Since streams are specific to each peer, specifying one or
-more streams means the other peer will create a corresponding stream (or streams)
-automatically on the other end of the connection, and will then automatically add the
-received track to those streams.
+After the `track` parameter, you can optionally specify one or more {{domxref("MediaStream")}} objects to add the track to.
+Only tracks are sent from one peer to another, not streams.
+Since streams are specific to each peer, specifying one or more streams means the other peer will create a corresponding stream (or streams) automatically on the other end of the connection, and will then automatically add the received track to those streams.
#### Streamless tracks
-If no streams are specified, then the track is **streamless**. This is
-perfectly acceptable, although it will be up to the remote peer to decide what stream to
-insert the track into, if any. This is a very common way to use `addTrack()`
-when building many types of simple applications, where only one stream is needed. For
-example, if all you're sharing with the remote peer is a single stream with an audio
-track and a video track, you don't need to deal with managing what track is in what
-stream, so you might as well just let the transceiver handle it for you.
+If no streams are specified, then the track is **streamless**.
+This is perfectly acceptable, although it will be up to the remote peer to decide what stream to insert the track into, if any.
+This is a very common way to use `addTrack()` when building many types of simple applications, where only one stream is needed.
+For example, if all you're sharing with the remote peer is a single stream with an audio track and a video track, you don't need to deal with managing what track is in what stream, so you might as well just let the transceiver handle it for you.
-Here's an example showing a function that uses {{domxref("MediaDevices.getUserMedia",
- "getUserMedia()")}} to obtain a stream from a user's camera and microphone, then adds
-each track from the stream to the peer connection, without specifying a stream for each
-track:
+Here's an example showing a function that uses {{domxref("MediaDevices.getUserMedia", "getUserMedia()")}} to obtain a stream from a user's camera and microphone, then adds each track from the stream to the peer connection, without specifying a stream for each track:
```js
async function openCall(pc) {
@@ -90,11 +76,9 @@ async function openCall(pc) {
}
```
-The result is a set of tracks being sent to the remote peer, with no stream
-associations. The handler for the {{DOMxRef("RTCPeerConnection/track_event", "track")}} event on the remote peer will be
-responsible for determining what stream to add each track to, even if that means adding
-them all to the same stream. The {{domxref("RTCPeerConnection.track_event", "ontrack")}}
-handler might look like this:
+The result is a set of tracks being sent to the remote peer, with no stream associations.
+The handler for the {{DOMxRef("RTCPeerConnection/track_event", "track")}} event on the remote peer will be responsible for determining what stream to add each track to, even if that means adding them all to the same stream.
+The {{domxref("RTCPeerConnection.track_event", "ontrack")}} handler might look like this:
```js
let inboundStream = null;
@@ -112,10 +96,9 @@ pc.ontrack = (ev) => {
};
```
-Here, the `track` event handler adds the track to the first stream specified
-by the event, if a stream is specified. Otherwise, the first time `ontrack`
-is called, a new stream is created and attached to the video element, and then the track
-is added to the new stream. From then on, new tracks are added to that stream.
+Here, the `track` event handler adds the track to the first stream specified by the event, if a stream is specified.
+Otherwise, the first time `ontrack` is called, a new stream is created and attached to the video element, and then the track is added to the new stream.
+From then on, new tracks are added to that stream.
You could also just create a new stream for each track received:
@@ -132,15 +115,10 @@ pc.ontrack = (ev) => {
#### Associating tracks with specific streams
-By specifying a stream and allowing {{domxref("RTCPeerConnection")}} to create streams
-for you, the streams' track associations are automatically managed for you by the WebRTC
-infrastructure. This includes things like changes to the transceiver's
-{{domxref("RTCRtpTransceiver.direction", "direction")}} and tracks being halted using
-{{domxref("RTCPeerConnection.removeTrack", "removeTrack()")}}.
+By specifying a stream and allowing {{domxref("RTCPeerConnection")}} to create streams for you, the streams' track associations are automatically managed for you by the WebRTC infrastructure.
+This includes things like changes to the transceiver's {{domxref("RTCRtpTransceiver.direction", "direction")}} and tracks being halted using {{domxref("RTCPeerConnection.removeTrack", "removeTrack()")}}.
-For example, consider this function that an application might use to begin streaming a
-device's camera and microphone input over an {{domxref("RTCPeerConnection")}} to a
-remote peer:
+For example, consider this function that an application might use to begin streaming a device's camera and microphone input over an {{domxref("RTCPeerConnection")}} to a remote peer:
```js
async function openCall(pc) {
@@ -160,42 +138,26 @@ The remote peer might then use a {{DOMxRef("RTCPeerConnection/track_event", "tra
pc.ontrack = ({ streams: [stream] }) => (videoElem.srcObject = stream);
```
-This sets the video element's current stream to the one that contains the track that's
-been added to the connection.
+This sets the video element's current stream to the one that contains the track that's been added to the connection.
### Reused senders
-This method may return either a new `RTCRtpSender` or, under very specific
-circumstances, an existing compatible sender which has not yet been used to transmit
-data. Compatible reusable `RTCRtpSender` instances meet these criteria:
+This method returns a new `RTCRtpSender` or an existing instance for reuse.
+An `RTCRtpSender` instance is only compatible for reuse if it meets the following criteria:
- There is no track already associated with the sender.
-- The {{domxref("RTCRtpTransceiver")}} associated with the sender has a
- {{domxref("RTCRtpReceiver")}} whose {{domxref("RTCRtpReceiver.track", "track")}}
- property specifies a {{domxref("MediaStreamTrack")}} whose
- {{domxref("MediaStreamTrack.kind", "kind")}} is the same as the `kind` of
- the `track` parameter specified when calling
- `RTCPeerConnection.addTrack()`. This ensures that a transceiver only
- handles audio or video and never both.
-- The `RTCRtpTransceiver`'s {{domxref("RTCRtpTransceiver.stopped",
- "stopped")}} property is `false`.
-- The `RTCRtpSender` being considered has never been used to send data. If
- the transceiver's {{domxref("RTCRtpTransceiver.currentDirection",
- "currentDirection")}} has ever been `"sendrecv"` or
- `"sendonly"`, the sender can't be reused.
-
-If all of those criteria are met, the sender gets reused, which results in these
-changes occurring to the existing `RTCRtpSender` and its
-`RTCRtpTransceiver`:
-
-- The `RTCRtpSender`'s {{domxref("RTCRtpSender.track", "track")}} is set to
- the specified track.
-- The sender's set of associated streams is set to the list of streams passed into
- this method, `stream...`.
-- The associated {{domxref("RTCRtpTransceiver")}} has its
- `currentDirection` updated to include sending; if its current value is
- `"recvonly"`, it becomes `"sendrecv"`, and if its current value
- is `"inactive"`, it becomes `"sendonly"`.
+- The {{domxref("RTCRtpTransceiver")}} associated with the sender has a {{domxref("RTCRtpReceiver")}} whose {{domxref("RTCRtpReceiver.track", "track")}} property specifies a {{domxref("MediaStreamTrack")}} whose {{domxref("MediaStreamTrack.kind", "kind")}} is the same as the `kind` of the `track` parameter specified when calling
+ `RTCPeerConnection.addTrack()`. This ensures that a transceiver only handles audio or video and never both.
+- The {{domxref("RTCRtpTransceiver.currentDirection")}} property is not `"stopped"`.
+- The `RTCRtpSender` being considered has never been used to send data.
+ If the transceiver's {{domxref("RTCRtpTransceiver.currentDirection", "currentDirection")}} has ever been `"sendrecv"` or `"sendonly"`, the sender can't be reused.
+
+If all of those criteria are met, the sender gets reused, which results in these changes occurring to the existing `RTCRtpSender` and its `RTCRtpTransceiver`:
+
+- The `RTCRtpSender`'s {{domxref("RTCRtpSender.track", "track")}} is set to the specified track.
+- The sender's set of associated streams is set to the list of streams passed into this method, `stream...`.
+- The associated {{domxref("RTCRtpTransceiver")}} has its `currentDirection` updated to indicate that it is sending;
+ if its current value is `"recvonly"`, it becomes `"sendrecv"`, and if its current value is `"inactive"`, it becomes `"sendonly"`.
### New senders
@@ -203,25 +165,17 @@ If no existing sender exists that can be reused, a new one is created. This also
results in the creation of the associated objects that must exist. The process of
creating a new sender results in these changes:
-- The new `RTCRtpSender` is created with the specified `track`
- and set of `stream`(s).
-- A new {{domxref("RTCRtpReceiver")}} is created with a _new_
- {{domxref("MediaStreamTrack")}} as its {{domxref("RTCRtpReceiver.track", "track")}}
- property (not the track specified as a parameter when calling
- `addTrack()`). This track's {{domxref("MediaStreamTrack.kind", "kind")}} is
- set to match the `kind` of the track provided as an input parameter.
-- A new {{domxref("RTCRtpTransceiver")}} is created and associated with the new sender
- and receiver.
-- The new transceiver's {{domxref("RTCRtpTransceiver.direction", "direction")}} is set
- to `"sendrecv"`.
-- The new transceiver is added to the `RTCPeerConnection`'s set of
- transceivers.
+- The new `RTCRtpSender` is created with the specified `track` and set of `stream`(s).
+- A new {{domxref("RTCRtpReceiver")}} is created with a _new_ {{domxref("MediaStreamTrack")}} as its {{domxref("RTCRtpReceiver.track", "track")}} property (not the track specified as a parameter when calling `addTrack()`).
+ This track's {{domxref("MediaStreamTrack.kind", "kind")}} is set to match the `kind` of the track provided as an input parameter.
+- A new {{domxref("RTCRtpTransceiver")}} is created and associated with the new sender and receiver.
+- The new transceiver's {{domxref("RTCRtpTransceiver.direction", "direction")}} is set to `"sendrecv"`.
+- The new transceiver is added to the `RTCPeerConnection`'s set of transceivers.
## Examples
-This example is drawn from the code presented in the article [Signaling and video calling](/en-US/docs/Web/API/WebRTC_API/Signaling_and_video_calling) and its corresponding sample code. It comes from the
-`handleVideoOfferMsg()` method there, which is called when an offer message
-is received from the remote peer.
+This example is drawn from the code presented in the article [Signaling and video calling](/en-US/docs/Web/API/WebRTC_API/Signaling_and_video_calling) and its corresponding sample code.
+It comes from the `handleVideoOfferMsg()` method there, which is called when an offer message is received from the remote peer.
```js
const mediaConstraints = {
@@ -240,20 +194,13 @@ pc.setRemoteDescription(desc)
});
```
-This code takes SDP which has been received from the remote peer and constructs a new
-{{domxref("RTCSessionDescription")}} to pass into
-{{domxref("RTCPeerConnection.setRemoteDescription", "setRemoteDescription()")}}. Once
-that succeeds, it uses {{domxref("MediaDevices.getUserMedia()")}} to obtain access to
-the local webcam and microphone.
+This code takes SDP which has been received from the remote peer and constructs a new {{domxref("RTCSessionDescription")}} to pass into {{domxref("RTCPeerConnection.setRemoteDescription", "setRemoteDescription()")}}.
+Once that succeeds, it uses {{domxref("MediaDevices.getUserMedia()")}} to obtain access to the local webcam and microphone.
-If that succeeds, the resulting stream is assigned as the source for a
-{{HTMLElement("video")}} element which is referenced by the variable
-`previewElement`.
+If that succeeds, the resulting stream is assigned as the source for a {{HTMLElement("video")}} element which is referenced by the variable `previewElement`.
-The final step is to begin sending the local video across the peer connection to the
-caller. This is done by adding each track in the stream by iterating over the list
-returned by {{domxref("MediaStream.getTracks()")}} and passing them to
-`addTrack()` along with the `stream` which they're a component of.
+The final step is to begin sending the local video across the peer connection to the caller.
+This is done by adding each track in the stream by iterating over the list returned by {{domxref("MediaStream.getTracks()")}} and passing them to `addTrack()` along with the `stream` which they're a component of.
## Specifications
diff --git a/files/en-us/web/api/rtcpeerconnection/getreceivers/index.md b/files/en-us/web/api/rtcpeerconnection/getreceivers/index.md
index 84030556d42f2ef..4ddf05589887134 100644
--- a/files/en-us/web/api/rtcpeerconnection/getreceivers/index.md
+++ b/files/en-us/web/api/rtcpeerconnection/getreceivers/index.md
@@ -8,10 +8,8 @@ browser-compat: api.RTCPeerConnection.getReceivers
{{APIRef("WebRTC")}}
-The **`RTCPeerConnection.getReceivers()`** method returns an
-array of {{domxref("RTCRtpReceiver")}} objects, each of which represents one RTP
-receiver. Each RTP receiver manages the reception and decoding of data for
-a {{domxref("MediaStreamTrack")}} on an {{domxref("RTCPeerConnection")}}
+The **`RTCPeerConnection.getReceivers()`** method returns an array of {{domxref("RTCRtpReceiver")}} objects, each of which represents one RTP receiver.
+Each RTP receiver manages the reception and decoding of data for a {{domxref("MediaStreamTrack")}} on an {{domxref("RTCPeerConnection")}}.
## Syntax
@@ -21,12 +19,12 @@ getReceivers()
### Return value
-An array of {{domxref("RTCRtpReceiver")}} objects, one for each track on the
-connection. The array is empty if there are no RTP receivers on the connection.
+An array of {{domxref("RTCRtpReceiver")}} objects, one for each track on the connection.
+The array is empty if there are no RTP receivers on the connection.
-The order of the returned `RTCRtpReceiver` instances is not defined by the
-specification, and may change from one call to `getReceivers()` to the
-next.
+The order of the returned `RTCRtpReceiver` instances is not defined by the specification, and may change from one call to `getReceivers()` to the next.
+
+The array does not include receivers associated with transceivers that have been [stopped](/en-US/docs/Web/API/RTCRtpTransceiver/currentDirection) (following offer/answer).
## Example
diff --git a/files/en-us/web/api/rtcpeerconnection/getsenders/index.md b/files/en-us/web/api/rtcpeerconnection/getsenders/index.md
index 39a4e641932d2fa..61cc9735ba8a498 100644
--- a/files/en-us/web/api/rtcpeerconnection/getsenders/index.md
+++ b/files/en-us/web/api/rtcpeerconnection/getsenders/index.md
@@ -8,12 +8,8 @@ browser-compat: api.RTCPeerConnection.getSenders
{{APIRef("WebRTC")}}
-The {{domxref("RTCPeerConnection")}} method
-**`getSenders()`** returns an array of
-{{domxref("RTCRtpSender")}} objects, each of which represents the RTP sender
-responsible for transmitting one track's data. A sender object provides methods
-and properties for examining and controlling the encoding and transmission of the
-track's data.
+The {{domxref("RTCPeerConnection")}} method **`getSenders()`** returns an array of {{domxref("RTCRtpSender")}} objects, each of which represents the RTP sender responsible for transmitting one track's data.
+A sender object provides methods and properties for examining and controlling the encoding and transmission of the track's data.
## Syntax
@@ -26,17 +22,13 @@ getSenders()
An array of {{domxref("RTCRtpSender")}} objects, one for each track on the connection.
The array is empty if there are no RTP senders on the connection.
-The order of the returned `RTCRtpSender`s is not defined by the
-specification, and may change from one call to `getSenders()` to the next.
+The order of the returned `RTCRtpSender` instances is not defined by the specification, and may change from one call to `getSenders()` to the next.
+
+The array does not include senders associated with transceivers that have been [stopped](/en-US/docs/Web/API/RTCRtpTransceiver/currentDirection) (following offer/answer).
## Example
-In this example, a `setMuting()` function is shown. This function takes as
-input an {{domxref("RTCPeerConnection")}}, `pc`, and a Boolean,
-`muting`. The function gets the list of the peer connection's senders and
-iterates over every sender, setting the corresponding media track's
-{{domxref("MediaStreamTrack.enabled", "enabled")}} to the inverse of the specified
-`muting`.
+In this example, a `setMuting()` function is shown. This function takes as input an {{domxref("RTCPeerConnection")}}, `pc`, and a Boolean, `muting`. The function gets the list of the peer connection's senders and iterates over every sender, setting the corresponding media track's {{domxref("MediaStreamTrack.enabled", "enabled")}} to the inverse of the specified `muting`.
```js
function setMuting(pc, muting) {
diff --git a/files/en-us/web/api/rtcpeerconnection/gettransceivers/index.md b/files/en-us/web/api/rtcpeerconnection/gettransceivers/index.md
index 924b23f510d19c2..2d00a2d17721055 100644
--- a/files/en-us/web/api/rtcpeerconnection/gettransceivers/index.md
+++ b/files/en-us/web/api/rtcpeerconnection/gettransceivers/index.md
@@ -8,10 +8,7 @@ browser-compat: api.RTCPeerConnection.getTransceivers
{{APIRef("WebRTC")}}
-The {{domxref("RTCPeerConnection")}} interface's
-**`getTransceivers()`** method returns a list of the
-{{domxref("RTCRtpTransceiver")}} objects being used to send and receive data on the
-connection.
+The {{domxref("RTCPeerConnection")}} interface's **`getTransceivers()`** method returns a list of the {{domxref("RTCRtpTransceiver")}} objects being used to send and receive data on the connection.
## Syntax
@@ -25,14 +22,13 @@ None.
### Return value
-An array of the {{domxref("RTCRtpTransceiver")}} objects representing the transceivers
-handling sending and receiving all media on the `RTCPeerConnection`. The list
-is in the order in which the transceivers were added to the connection.
+An array of the {{domxref("RTCRtpTransceiver")}} objects representing the transceivers handling sending and receiving all media on the `RTCPeerConnection`.
+The array is in the order in which the transceivers were added to the connection.
+The array does not include transceivers that have already been [stopped](/en-US/docs/Web/API/RTCRtpTransceiver/currentDirection) (following offer/answer).
## Examples
-The following snippet of code stops all transceivers associated with an
-`RTCPeerConnection`.
+The following snippet of code stops all transceivers associated with an `RTCPeerConnection`.
```js
pc.getTransceivers().forEach((transceiver) => {
diff --git a/files/en-us/web/api/rtcrtptransceiver/currentdirection/index.md b/files/en-us/web/api/rtcrtptransceiver/currentdirection/index.md
index c7a5e752b2c9169..4fc47a3e14a2d8c 100644
--- a/files/en-us/web/api/rtcrtptransceiver/currentdirection/index.md
+++ b/files/en-us/web/api/rtcrtptransceiver/currentdirection/index.md
@@ -8,61 +8,40 @@ browser-compat: api.RTCRtpTransceiver.currentDirection
{{APIRef("WebRTC")}}
-The read-only {{domxref("RTCRtpTransceiver")}} property
-**`currentDirection`** is a string which indicates the current
-directionality of the transceiver.
+The read-only {{domxref("RTCRtpTransceiver")}} property **`currentDirection`** is a string which indicates the current negotiated directionality of the transceiver.
-Its value is one of the strings defined in the table below.
+The directionality indicates whether the transceiver will offer to send and/or receive {{Glossary("RTP")}} data, or whether it is inactive or stopped and won't send or receive data.
-You can examine and set the transceiver's preferred directionality using
-{{domxref("RTCRtpTransceiver.direction", "direction")}} property.
+The transceiver's preferred directionality can be set and read using the {{domxref("RTCRtpTransceiver.direction", "direction")}} property.
+Changing the `direction` triggers a renegotiation, which may eventually result in the `currentDirection` also changing.
## Value
-A string whose value is one of the strings which are a member of the following values.
-
-
-
-
- Value
- RTCRtpSender
behavior
- RTCRtpReceiver
behavior
-
-
-
-
- "sendrecv"
-
- Offers to send {{Glossary("RTP")}} data, and will do so if the
- other peer accepts the connection and at least one of the sender's
- encodings is active.
-
-
- Offers to receive RTP data, and does so if the other peer accepts.
-
-
-
- "sendonly"
-
- Offers to send RTP data, and will do so if the other peer accepts the
- connection and at least one of the sender's encodings is active.
-
- Does not offer to receive RTP data and will not do so.
-
-
- "recvonly"
- Does not offer to send RTP data, and will not do so.
-
- Offers to receive RTP data, and will do so if the other peer offers.
-
-
-
- "inactive"
- Does not offer to send RTP data, and will not do so.
- Does not offer to receive RTP data and will not do so.
-
-
-
+The value is initially `null`, prior to negotiation using an offer/answer.
+
+After negotiation the value is a string with one of the following values:
+
+- `"sendrecv"`
+ - : Transceiver offers to send and receive RTP data:
+ - `RTCRtpSender`: Offers to send RTP data, and will do so if the remote peer accepts the connection and at least one of the sender's encodings is active.
+ - `RTCRtpReceiver`: Offers to receive RTP data, and does so if the remote peer accepts.
+- `"sendonly"`
+ - : Transceiver offers to send but not receive RTP data:
+ - `RTCRtpSender`: Offers to send RTP data, and will do so if the remote peer accepts the connection and at least one of the sender's encodings is active.
+ - `RTCRtpReceiver`: Does _not_ offer to receive RTP data and will not do so.
+- `"recvonly"`
+ - : Transceiver offers to receive but not set RTP data:
+ - `RTCRtpSender`: Does _not_ offer to send RTP data, and will not do so.
+ - `RTCRtpReceiver`: Offers to receive RTP data, and will do so if the remote peer offers.
+- `"inactive"`
+ - : Transceiver is inactive:
+ - `RTCRtpSender`: Does _not_ offer to send RTP data, and will not do so.
+ - `RTCRtpReceiver`: Does _not_ offer to receive RTP data and will not do so.
+- `"stopped"`
+ - : This is the terminal state of the transceiver.
+ The transceiver is stopped and will not send or receive RTP data or offer to do so.
+ - `RTCRtpSender`: Does _not_ offer to send RTP data, and will not do so.
+ - `RTCRtpReceiver`: Does _not_ offer to receive RTP data and will not do so.
## Specifications
diff --git a/files/en-us/web/api/rtcrtptransceiver/direction/index.md b/files/en-us/web/api/rtcrtptransceiver/direction/index.md
index 117d5836da87f02..0eea2d8de8242c5 100644
--- a/files/en-us/web/api/rtcrtptransceiver/direction/index.md
+++ b/files/en-us/web/api/rtcrtptransceiver/direction/index.md
@@ -8,94 +8,65 @@ browser-compat: api.RTCRtpTransceiver.direction
{{APIRef("WebRTC")}}
-The {{domxref("RTCRtpTransceiver")}} property
-**`direction`** is a string which indicates the transceiver's
-preferred directionality.
+The {{domxref("RTCRtpTransceiver")}} property **`direction`** is a string that indicates the transceiver's _preferred_ directionality.
-Its value must be one of the strings defined in the table below.
-
-The transceiver's _current_ direction is indicated by the
-{{domxref("RTCRtpTransceiver.currentDirection", "currentDirection")}} property.
+The directionality indicates whether the transceiver will offer to send and/or receive {{Glossary("RTP")}} data, or whether it is inactive or stopped (terminated).
+When setting the transceiver's direction, the value is not applied immediately.
+The _current_ direction is indicated by the {{domxref("RTCRtpTransceiver.currentDirection", "currentDirection")}} property.
## Value
-A string whose value is one of the strings which are a member of the following values, indicating the transceiver's preferred direction.
-
-
-
-
- Value
- RTCRtpSender
behavior
- RTCRtpReceiver
behavior
-
-
-
-
- "sendrecv"
-
- Offers to send {{Glossary("RTP")}} data, and will do so if the
- other peer accepts the connection and at least one of the sender's
- encodings is active.
-
-
- Offers to receive RTP data, and does so if the other peer accepts.
-
-
-
- "sendonly"
-
- Offers to send RTP data, and will do so if the other peer accepts the
- connection and at least one of the sender's encodings is active.
-
- Does not offer to receive RTP data and will not do so.
-
-
- "recvonly"
- Does not offer to send RTP data, and will not do so.
-
- Offers to receive RTP data, and will do so if the other peer offers.
-
-
-
- "inactive"
- Does not offer to send RTP data, and will not do so.
- Does not offer to receive RTP data and will not do so.
-
-
-
+A string with one of the following values:
+
+- `"sendrecv"`
+ - : Transceiver offers to send and receive RTP data:
+ - `RTCRtpSender`: Offers to send RTP data, and will do so if the remote peer accepts the connection and at least one of the sender's encodings is active.
+ - `RTCRtpReceiver`: Offers to receive RTP data, and does so if the remote peer accepts.
+- `"sendonly"`
+ - : Transceiver offers to send but not receive RTP data:
+ - `RTCRtpSender`: Offers to send RTP data, and will do so if the remote peer accepts the connection and at least one of the sender's encodings is active.
+ - `RTCRtpReceiver`: Does _not_ offer to receive RTP data and will not do so.
+- `"recvonly"`
+ - : Transceiver offers to receive but not set RTP data:
+ - `RTCRtpSender`: Does _not_ offer to send RTP data, and will not do so.
+ - `RTCRtpReceiver`: Offers to receive RTP data, and will do so if the remote peer offers.
+- `"inactive"`
+ - : Transceiver is inactive:
+ - `RTCRtpSender`: Does _not_ offer to send RTP data, and will not do so.
+ - `RTCRtpReceiver`: Does _not_ offer to receive RTP data and will not do so.
+- `"stopped"`
+ - : This is the terminal state of the transceiver.
+ The transceiver is stopped and will not send or receive RTP data or offer to do so.
+ Setting this value when the transceiver is not already stopped raises a `TypeError`.
+ - `RTCRtpSender`: Does _not_ offer to send RTP data, and will not do so.
+ - `RTCRtpReceiver`: Does _not_ offer to receive RTP data and will not do so.
### Exceptions
When setting the value of `direction`, the following exception can occur:
- `InvalidStateError` {{domxref("DOMException")}}
- - : Thrown if either the receiver's {{domxref("RTCPeerConnection")}} is closed or the
- {{domxref("RTCRtpReceiver")}} is stopped.
-
-## Usage notes
-
-### Setting the direction
+ - : The receiver's {{domxref("RTCPeerConnection")}} is closed or the {{domxref("RTCRtpReceiver")}} is stopped.
+- `TypeError`
+ - : The value is being set to `stopped` when the current value is anything other than `stopped`.
-When you change the value of `direction`, an `InvalidStateError`
-exception will occur if the connection is closed or the receiver is stopped.
+## Description
-If the new value of `direction` is in fact different from the existing
-value, renegotiation of the connection is required, so a {{domxref("RTCPeerConnection.negotiationneeded_event", "negotiationneeded")}}
-event is sent to the {{domxref("RTCPeerConnection")}}.
+The **`direction`** property can be used to set or get the transceiver's _preferred_ directionality.
-### Effect on offers and answers
-
-The value of `direction` is used by
-{{domxref("RTCPeerConnection.createOffer()")}} or
-{{domxref("RTCPeerConnection.createAnswer()")}} in order to generate the SDP generated
-by each of those methods. The SDP contains an a-line which specifies the directionality.
-For example, if the `direction` is specified as `"sendrecv"`, the
-corresponding SDP a-line is:
+Updating the directionality does not take effect immediately.
+If the new value of `direction` is different from the existing value, renegotiation of the connection is required, so a {{domxref("RTCPeerConnection.negotiationneeded_event", "negotiationneeded")}} event is sent to the {{domxref("RTCPeerConnection")}}.
+A `direction` value (other than `stopped`) is then used by {{domxref("RTCPeerConnection.createOffer()")}} or {{domxref("RTCPeerConnection.createAnswer()")}} in order to generate the {{glossary("SDP")}} message created those methods.
+For example, if the `direction` is specified as `"sendrecv"`, the corresponding SDP a-line indicates the directionality:
```plain
a=sendrecv
```
+The new directionality takes effect once the negotiation process is completed and the new session description is successfully applied.
+
+The transceiver's _current_ direction is indicated by the {{domxref("RTCRtpTransceiver.currentDirection", "currentDirection")}} property.
+
## Specifications
{{Specifications}}
diff --git a/files/en-us/web/api/rtcrtptransceiver/index.md b/files/en-us/web/api/rtcrtptransceiver/index.md
index 9d966c842d2163a..592a5278524ac86 100644
--- a/files/en-us/web/api/rtcrtptransceiver/index.md
+++ b/files/en-us/web/api/rtcrtptransceiver/index.md
@@ -9,14 +9,17 @@ browser-compat: api.RTCRtpTransceiver
The WebRTC interface **`RTCRtpTransceiver`** describes a permanent pairing of an {{domxref("RTCRtpSender")}} and an {{domxref("RTCRtpReceiver")}}, along with some shared state.
-Each {{Glossary("SDP")}} media section describes one bidirectional SRTP ("Secure Real Time Protocol") stream (excepting the media section for {{domxref("RTCDataChannel")}}, if present). This pairing of send and receive SRTP streams is significant for some applications, so `RTCRtpTransceiver` is used to represent this pairing, along with other important state from the media section. Each non-disabled SRTP media section is always represented by exactly one transceiver.
+Each {{Glossary("SDP")}} media section describes one bidirectional SRTP ("Secure Real Time Protocol") stream (excepting the media section for {{domxref("RTCDataChannel")}}, if present).
+This pairing of send and receive SRTP streams is significant for some applications, so `RTCRtpTransceiver` is used to represent this pairing, along with other important state from the media section.
+Each non-disabled SRTP media section is always represented by exactly one transceiver.
A transceiver is uniquely identified using its {{domxref("RTCRtpTransceiver.mid", "mid")}} property, which is the same as the media ID (`mid`) of its corresponding m-line. An `RTCRtpTransceiver` is **associated** with an m-line if its `mid` is non-null; otherwise it's considered disassociated.
## Instance properties
- {{domxref("RTCRtpTransceiver.currentDirection", "currentDirection")}} {{ReadOnlyInline}}
- - : A read-only string which indicates the transceiver's current directionality, or `null` if the transceiver is stopped or has never participated in an exchange of offers and answers. To change the transceiver's directionality, set the value of the {{domxref("RTCRtpTransceiver.direction", "direction")}} property.
+ - : A read-only string which indicates the transceiver's current negotiated directionality, or `null` if the transceiver has never participated in an exchange of offers and answers.
+ To change the transceiver's directionality, set the value of the {{domxref("RTCRtpTransceiver.direction", "direction")}} property.
- {{domxref("RTCRtpTransceiver.direction", "direction")}}
- : A string which is used to set the transceiver's desired direction.
- {{domxref("RTCRtpTransceiver.mid", "mid")}} {{ReadOnlyInline}}
@@ -33,7 +36,8 @@ A transceiver is uniquely identified using its {{domxref("RTCRtpTransceiver.mid"
- {{domxref("RTCRtpTransceiver.setCodecPreferences", "setCodecPreferences()")}}
- : A list of {{domxref("RTCRtpCodecParameters")}} objects which override the default preferences used by the {{Glossary("user agent")}} for the transceiver's codecs.
- {{domxref("RTCRtpTransceiver.stop", "stop()")}}
- - : Permanently stops the `RTCRtpTransceiver`. The associated sender stops sending data, and the associated receiver likewise stops receiving and decoding incoming data.
+ - : Permanently stops the `RTCRtpTransceiver`.
+ The associated sender stops sending data, and the associated receiver likewise stops receiving and decoding incoming data.
## Specifications
diff --git a/files/en-us/web/api/rtcrtptransceiver/stop/index.md b/files/en-us/web/api/rtcrtptransceiver/stop/index.md
index ef876853134d01b..4fd168380281d88 100644
--- a/files/en-us/web/api/rtcrtptransceiver/stop/index.md
+++ b/files/en-us/web/api/rtcrtptransceiver/stop/index.md
@@ -8,17 +8,9 @@ browser-compat: api.RTCRtpTransceiver.stop
{{APIRef("WebRTC")}}
-The **`stop()`** method in the
-{{domxref("RTCRtpTransceiver")}} interface permanently stops the transceiver by
-stopping both the associated {{domxref("RTCRtpSender")}} and
+The **`stop()`** method in the {{domxref("RTCRtpTransceiver")}} interface permanently stops the transceiver by stopping both the associated {{domxref("RTCRtpSender")}} and
{{domxref("RTCRtpReceiver")}}.
-> **Note:** Until recently, the {{domxref("RTCRtpTransceiver.stopped", "stopped")}} property was provided to return `true` if the connection is
-> stopped. That property has been deprecated and will be removed at some point. Instead,
-> check the value of {{domxref("RTCRtpTransceiver.currentDirection", "currentDirection")}}. If it's `stopped`, the transceiver has been stopped.
-
-This method does nothing if the transceiver is already stopped.
-
## Syntax
```js-nolint
@@ -38,23 +30,16 @@ None ({{jsxref("undefined")}}).
- `InvalidStateError` {{domxref("DOMException")}}
- : Thrown if the `RTCPeerConnection`, of which the transceiver is a member, is closed.
-## Usage notes
+## Description
-When you call `stop()` on a transceiver, the sender immediately stops
-sending media and each of its RTP streams are closed using the {{Glossary("RTCP")}}
-`"BYE"` message. The receiver then stops receiving media; the receiver's
-{{domxref("RTCRtpReceiver.track", "track")}} is stopped, and the transceiver's
-{{domxref("RTCRtpTransceiver.direction", "direction")}} is changed to
-`stopped`, and renegotiation is triggered by sending a
-`negotiationneeded` event to the `RTCPeerConnection`.
+When you call `stop()` on a transceiver, the sender immediately stops sending media and each of its RTP streams are closed using the {{Glossary("RTCP")}} `"BYE"` message.
+The receiver then stops receiving media; the receiver's {{domxref("RTCRtpReceiver.track", "track")}} is stopped, and the transceiver's {{domxref("RTCRtpTransceiver.direction", "direction")}} is changed to `stopped`.
+Renegotiation is triggered by sending a {{domxref("RTCPeerConnection.negotiationneeded_event", "negotiationneeded")}} event to the transceiver's {{domxref("RTCPeerConnection")}}, so that the connection can adapt to the change.
-The negotiation process causes {{domxref("RTCRtpTransceiver.currentDirection",
- "currentNegotiation")}} to be set to `stopped`, finally indicating that the
-transceiver has been fully stopped.
+The method does nothing if the transceiver is already stopped.
+You can check whether it has stopped by comparing {{domxref("RTCRtpTransceiver.currentDirection", "currentDirection")}} to `"stopped"`.
-> **Note:** Stopping the transceiver causes a
-> {{domxref("RTCPeerConnection.negotiationneeded_event", "negotiationneeded")}} event to be sent to the transceiver's
-> {{domxref("RTCPeerConnection")}}, so the connection can adapt to the change.
+> **Note:** Earlier versions of the specification used the deprecated {{domxref("RTCRtpTransceiver.stopped", "stopped")}} {{deprecated_inline}} property to indicate if the transceiver has stopped.
## Specifications
diff --git a/files/en-us/web/api/rtcrtptransceiver/stopped/index.md b/files/en-us/web/api/rtcrtptransceiver/stopped/index.md
index 2e5a0d8feba0631..fa31b6d5606e4c8 100644
--- a/files/en-us/web/api/rtcrtptransceiver/stopped/index.md
+++ b/files/en-us/web/api/rtcrtptransceiver/stopped/index.md
@@ -10,17 +10,11 @@ browser-compat: api.RTCRtpTransceiver.stopped
{{APIRef("WebRTC")}}{{deprecated_header}}
-The read-only **`stopped`** property
-on the {{domxref("RTCRtpTransceiver")}} interface indicates whether or not the
-transceiver's associated sender and receiver have both been stopped.
+> **Note:** Instead of using this deprecated property, compare {{domxref("RTCRtpTransceiver.currentDirection", "currentDirection")}} to `"stopped"`.
-The transceiver is stopped if the {{domxref("RTCRtpTransceiver.stop", "stop()")}}
-method has been called or if a change to either the local or the remote description has
-caused the transceiver to be stopped for some reason.
+The read-only **`stopped`** property on the {{domxref("RTCRtpTransceiver")}} interface indicates whether or not the transceiver's associated sender and receiver have both been stopped.
-> **Note:** This property is _deprecated_ and will be removed in the future. Instead, look
-> at the value of {{domxref("RTCRtpTransceiver.currentDirection", "currentDirection")}}.
-> Its value is `stopped` if the transceiver has stopped.
+The transceiver is stopped if the {{domxref("RTCRtpTransceiver.stop", "stop()")}} method has been called or if a change to either the local or the remote description has caused the transceiver to be stopped for some reason.
## Value
diff --git a/files/en-us/web/api/serviceworkercontainer/controllerchange_event/index.md b/files/en-us/web/api/serviceworkercontainer/controllerchange_event/index.md
index ce1b77af3b0befa..5ad16a7480a5ea0 100644
--- a/files/en-us/web/api/serviceworkercontainer/controllerchange_event/index.md
+++ b/files/en-us/web/api/serviceworkercontainer/controllerchange_event/index.md
@@ -8,10 +8,7 @@ browser-compat: api.ServiceWorkerContainer.controllerchange_event
{{APIRef("Service Workers API")}}
-The **`controllerchange`** event of the
-{{domxref("ServiceWorkerContainer")}} interface fires when the document's associated
-{{domxref("ServiceWorkerRegistration")}} acquires a new
-{{domxref("ServiceWorkerRegistration.active","active")}} worker.
+The **`controllerchange`** event of the {{domxref("ServiceWorkerContainer")}} interface fires when the document's associated {{domxref("ServiceWorkerRegistration")}} acquires a new {{domxref("ServiceWorkerRegistration.active","active")}} worker.
## Syntax
@@ -30,7 +27,9 @@ A generic {{domxref("Event")}}.
## Example
```js
-// TBD
+navigator.serviceWorker.addEventListener("controllerchange", () => {
+ console.log("The controller of current browsing context has changed.");
+});
```
## Specifications
diff --git a/files/en-us/web/api/serviceworkerregistration/getnotifications/index.md b/files/en-us/web/api/serviceworkerregistration/getnotifications/index.md
index c6010b2341ebf4d..76fef6a9b1e8435 100644
--- a/files/en-us/web/api/serviceworkerregistration/getnotifications/index.md
+++ b/files/en-us/web/api/serviceworkerregistration/getnotifications/index.md
@@ -13,7 +13,7 @@ the {{domxref("ServiceWorkerRegistration")}} interface returns a list of the
notifications in the order that they were created from the current origin via the
current service worker registration. Origins can have many active but
differently-scoped service worker registrations. Notifications created by one service
-worker on the same origin will not be available to other active services workers on
+worker on the same origin will not be available to other active service workers on
that same origin.
## Syntax
diff --git a/files/en-us/web/api/shadowroot/adoptedstylesheets/index.md b/files/en-us/web/api/shadowroot/adoptedstylesheets/index.md
index 3993942e544f2e0..496f280bd70bada 100644
--- a/files/en-us/web/api/shadowroot/adoptedstylesheets/index.md
+++ b/files/en-us/web/api/shadowroot/adoptedstylesheets/index.md
@@ -24,8 +24,8 @@ Only stylesheets created using the [`CSSStyleSheet()` constructor](/en-US/docs/W
The value is an array of {{domxref("CSSStyleSheet()")}} instances that must have been created using the {{domxref("CSSStyleSheet.CSSStyleSheet()", "CSSStyleSheet()")}} constructor within the context of the shadow root's parent {{domxref("Document")}}.
-If the array needs to be modified, then a new array must be assigned (in-place mutations like `push()` will throw an exception).
-Note, however, that the {{domxref("CSSStyleSheet()")}} instances themselves can be modified, and these changes will apply wherever the stylesheet is adopted.
+If the array needs to be modified, use in-place mutations like `push()`.
+Note, the {{domxref("CSSStyleSheet()")}} instances themselves can also be modified, and these changes will apply wherever the stylesheet is adopted.
## Examples
@@ -47,7 +47,7 @@ We then create a {{domxref("ShadowRoot")}} and pass the sheet object to `adopted
const node = document.createElement("div");
const shadow = node.attachShadow({ mode: "open" });
-//Adopt the sheet into the shadow DOM
+// Adopt the sheet into the shadow DOM
shadow.adoptedStyleSheets = [sheet];
```
@@ -61,15 +61,14 @@ sheet.insertRule("* { background-color: blue; }");
### Append a new stylesheet
-To _append_ a stylesheet to the `adoptedStyleSheets` property we have to create and assign a new array that contains both the original stylesheets in the property and the new style sheet.
-This is demonstrated for our shadow root object below using spread-syntax:
+New stylesheets can be _appended_ to the document or shadow root by using `adoptedStyleSheets.push()`:
```js
const extraSheet = new CSSStyleSheet();
extraSheet.replaceSync("p { color: green; }");
-// Combine the existing sheets and new one
-shadow.adoptedStyleSheets = [...document.adoptedStyleSheets, extraSheet];
+// Concat the new sheet.
+shadow.adoptedStyleSheets.push(extraSheet);
```
## Specifications
diff --git a/files/en-us/web/api/speculation_rules_api/index.md b/files/en-us/web/api/speculation_rules_api/index.md
new file mode 100644
index 000000000000000..0203449585fd210
--- /dev/null
+++ b/files/en-us/web/api/speculation_rules_api/index.md
@@ -0,0 +1,411 @@
+---
+title: Speculation Rules API
+slug: Web/API/Speculation_Rules_API
+page-type: web-api-overview
+status:
+ - experimental
+browser-compat: html.elements.script.type.speculationrules
+---
+
+{{SeeCompatTable}}{{DefaultAPISidebar("Speculation Rules API")}}
+
+The **Speculation Rules API** is designed to improve performance for future navigations. It targets document URLs rather than specific resource files, and so makes sense for multi-page applications (MPAs) rather than single-page applications (SPAs).
+
+The Speculation Rules API provides an alternative to the widely-available [`
`](/en-US/docs/Web/HTML/Attributes/rel/prefetch) feature and is designed to supersede the Chrome-only deprecated [`
`](/en-US/docs/Web/HTML/Attributes/rel/prerender) feature. It provides many improvements over these technologies, along with a more expressive, configurable syntax for specifying which documents should be prefetched or prerendered.
+
+> **Note:** The Speculation Rules API doesn't handle subresource prefetches; for that you'll need to use `
`.
+
+## Concepts and usage
+
+Speculation rules are specified inside [``](/en-US/docs/Web/HTML/Element/script/type/speculationrules). The rules are specified as a JSON structure.
+
+For example:
+
+```html
+
+```
+
+You specify a different array to contain the rules for each speculative loading type (for example `"prerender"` or `"prefetch"`). Each rule is contained in an object that specifies for example a list of resources to be fetched, plus options such as an explicit [`Referrer-Policy`](/en-US/docs/Web/HTTP/Headers/Referrer-Policy) setting for each rule. Note that prerendered URLs are also prefetched.
+
+See [`` will cause supporting browsers to download the response body of the referenced pages, but none of the subresources referenced by the page. When a prefetched page is navigated to, it will render much more quickly than if it were not prefetched.
+
+The results are kept in a per-document in-memory cache. Any cached prefetches are discarded when you navigate away from the current page, except of course a prefetched document that you then navigate to.
+
+This means that if you prefetch something the user doesn't navigate to, it is generally a waste of resources, although the result may populate the HTTP cache if headers allow. That said, the upfront cost of a prefetch is much smaller than the upfront cost of a prerender, so you are encouraged to adopt prefetching broadly, for example prefetching all of the significant pages on your site, provided they are safe to prefetch (see [Unsafe speculative loading conditions](#unsafe_speculative_loading_conditions) for more details).
+
+Same-site and cross-site prefetches will work, but cross-site prefetches are limited (see ["same-site" and "cross-site"](https://web.dev/same-site-same-origin/#same-site-cross-site) for an explanation of the difference between the two). For privacy reasons cross-site prefetches will currently only work if the user has no cookies set for the destination site — we don't want sites to be able to track user activity via prefetched pages (which they may never even actually visit) based on previously-set cookies.
+
+> **Note:** In the future an opt-in will be provided via the [`Supports-Loading-Mode`](/en-US/docs/Web/HTTP/Headers/Supports-Loading-Mode) header, but this was not implemented at the time of writing.
+
+For browsers that support it, speculation rules prefetch should be preferred over older prefetch mechanisms, namely [`
`](/en-US/docs/Web/HTML/Attributes/rel/prefetch) and {{domxref("fetch()")}} with a `priority: "low"` option set on it. Because we know that speculation rules prefetch is for navigations, not general resource prefetching:
+
+- It can be used for cross-site navigations, whereas `
` cannot.
+- It doesn't get blocked by [Cache-Control](/en-US/docs/Web/HTTP/Headers/Cache-Control) headers, whereas `
` often does.
+
+In addition, speculation rules prefetch:
+
+- Automatically lowers the priority when needed (`fetch()` doesn't).
+- Is respectful of the user's configuration. For example, prefetching doesn't happen when the user's device is in Battery Saver or Data Saver mode.
+- Stores the prefetched resources in a per-document in-memory cache as opposed to the HTTP cache, which may result in slightly faster prefetching.
+
+### Using prerendering
+
+Including `prerender` rules inside `` will cause supporting browsers to fetch, render, and load the content into an invisible tab, stored in a per-document in-memory cache. This includes loading all subresources, running all JavaScript, and even loading subresources and performing data fetches started by JavaScript. Any cached prerenders and their subresources are discarded when you navigate away from the current page, except of course a prerendered document that you then navigate to.
+
+Future navigations to a prerendered page will be near-instant. The browser activates the invisible tab instead of carrying out the usual navigation process, replacing the old foreground page with the prerendered page. If a page is activated before it has fully prerendered, it is activated in its current state and then continues to load, which means you will still see a significant performance improvement.
+
+Prerendering uses memory and network bandwidth. If you prerender something the user doesn't navigate to, these are wasted (although the result may populate the HTTP cache if headers allow, allowing later use). The upfront cost of a prerender is much larger than the upfront cost of a prefetch, and there are more conditions that could make content unsafe to prerender (see [Unsafe speculative loading conditions](#unsafe_speculative_loading_conditions) for more details). As a result, you are encouraged to adopt prerendering more sparingly, carefully considering cases where there is a high likelihood of the page being navigated to, and you think the user experience benefit is worth the extra cost.
+
+> **Note:** To put the amount of potential resource wastage in perspective, a prerender uses about the same amount of resources as rendering an {{htmlelement("iframe")}}.
+
+> **Note:** Many APIs will be automatically deferred when prerendering/until activation. See [Platform features deferred or restricted during prerender](#platform_features_deferred_or_restricted_during_prerender) for more details.
+
+Prerendering is restricted to same-origin documents by default. Cross-origin, same-site prerendering is possible — it requires the navigation target to opt-in using the [`Supports-Loading-Mode`](/en-US/docs/Web/HTTP/Headers/Supports-Loading-Mode) header with a value of `credentialed-prerender`. Cross-site prerendering is not possible at this time.
+
+For browsers that support it, speculation rules prerender should be preferred over older prerender mechanisms, namely [`
`](/en-US/docs/Web/HTML/Attributes/rel/prerender):
+
+- `
` is Chrome-specific and was never standardized, and the Chrome engineering team are in the process of sunsetting it.
+- It loads subresources loaded via JavaScript, whereas `
` doesn't.
+- It doesn't get blocked by [Cache-Control](/en-US/docs/Web/HTTP/Headers/Cache-Control) settings, whereas `
` often does.
+- Speculation rules prerender should be treated as a hint and a progressive enhancement. Unlike `
`, it is a speculative hint and the browser may choose not to act upon the hint based on user settings, current memory usage, or other heuristics.
+
+### Speculation rules API feature detection
+
+You can check if the Speculation Rules API is supported using the following code:
+
+```js
+if (
+ HTMLScriptElement.supports &&
+ HTMLScriptElement.supports("speculationrules")
+) {
+ console.log("Your browser supports the Speculation Rules API.");
+}
+```
+
+For example, you might want to insert speculation rules for prefetching in supporting browsers, but use an older technology such as `
` in others:
+
+```js
+if (
+ HTMLScriptElement.supports &&
+ HTMLScriptElement.supports("speculationrules")
+) {
+ const specScript = document.createElement("script");
+ specScript.type = "speculationrules";
+ const specRules = {
+ prefetch: [
+ {
+ source: "list",
+ urls: ["/next.html"],
+ },
+ ],
+ };
+ specScript.textContent = JSON.stringify(specRules);
+ document.body.append(specScript);
+} else {
+ const linkElem = document.createElement("link");
+ linkElem.rel = "prefetch";
+ linkElem.href = "/next.html";
+ document.head.append(linkElem);
+}
+```
+
+## Detecting prefetched and prerendered pages
+
+This section looks at different ways to detect whether a requested page has been prefetched or prerendered.
+
+### Server-side detection
+
+Prefetched and prerendered page requests are sent with the [Sec-Purpose](/en-US/docs/Web/HTTP/Headers/Sec-Purpose) request header:
+
+For prefetch:
+
+```http
+Sec-Purpose: prefetch
+```
+
+For prerender:
+
+```http
+Sec-Purpose: prefetch;prerender
+```
+
+Servers can respond based on this header, for example to log speculative load requests, return different content, or even prevent the speculative loading from happening. If a non-success response code is returned (not 200 or 304), then the page will not be prefetched/prerendered. This is the easiest way to prevent speculative loading, although it is usually a better approach to allow the prefetch/prerender, but delay any actions that should only happen then the page is actually viewed, using JavaScript.
+
+### JavaScript prefetch detection
+
+When a page is prefetched, its {{domxref("PerformanceResourceTiming.deliveryType")}} entry will return a value of `"navigational-prefetch"`. You could use the following to run a function when a performance entry of type `"navigational-prefetch"` is received:
+
+```js
+if (
+ performance.getEntriesByType("navigation")[0].deliveryType ===
+ "navigational-prefetch"
+) {
+ respondToPrefetch(); // Author-defined function
+}
+```
+
+This technique is useful when measuring performance, or when you want to defer actions that might cause problems if they occur during prefetching (see [Unsafe prefetching](#unsafe_prefetching)).
+
+### JavaScript prerender detection
+
+To run an activity while the page is prerendering, you can check for the {{domxref("Document.prerendering")}} property. You could for example run some analytics:
+
+```js
+if (document.prerendering) {
+ analytics.sendInfo("got this far during prerendering!");
+}
+```
+
+When a prerendered document is activated, {{domxref("PerformanceNavigationTiming.activationStart")}} is set to a {{domxref("DOMHighResTimeStamp")}} representing the time between when the prerender was started and the document was actually activated. The following function can check for prerendering _and_ prerendered pages:
+
+```js
+function pagePrerendered() {
+ return (
+ document.prerendering ||
+ self.performance?.getEntriesByType?.("navigation")[0]?.activationStart > 0
+ );
+}
+```
+
+When the prerendered page is activated by the user viewing the page, the {{domxref("Document.prerenderingchange_event", "prerenderingchange")}} event will fire. This can be used to enable activities that previously would be started by default on page load but which you wish to delay until the page is actually viewed by the user. The following code sets up an event listener to run a function once prerendering has finished, on a prerendered page, or runs it immediately on a non-prerendered page:
+
+```js
+if (document.prerendering) {
+ document.addEventListener("prerenderingchange", initAnalytics, {
+ once: true,
+ });
+} else {
+ initAnalytics();
+}
+```
+
+## Unsafe speculative loading conditions
+
+This section covers conditions to look out for, under which prefetching and/or prerendering are **unsafe**. This means that prefetching/prerendering pages that exhibit these conditions may require mitigations in your code, or need to be avoided altogether.
+
+### Unsafe prefetching
+
+As mentioned earlier, we recommend adopting prefetching broadly, as the risk to reward ratio is fairly low — the potential for resource wastage is minimal, and the performance improvements can be significant. However, you need to make sure prefetched pages do not cause problems with the flow of your application.
+
+When a prefetch is done, the browser downloads the response body of the referenced page via a single GET request, which the user may navigate to at a future time. Problems can arise specifically when the URL of the request performs a server-initiated side effect that you don't want to happen until the URL is actually navigated to.
+
+For example:
+
+- Sign-out URLs.
+- Language switching URLs.
+- "Add to cart" URLs.
+- Sign-in flow URLs where the server causes an SMS to be sent, for example as a one-time password (OTP).
+- URLs that increment a user's usage allowance numbers, such as consuming their monthly free article allowance or starting the timer on their monthly minutes.
+- URLs that initiate server-side ad conversion tracking.
+
+Such issues can be mitigated on the server by watching for the [`Sec-Purpose: prefetch`](/en-US/docs/Web/HTTP/Headers/Sec-Purpose) header as the requests come in, and then running specific code to defer problematic functionality. Later on, when the page is actually navigated to, you can initiate the deferred functionality via JavaScript if needed.
+
+> **Note:** You can find more details about the detection code in the [Detecting prefetched and prerendered pages](#detecting_prefetched_and_prerendered_pages) section.
+
+If the functionality only occurs under normal circumstances when JavaScript runs, then prefetching is safe, since the JavaScript will not run until activation.
+
+It is also potentially risky to prefetch a document whose server-rendered contents will change due to actions the user can take on the current page. This could include, for example, flash sale pages or movie theater seat maps. Test such cases carefully, and mitigate such issues by updating content once the page is loaded. See [Server-rendered varying state](#server-rendered_varying_state) for more details about these cases.
+
+> **Note:** Browsers will cache prefetched pages for a short time (Chrome for example caches them for for 5 minutes) before discarding them, so in any case, your users might see content that is up to 5 minutes out of date.
+
+One final tip is to audit the URLs listed as disallowed in your [`Robots.txt`](/en-US/docs/Glossary/Robots.txt) file — normally these URLs point to pages that can only be accessed by authenticated users, and therefore should not be included in search engine results. Many of these will be fine, but it can be a good place to find URLs unsafe for prefetching (i.e. they exhibit the conditions described above).
+
+### Unsafe prerendering
+
+Prerendering is more risky to adopt than prefetching and should therefore be done more sparingly, in cases where it is worth it. There are more unsafe conditions to watch out for with prerendering so, while the reward is higher, the risk is too.
+
+When a prerender is done, the browser GETs the URL and renders and loads the content into an invisible tab. This includes running the content's JavaScript and loading all subresources, including those fetched by JavaScript. Content can be potentially unsafe to prerender if any of the following conditions are observed:
+
+- The URL is [unsafe to prefetch](#unsafe_prefetching). Read the previous section first if you haven't already, and understand that these conditions also equally apply to unsafe prerendering.
+- The page's JavaScript modifies client-side storage (for example [Web Storage](/en-US/docs/Web/API/Web_Storage_API) or [IndexedDB](/en-US/docs/Web/API/IndexedDB_API)) on load in a way that may cause confusing effects in other, non-prerendered pages that the user is currently looking at.
+- The page runs JavaScript or loads images that cause side effects such as sending analytics, recording ad impressions, or otherwise modifying the state of the application as if the user had already interacted with it. Again, this can affect the flow of the application, or cause incorrect performance or usage reporting. See [Server-rendered varying state](#server-rendered_varying_state) for more details about such use cases.
+
+To mitigate such problems, you can use the following techniques:
+
+- Watch for the [`Sec-Purpose: prerender`](/en-US/docs/Web/HTTP/Headers/Sec-Purpose) header on the server as the requests come in, and then run specific code to defer problematic functionality.
+- Use the {{domxref("Document.prerenderingchange_event", "prerenderingchange")}} event to detect when the prerendered page is actually activated and run code as a result. This is useful in two cases:
+ - Deferring code that may cause problems if it is run before the page is viewed. For example, you may want to wait until after activation to update client-side storage or modify server-side state using JavaScript. This can avoid situations when the UI and the application state become out of sync with one another, for example a shopping cart showing no items even though the user has added some.
+ - If the above is not possible, then you could still rerun code after the page is activated to bring the app up-to-date again. For example, a highly-dynamic flash sale page might rely on content updates coming in from a third-party library. If you can't delay the updates, you can always get fresh updates once the user views the page. Prerendered pages can be updated in real time using the [Broadcast Channel API](/en-US/docs/Web/API/Broadcast_Channel_API), or another mechanism such as {{domxref("fetch()")}} or a {{domxref("WebSocket")}}. This guarantees that the user will see up-to-date content after prerendering activation.
+- Manage your third-party analytics scripts carefully — if possible, use scripts that are prerendering-aware (for example use the {{domxref("Document.prerendering")}} property to defer running on prerendering pages) such as Google Analytics or NewRelic.
+ - Note that cross-origin {{htmlelement("iframe")}} loads are delayed while prerendering, therefore most other third-party widgets such as adtech are actually safe to use while prerendering.
+ - For third-party scripts that are not prerendering-aware, avoid loading them until after activation using the {{domxref("Document.prerenderingchange_event", "prerenderingchange")}} event, as mentioned earlier.
+
+### Server-rendered varying state
+
+There are two main types of server-rendered state to be concerned with: **outdated state**, and **user-specific state**. This can cause both unsafe prefetching and prerendering.
+
+- Outdated state: Consider the example of a server-rendered list of blog comments, which may become out of date between the blog post being prerendered, and it being viewed. This might be particularly problematic if the current page is an admin panel where the user is deleting spam comments. If the user then navigates to the blog post, they might be confused as to why they can see the spam comments they just deleted.
+- User-specific state: Consider the example of tracking sign-in state via a cookie. Problems can arise like the following:
+ - The user visits `https://site.example/a` in tab 1 and `https://site.example/b` in tab 2, while logged out.
+ - `https://site.example/b` prerenders `https://site.example/c`. It will be prerendered in a logged-out state.
+ - The user signs in to `https://site.example` in tab 1.
+ - The user switches to tab 2 and clicks the link to `https://site.example/c`, which activates the prerendered page.
+ - Tab 2 displays a signed-out view of `https://site.example/c`, which confuses the user since they thought they were logged in.
+
+User-specific state problems can occur for other user settings, for example language settings, dark-mode preferences, or adding items to a cart. They can also occur when only a single tab is involved:
+
+- Let's say the user visits `https://site.example/product`.
+- `https://site.example.com/product` prerenders `https://site.example.com/cart`. It prerenders with 0 items in the cart.
+- The user clicks on the "Add to cart" buttons, which initiates a fetch request to add the item to the user's cart (with no page reload).
+- The user clicks on the link to `https://site.example.com/cart`, which activates the prerendered page.
+- The user sees an empty cart, even though they just added something to it.
+
+The best mitigation for these cases, and indeed any time when content can get out of sync with the server, is for pages to refresh themselves as needed. For example, a server might use the [Broadcast Channel API](/en-US/docs/Web/API/Broadcast_Channel_API), or another mechanism such as {{domxref("fetch()")}} or a {{domxref("WebSocket")}}. Pages can then update themselves appropriately, including speculatively loaded pages that have not yet activated.
+
+## Session history behavior for prerendered documents
+
+Activating a prerendering/prerendered document behaves like any conventional navigation, from the end-user perspective. The activated document is displayed in the tab and appended to session history, and any existing forward history entries are pruned. Any navigations taking place within the prerendering browsing context _before_ activation do not affect the session history.
+
+From the developer's perspective, a prerendering document can be thought of as having a **trivial session history** where only one entry — the current entry — exists. All navigations within the prerendering context are effectively replaced.
+
+While API features that operate on session history (for example {{domxref("History")}} and {{domxref("Navigation")}}) can be called within prerendering documents, they only operate on the context's trivial session history. Consequently, prerendering documents do not take part in their referring page's joint session history. For example, they cannot navigate their referrer via {{domxref("History.back()")}}.
+
+This design ensures that users get the expected experience when using the back button — i.e. that they are taken back to the last thing they saw. Once a prerendering document is activated, only a single session history entry gets appended to the joint session history, ignoring any previous navigations that happened within the prerendering browsing context. Going back one step in the joint session history — for example by pressing the back button — takes the user back to the referrer page.
+
+## Platform features deferred or restricted during prerender
+
+Because a prerendered page is opened in a hidden state, a number of APIs and other web platform features that cause potentially intrusive behaviors are not activated in this state, and are instead deferred until the page is activated or restricted altogether. In the small number of cases where this is not yet possible, the prerender is canceled.
+
+### Asynchronous API deferral
+
+The following asynchronous features' results are deferred in prerendered documents until they are activated:
+
+- [Audio Output Devices API](/en-US/docs/Web/API/Audio_Output_Devices_API): {{domxref("MediaDevices.selectAudioOutput()")}}
+- [Background Fetch API](/en-US/docs/Web/API/Background_Fetch_API): {{domxref("BackgroundFetchManager.fetch()")}}
+- [Broadcast Channel API](/en-US/docs/Web/API/Broadcast_Channel_API): {{domxref("BroadcastChannel.postMessage()")}}
+- [Credential Management API](/en-US/docs/Web/API/Credential_Management_API): {{domxref("CredentialsContainer.create()")}}, {{domxref("CredentialsContainer.get()")}}, {{domxref("CredentialsContainer.store()")}}
+- [Encrypted Media Extensions API](/en-US/docs/Web/API/Encrypted_Media_Extensions_API): {{domxref("Navigator.requestMediaKeySystemAccess()")}}
+- [Gamepad API](/en-US/docs/Web/API/Gamepad_API): {{domxref("Navigator.getGamepads()")}}, {{domxref("Window.gamepadconnected_event", "gamepadconnected")}} event, {{domxref("Window.gamepaddisconnected_event", "gamepaddisconnected")}} event
+- [Geolocation API](/en-US/docs/Web/API/Geolocation_API): {{domxref("Geolocation.getCurrentPosition()")}}, {{domxref("Geolocation.watchPosition()")}}, {{domxref("Geolocation.clearWatch()")}}
+- {{domxref("HTMLMediaElement")}} API: The playback position will not advance while the containing document is prerendering
+- [Idle Detection API](/en-US/docs/Web/API/Idle_Detection_API): {{domxref("IdleDetector.start()")}}
+- [Media Capture and Streams API](/en-US/docs/Web/API/Media_Capture_and_Streams_API): {{domxref("MediaDevices.getUserMedia()")}} (and the legacy {{domxref("Navigator.getUserMedia()")}} version), {{domxref("MediaDevices.enumerateDevices()")}}
+- [Notifications API](/en-US/docs/Web/API/Notifications_API): {{domxref("Notification.Notification", "Notification()")}} constructor, {{domxref("Notification.requestPermission()")}}
+- [Push API](/en-US/docs/Web/API/Push_API): {{domxref("PushManager.subscribe()")}}
+- [Screen Orientation API](/en-US/docs/Web/API/Screen_Orientation_API): {{domxref("ScreenOrientation.lock()")}}, {{domxref("ScreenOrientation.unlock()")}}
+- [Sensor APIs](/en-US/docs/Web/API/Sensor_APIs): {{domxref("Sensor.start()")}}
+- [Service Worker API](/en-US/docs/Web/API/Service_Worker_API): {{domxref("ServiceWorker.postMessage()")}}, {{domxref("ServiceWorkerContainer.register()")}}, {{domxref("ServiceWorkerRegistration.update()")}}, {{domxref("ServiceWorkerRegistration.unregister()")}}
+- [Storage API](/en-US/docs/Web/API/Storage_API): {{domxref("StorageManager.persist()")}}
+- [Web Audio API](/en-US/docs/Web/API/Web_Audio_API): {{domxref("AudioContext")}}s are not allowed to start while the containing document is prerendering
+- [Web Bluetooth API](/en-US/docs/Web/API/Web_Bluetooth_API): {{domxref("Bluetooth.getDevices()")}}, {{domxref("Bluetooth.requestDevice()")}}
+- [WebHID API](/en-US/docs/Web/API/WebHID_API): {{domxref("HID.getDevices()")}}, {{domxref("HID.requestDevice()")}}
+- [Web Locks API](/en-US/docs/Web/API/Web_Locks_API): {{domxref("LockManager.query()")}}, {{domxref("LockManager.request()")}}
+- [Web MIDI API](/en-US/docs/Web/API/Web_MIDI_API): {{domxref("Navigator.requestMIDIAccess()")}}
+- [Web NFC API](/en-US/docs/Web/API/Web_NFC_API): {{domxref("NDefReader.write()")}}, {{domxref("NDefReader.scan()")}}
+- [Web Serial API](/en-US/docs/Web/API/Web_Serial_API): {{domxref("Serial.getPorts()")}}, {{domxref("Serial.requestPort()")}}
+- [Web Speech API](/en-US/docs/Web/API/Web_Speech_API): {{domxref("SpeechRecognition.abort()")}}, {{domxref("SpeechRecognition.start()")}}, {{domxref("SpeechRecognition.stop()")}}, {{domxref("SpeechSynthesis.cancel()")}}, {{domxref("SpeechSynthesis.pause()")}}, {{domxref("SpeechSynthesis.resume()")}}, {{domxref("SpeechSynthesis.speak()")}}
+- [WebUSB API](/en-US/docs/Web/API/WebUSB_API): {{domxref("USB.getDevices()")}}, {{domxref("USB.requestDevice()")}}
+- [WebXR Device API](/en-US/docs/Web/API/WebXR_Device_API): {{domxref("XRSystem.requestSession()")}}
+
+### Implicitly restricted APIs
+
+The following features will automatically fail or no-op in documents that are not activated.
+
+APIs that require [transient activation](/en-US/docs/Glossary/Transient_activation) or [sticky activation](/en-US/docs/Glossary/Sticky_activation):
+
+- Confirmation dialogs generated by the {{domxref("Window.beforeunload_event", "beforeunload")}} event
+- The firing of any events in the [Clipboard API](/en-US/docs/Web/API/Clipboard_API).
+- [File System API](/en-US/docs/Web/API/File_System_API): {{domxref("Window.showDirectoryPicker()")}}, {{domxref("Window.showOpenFilePicker()")}}, {{domxref("Window.showSaveFilePicker()")}}
+- [Fullscreen API](/en-US/docs/Web/API/Fullscreen_API): {{domxref("Element.requestFullscreen()")}}
+- [Idle Detection API](/en-US/docs/Web/API/Idle_Detection_API): {{domxref("IdleDetector.requestPermission()")}}
+- [Keyboard API](/en-US/docs/Web/API/Keyboard_API): {{domxref("Keyboard.lock()")}} (which requires fullscreen)
+- [Payment Request API](/en-US/docs/Web/API/Payment_Request_API): {{domxref("PaymentRequest.show()")}}
+- [Presentation API](/en-US/docs/Web/API/Presentation_API): {{domxref("PresentationRequest.start()")}}
+- [Pointer Lock API](/en-US/docs/Web/API/Pointer_Lock_API): {{domxref("Element.requestPointerLock()")}}
+- [Screen Capture API](/en-US/docs/Web/API/Screen_Capture_API): {{domxref("MediaDevices.getDisplayMedia()")}}
+- [Web Share API](/en-US/docs/Web/API/Web_Share_API): {{domxref("Navigator.share()")}}
+- {{domxref("Window.open()")}}
+
+APIs that require the containing document to be focused:
+
+- [Clipboard API](/en-US/docs/Web/API/Clipboard_API): {{domxref("Clipboard.read()")}}, {{domxref("Clipboard.readText()")}}, {{domxref("Clipboard.write()")}}, {{domxref("Clipboard.writeText()")}}
+
+APIs that require the containing document's {{domxref("Document.visibilityState")}} to be `"visible"`:
+
+- [Picture-in-Picture API](/en-US/docs/Web/API/Picture-in-Picture_API): {{domxref("HTMLVideoElement.requestPictureInPicture()")}} (requires the containing document's visibility state to be `"visible", _or_ [transient activation](/en-US/docs/Glossary/Transient_activation))
+- [Screen Wake Lock API](/en-US/docs/Web/API/Screen_Wake_Lock_API): {{domxref("WakeLock.request()")}}
+
+### Other restricted features
+
+- Download links, i.e. {{htmlelement("a")}} and {{htmlelement("area")}} elements with the `download` attribute, will have their downloads delayed until prerendering has finished.
+- No cross-site navigations: Any prerendering document that navigates to a different site will be immediately discarded before a request to that other site is sent.
+- Restricted URLs: Prerendering documents cannot host non-HTTP(S) top-level URLs. Including the following URL types will cause the prerender to be immediately discarded:
+ - `javascript:` URLs
+ - `data:` URLs
+ - `blob:` URLs
+ - `about:` URLs, including `about:blank` and `about:srcdoc`
+- Session storage: {{domxref("Window.sessionStorage")}} can be used, but the behavior is very specific, to avoid breaking sites that expect only one page to access the tab's session storage at a time. A prerendered page therefore starts out with a clone of the tab's session storage state from when it was created. Upon activation, the prerendered page's storage clone is discarded, and the tab's main storage state is used instead. Pages that use session storage can use the {{domxref("Document.prerenderingchange_event", "prerenderingchange")}} event to detect when this storage swap occurs.
+- {{domxref("Window.print()")}}: Any calls to this method are ignored.
+- "Simple dialog methods" are restricted as follows:
+ - {{domxref("Window.alert()")}} immediately returns without showing a dialog.
+ - {{domxref("Window.confirm()")}} immediately returns `false` without showing a dialog.
+ - {{domxref("Window.prompt()")}} immediately returns an empty string (`""`) without showing a dialog.
+- Dedicated/shared worker scripts are loaded, but their execution is deferred until the prerendered document is activated.
+- Cross-origin {{htmlelement("iframe")}} loads are delayed while prerendering until after the page is activated.
+
+## Interfaces
+
+The Speculation Rules API does not define any interfaces of its own.
+
+### Extensions to other interfaces
+
+- {{domxref("Document.prerendering")}}
+ - : A boolean property that returns `true` if the document is currently in the process of prerendering.
+- {{domxref("Document.prerenderingchange_event", "prerenderingchange")}} event
+ - : Fired on a prerendered document when it is activated (i.e. the user views the page).
+- {{domxref("PerformanceNavigationTiming.activationStart")}}
+ - : A number representing the time between when a document starts prerendering and when it is activated.
+- {{domxref("PerformanceResourceTiming.deliveryType")}}, `"navigational-prefetch"` value
+ - : Signals that the type of a performance entry is a prefetch.
+
+## HTTP headers
+
+- [`Content-Security-Policy`](/en-US/docs/Web/HTTP/Headers/Content-Security-Policy) `'inline-speculation-rules'` value
+ - : Used to opt-in to allowing usage of ``](/en-US/docs/Web/HTML/Element/script/type/speculationrules)
+ - : Used to define a set of prefetch and/or prerender speculation rules on the current document.
+
+## Examples
+
+You can find a [complete prerender demo here](https://prerender-demos.glitch.me/).
+
+## Specifications
+
+{{Specifications}}
+
+## Browser compatibility
+
+{{Compat}}
+
+## See also
+
+- [Prerender pages in Chrome for instant page navigations](https://developer.chrome.com/blog/prerender-pages/) on developer.chrome.com (2023)
+- [Speculative loading](/en-US/docs/Web/Performance/Speculative_loading) for a comparison of speculation rules and other similar performance improvement features.
diff --git a/files/en-us/web/api/storage_access_api/index.md b/files/en-us/web/api/storage_access_api/index.md
index 678b338c54a370a..58e4b037061a256 100644
--- a/files/en-us/web/api/storage_access_api/index.md
+++ b/files/en-us/web/api/storage_access_api/index.md
@@ -11,7 +11,7 @@ browser-compat:
The Storage Access API provides a way for cross-site content loaded in a third-party context (i.e. embedded in an {{htmlelement("iframe")}}) to gain access to _unpartitioned cookies_ that it would normally only have access to in a first-party context (i.e. when loaded directly in a browser tab).
-> **Note:** When we say _unpartitioned cookies_, we are talking about cookies stored in the traditional way they have historically been stored since the early web — all cookies set on the same site are stored in the same cookie jar. This is in contrast to _partitioned cookies_, where embedded resources under each top-level origin are given a unique cookie storage space (see for example [Cookies Having Independent Partitioned State (CHIPS)](/en-US/docs/Web/Privacy/Partitioned_cookies)).
+> **Note:** When we say _unpartitioned cookies_, we are talking about cookies stored in the traditional way they have historically been stored since the early web — all cookies set on the same site are stored in the same cookie jar. This is in contrast to _partitioned cookies_, where embedded resources under each top-level site are given a unique cookie storage space (see for example [Cookies Having Independent Partitioned State (CHIPS)](/en-US/docs/Web/Privacy/Partitioned_cookies)).
The Storage Access API is relevant to user agents that by default block access to unpartitioned cookies by sites loaded in a third-party context to improve privacy (for example, to prevent tracking). There are legitimate uses for unpartitioned cookie access by third-party content that we still want to enable, even with these default restrictions in place. Examples include SSO with federated IdPs, or persisting user details such as location data or viewing preferences across different sites.
@@ -86,8 +86,6 @@ Although the API surface is the same, websites using the Storage Access API shou
Design properties unique to Firefox are summarized here:
- If the embedded origin `tracker.example` has already obtained unpartitioned cookie access on the top-level origin `foo.example`, and the user visits a page from `foo.example` embedding a page from `tracker.example` again in less than 30 days, the embedded origin will have unpartitioned cookie access immediately when loading.
-- If an embedded page from `tracker.example` has previously successfully obtained unpartitioned cookie access on top-level origin `foo.example`, all embedded subresources from `tracker.example` on `foo.example` (e.g. scripts, images, stylesheets, etc.) will load with access to their cookies, which means they may send Cookie headers and honor incoming {{httpheader("Set-Cookie")}} headers.
-- In Firefox, when the promise returned from `requestStorageAccess()` is resolved, the embedded page will gain access to its entire first-party storage, not just unpartitioned cookies. This includes access to APIs such as [Web Storage](/en-US/docs/Web/API/Web_Storage_API), [IndexedDB](/en-US/docs/Web/API/IndexedDB_API), [DOM Cache](/en-US/docs/Web/API/Cache), and so on.
- The storage access grants are phased out after 30 calendar days have passed.
Documentation for Firefox's new storage access policy for blocking tracking cookies includes [a detailed description](/en-US/docs/Web/Privacy/Storage_Access_Policy#storage_access_grants) of the scope of storage access grants.
diff --git a/files/en-us/web/api/storage_access_api/using/index.md b/files/en-us/web/api/storage_access_api/using/index.md
index 745ca019093c0e3..c20834c2d1d347e 100644
--- a/files/en-us/web/api/storage_access_api/using/index.md
+++ b/files/en-us/web/api/storage_access_api/using/index.md
@@ -45,7 +45,7 @@ function doThingsWithCookies() {
}
async function handleCookieAccess() {
- if (document.hasStorageAccess) {
+ if (!document.hasStorageAccess) {
// This browser doesn't support the Storage Access API
// so let's just hope we have access!
doThingsWithCookies();
diff --git a/files/en-us/web/api/storage_api/storage_quotas_and_eviction_criteria/index.md b/files/en-us/web/api/storage_api/storage_quotas_and_eviction_criteria/index.md
index 52f9a2cb121f6af..b0f0c726ab6b668 100644
--- a/files/en-us/web/api/storage_api/storage_quotas_and_eviction_criteria/index.md
+++ b/files/en-us/web/api/storage_api/storage_quotas_and_eviction_criteria/index.md
@@ -107,7 +107,13 @@ Like with Firefox, because this quota is calculated based on the hard drive tota
#### Safari
-In Safari, an origin is given an initial 1 GiB quota. Once the origin reaches this limit, Safari asks the user for permission to let the origin store more data. This happens whether the origin stores data in best-effort mode or persistent mode.
+Starting with macOS 14 and iOS 17, Safari allots up to around 20% of the total disk space for each origin. If the user has saved it as a web app on the Home Screen or the Dock, this limit is increased to up to 60% of the disk size. For privacy reasons, {{Glossary("Same-origin policy", "cross-origin")}} frames have a separate quota, amounting to roughly 1/10 of their parents.
+
+For instance, a macOS device with a 1 TiB drive will limit each origin to around 200 GiB. If the user stores a web app on its Dock, that will be alloted a greater limit of around 600 GiB.
+
+Like other browsers, the exact limits enforced by the quota may vary as to avoid fingerprinting. Additionally, Safari also enforces an overall quota that stored data across all origins cannot grow beyond: 80% of disk size for each browser and web app, and 15% of disk size for each non-browser app that displays web content. More info on Safari's storage policies can be found on the [Webkit blog](https://www.webkit.org/blog/14403/updates-to-storage-policy/).
+
+In earlier versions of Safari, an origin is given an initial 1 GiB quota. Once the origin reaches this limit, Safari asks the user for permission to let the origin store more data. This happens whether the origin stores data in best-effort mode or persistent mode.
## How to check the available space?
diff --git a/files/en-us/web/api/storagemanager/estimate/index.md b/files/en-us/web/api/storagemanager/estimate/index.md
index 85d6105336595f4..ee29c881e7d7ad4 100644
--- a/files/en-us/web/api/storagemanager/estimate/index.md
+++ b/files/en-us/web/api/storagemanager/estimate/index.md
@@ -81,7 +81,7 @@ navigator.storage.estimate().then((estimate) => {
## See also
-- Storage API
+- [Storage API](/en-US/docs/Web/API/Storage_API)
- {{domxref("Storage")}}, the object returned by {{domxref("Window.localStorage")}}
- {{domxref("StorageManager")}}
- {{domxref("navigator.storage")}}
diff --git a/files/en-us/web/api/streams_api/using_readable_byte_streams/index.md b/files/en-us/web/api/streams_api/using_readable_byte_streams/index.md
index 47b5fb9f6bdde6e..7bb7dcd7e7e3187 100644
--- a/files/en-us/web/api/streams_api/using_readable_byte_streams/index.md
+++ b/files/en-us/web/api/streams_api/using_readable_byte_streams/index.md
@@ -414,16 +414,18 @@ This live example shows how data might be read from an "pull" underlying byte so
For the underlying pull source we use the following class to (_very_ superficially) mock a nodejs [`FileHandle`](https://nodejs.org/api/fs.html#class-filehandle), and in particular the [`read()`](https://nodejs.org/api/fs.html#filehandlereadbuffer-offset-length-position) method.
The class generates random data to represent a file.
-The `read()` method reads from this data into a provided buffer from the specified position.
+The `read()` method reads a "semi-random" sized block of random data into a provided buffer from the specified position.
The `close()` method does nothing: it is only provided to show where you might close the source when defining the constructor for the stream.
-> **Note:** This same class is used for all the "pull source" examples.
+> **Note:** A similar class is used for all the "pull source" examples.
> It is shown here for information only (so that it is obvious that it is a mock).
```js
class MockUnderlyingFileHandle {
constructor() {
- this.maxdata = 1300; // "file size"
+ this.maxdata = 100; // "file size"
+ this.maxReadChunk = 25; // "max read chunk size"
+ this.minReadChunk = 13; // "min read chunk size"
this.filedata = this.randomByteArray(this.maxdata);
this.position = 0;
}
@@ -442,13 +444,21 @@ class MockUnderlyingFileHandle {
return;
}
+ // Simulate a file read that returns random numbers of bytes
+ // Read minimum of bytes requested and random bytes that can be returned
+ let readLength =
+ Math.floor(
+ Math.random() * (this.maxReadChunk - this.minReadChunk + 1),
+ ) + this.minReadChunk;
+ readLength = length > readLength ? readLength : length;
+
// Read random data into supplied buffer
- const myview = new Uint8Array(buffer, offset, length);
+ const myview = new Uint8Array(buffer, offset, readLength);
// Write the length of data specified
- for (let i = 0; i < length; i++) {
+ for (let i = 0; i < readLength; i++) {
myview[i] = this.filedata[position + i];
- resultobj["bytesRead"] = i;
- if (position + i >= this.maxdata) {
+ resultobj["bytesRead"] = i + 1;
+ if (position + i + 1 >= this.maxdata) {
break;
}
}
@@ -573,8 +583,8 @@ function makeReadableByteFileStream(filename) {
const theView = controller.byobRequest.view;
const { bytesRead, buffer } = await fileHandle.read(
theView.buffer,
- theView.offset,
- theView.length,
+ theView.byteOffset,
+ theView.byteLength,
position,
);
if (bytesRead === 0) {
@@ -608,40 +618,40 @@ When the underlying source signals that it has no more data, the `reader.read()`
```js
const reader = stream.getReader({ mode: "byob" });
-let buffer = new ArrayBuffer(4000);
+let buffer = new ArrayBuffer(200);
readStream(reader);
function readStream(reader) {
let bytesReceived = 0;
let offset = 0;
- while (offset < buffer.byteLength) {
- // read() returns a promise that resolves when a value has been received
- reader
- .read(new Uint8Array(buffer, offset, buffer.byteLength - offset))
- .then(function processText({ done, value }) {
- // Result objects contain two properties:
- // done - true if the stream has already given all its data.
- // value - some data. Always undefined when done is true.
-
- if (done) {
- logConsumer(`readStream() complete. Total bytes: ${bytesReceived}`);
- return;
- }
+ // read() returns a promise that resolves when a value has been received
+ reader
+ .read(new Uint8Array(buffer, offset, buffer.byteLength - offset))
+ .then(function processText({ done, value }) {
+ // Result objects contain two properties:
+ // done - true if the stream has already given all its data.
+ // value - some data. Always undefined when done is true.
+
+ if (done) {
+ logConsumer(`readStream() complete. Total bytes: ${bytesReceived}`);
+ return;
+ }
- buffer = value.buffer;
- offset += value.byteLength;
- bytesReceived += value.byteLength;
+ buffer = value.buffer;
+ offset += value.byteLength;
+ bytesReceived += value.byteLength;
- logConsumer(`Read ${bytesReceived} bytes: ${value}`);
- result += value;
+ logConsumer(
+ `Read ${value.byteLength} (${bytesReceived}) bytes: ${value}`,
+ );
+ result += value;
- // Read some more, and call this function again
- return reader
- .read(new Uint8Array(buffer, offset, buffer.byteLength - offset))
- .then(processText);
- });
- }
+ // Read some more, and call this function again
+ return reader
+ .read(new Uint8Array(buffer, offset, buffer.byteLength - offset))
+ .then(processText);
+ });
}
```
@@ -661,7 +671,8 @@ The logging from the underlying pull source (left) and consumer (right) are show
Of particular note are that the:
- `start()` function is passed a `ReadableByteStreamController`
-- the buffer passed to the reader is large enough to encompass the whole "file", so the whole file is transferred in one operation.
+- the buffer passed to the reader is large enough to encompass the whole "file".
+ The underlying data source supplies the data in random-sized chunks.
{{EmbedLiveSample("Underlying pull source","100%","500px")}}
@@ -673,12 +684,14 @@ This uses the same [mocked underlying file source](#mocked_underlying_file_sourc
```js hidden
class MockUnderlyingFileHandle {
constructor() {
- this.maxdata = 1300; // "file size"
+ this.maxdata = 100; // "file size"
+ this.maxReadChunk = 25; // "max read chunk size"
+ this.minReadChunk = 13; // "min read chunk size"
this.filedata = this.randomByteArray(this.maxdata);
this.position = 0;
}
- /* Read data from "file" at position/length into specified buffer offset */
+ // Read data from "file" at position/length into specified buffer offset
read(buffer, offset, length, position) {
// Object used to resolve promise
const resultobj = {};
@@ -687,23 +700,33 @@ class MockUnderlyingFileHandle {
return new Promise((resolve /*, reject*/) => {
if (position >= this.maxdata) {
- // out of data
+ //out of data
resolve(resultobj);
return;
}
+ // Simulate a file read that returns random numbers of bytes
+ // Read minimum of bytes requested and random bytes that can be returned
+ let readLength =
+ Math.floor(
+ Math.random() * (this.maxReadChunk - this.minReadChunk + 1),
+ ) + this.minReadChunk;
+ readLength = length > readLength ? readLength : length;
+
// Read random data into supplied buffer
- const myview = new Uint8Array(buffer, offset, length);
+ const myview = new Uint8Array(buffer, offset, readLength);
// Write the length of data specified
- for (let i = 0; i < length; i++) {
+ for (let i = 0; i < readLength; i++) {
myview[i] = this.filedata[position + i];
- resultobj["bytesRead"] = i;
- if (position + i >= this.maxdata) {
+ resultobj["bytesRead"] = i + 1;
+ if (position + i + 1 >= this.maxdata) {
break;
}
}
// Emulate slow read of data
- setTimeout(() => resolve(resultobj), 1000);
+ setTimeout(() => {
+ resolve(resultobj);
+ }, 1000);
});
}
@@ -790,7 +813,7 @@ function logConsumer(result) {
The only difference in our underlying source is that we must specify `autoAllocateChunkSize`, and that the size will be used as the view buffer size for `controller.byobRequest`, rather than one supplied by the consumer.
```js
-const DEFAULT_CHUNK_SIZE = 200;
+const DEFAULT_CHUNK_SIZE = 20;
const stream = makeReadableByteFileStream("dummy file.txt");
function makeReadableByteFileStream(filename) {
@@ -811,8 +834,8 @@ function makeReadableByteFileStream(filename) {
const theView = controller.byobRequest.view;
const { bytesRead, buffer } = await fileHandle.read(
theView.buffer,
- theView.offset,
- theView.length,
+ theView.byteOffset,
+ theView.byteLength,
position,
);
if (bytesRead === 0) {
@@ -864,7 +887,9 @@ function readStream(reader) {
}
bytesReceived += value.length;
- logConsumer(`Read ${bytesReceived} bytes so far. Current bytes = ${value}`);
+ logConsumer(
+ `Read ${value.length} (${bytesReceived}). Current bytes = ${value}`,
+ );
result += value;
// Read some more, and call this function again
@@ -885,9 +910,9 @@ button.addEventListener("click", () => {
#### Result
-The logging from the underlying bye pull source (left) and consumer (right) are shown below.
+The logging from the underlying byte pull source (left) and consumer (right) are shown below.
-Note that the chunks are now 200-byte wide, as specified in the underlying byte source.
+Note that the chunks are now _at most_ 20-byte wide, as this is the size of the auto allocated buffer specified in the underlying byte source (`autoAllocateChunkSize`).
These are made as zero-copy transfers.
{{EmbedLiveSample("Underlying pull source with default reader","100%","500px")}}
@@ -899,7 +924,9 @@ For completeness, we can also use a default reader with a byte source that does
```js hidden
class MockUnderlyingFileHandle {
constructor() {
- this.maxdata = 1300; // "file size"
+ this.maxdata = 100; // "file size"
+ this.maxReadChunk = 25; // "max read chunk size"
+ this.minReadChunk = 13; // "min read chunk size"
this.filedata = this.randomByteArray(this.maxdata);
this.position = 0;
}
@@ -918,13 +945,21 @@ class MockUnderlyingFileHandle {
return;
}
+ // Simulate a file read that returns random numbers of bytes
+ // Read minimum of bytes requested and random bytes that can be returned
+ let readLength =
+ Math.floor(
+ Math.random() * (this.maxReadChunk - this.minReadChunk + 1),
+ ) + this.minReadChunk;
+ readLength = length > readLength ? readLength : length;
+
// Read random data into supplied buffer
- const myview = new Uint8Array(buffer, offset, length);
+ const myview = new Uint8Array(buffer, offset, readLength);
// Write the length of data specified
- for (let i = 0; i < length; i++) {
+ for (let i = 0; i < readLength; i++) {
myview[i] = this.filedata[position + i];
- resultobj["bytesRead"] = i;
- if (position + i >= this.maxdata) {
+ resultobj["bytesRead"] = i + 1;
+ if (position + i + 1 >= this.maxdata) {
break;
}
}
@@ -1019,7 +1054,7 @@ Note below that to support this case, in `pull()` we need to check if the `byobR
```js
const stream = makeReadableByteFileStream("dummy file.txt");
-const DEFAULT_CHUNK_SIZE = 300;
+const DEFAULT_CHUNK_SIZE = 40;
function makeReadableByteFileStream(filename) {
let fileHandle;
@@ -1040,8 +1075,8 @@ function makeReadableByteFileStream(filename) {
const theView = controller.byobRequest.view;
const { bytesRead, buffer } = await fileHandle.read(
theView.buffer,
- theView.offset,
- theView.length,
+ theView.byteOffset,
+ theView.byteLength,
position,
);
if (bytesRead === 0) {
@@ -1062,8 +1097,8 @@ function makeReadableByteFileStream(filename) {
const mynewBuffer = new Uint8Array(DEFAULT_CHUNK_SIZE);
const { bytesRead, buffer } = await fileHandle.read(
mynewBuffer.buffer,
- mynewBuffer.offset,
- mynewBuffer.length,
+ mynewBuffer.byteOffset,
+ mynewBuffer.byteLength,
position,
);
if (bytesRead === 0) {
diff --git a/files/en-us/web/api/streams_api/using_readable_streams/index.md b/files/en-us/web/api/streams_api/using_readable_streams/index.md
index 628a4f3d5a508a4..044419c4eac47b5 100644
--- a/files/en-us/web/api/streams_api/using_readable_streams/index.md
+++ b/files/en-us/web/api/streams_api/using_readable_streams/index.md
@@ -207,7 +207,7 @@ button.addEventListener("click", () => aborter.abort());
logChunks("http://example.com/somefile.txt", { signal: aborter.signal });
async function logChunks(url, { signal }) {
- const response = await fetch(url, signal);
+ const response = await fetch(url, { signal });
for await (const chunk of response.body) {
// Do something with the chunk
}
diff --git a/files/en-us/web/api/stylepropertymapreadonly/index.md b/files/en-us/web/api/stylepropertymapreadonly/index.md
index 44cc1562998c5b1..27e9217aa05fed6 100644
--- a/files/en-us/web/api/stylepropertymapreadonly/index.md
+++ b/files/en-us/web/api/stylepropertymapreadonly/index.md
@@ -66,7 +66,7 @@ const stylesList = document.querySelector("#output");
// Retrieve all computed styles with computedStyleMap()
const stylePropertyMap = myElement.computedStyleMap();
-// iterate thru the map of all the properties and values, adding a
and for each
+// iterate through the map of all the properties and values, adding a and for each
for (const [prop, val] of stylePropertyMap) {
// properties
const cssProperty = document.createElement("dt");
diff --git a/files/en-us/web/api/subtlecrypto/derivekey/index.md b/files/en-us/web/api/subtlecrypto/derivekey/index.md
index 8fe58f9d569dbec..e43424bf618e095 100644
--- a/files/en-us/web/api/subtlecrypto/derivekey/index.md
+++ b/files/en-us/web/api/subtlecrypto/derivekey/index.md
@@ -30,8 +30,7 @@ deriveKey(algorithm, baseKey, derivedKeyAlgorithm, extractable, keyUsages)
- : An object defining the [derivation algorithm](#supported_algorithms) to use.
- To use [ECDH](#ecdh), pass an
[`EcdhKeyDeriveParams`](/en-US/docs/Web/API/EcdhKeyDeriveParams) object.
- - To use [HKDF](#hkdf), pass
- an [`HkdfParams`](/en-US/docs/Web/API/HkdfParams) object.
+ - To use [HKDF](#hkdf), pass an [`HkdfParams`](/en-US/docs/Web/API/HkdfParams) object.
- To use [PBKDF2](#pbkdf2), pass
a [`Pbkdf2Params`](/en-US/docs/Web/API/Pbkdf2Params) object.
- `baseKey`
@@ -42,12 +41,13 @@ deriveKey(algorithm, baseKey, derivedKeyAlgorithm, extractable, keyUsages)
`CryptoKey` using
[`SubtleCrypto.importKey()`](/en-US/docs/Web/API/SubtleCrypto/importKey).
- `derivedKeyAlgorithm`
- - : An object defining the algorithm the derived key will be used for.
- - For [HMAC](/en-US/docs/Web/API/SubtleCrypto/sign#hmac): pass an
- [`HmacKeyGenParams`](/en-US/docs/Web/API/HmacKeyGenParams) object.
+ - : An object defining the algorithm the derived key will be used for:
+ - For [HMAC](/en-US/docs/Web/API/SubtleCrypto/sign#hmac) pass an [`HmacKeyGenParams`](/en-US/docs/Web/API/HmacKeyGenParams) object.
- For [AES-CTR](/en-US/docs/Web/API/SubtleCrypto/encrypt#aes-ctr), [AES-CBC](/en-US/docs/Web/API/SubtleCrypto/encrypt#aes-cbc),
- [AES-GCM](/en-US/docs/Web/API/SubtleCrypto/encrypt#aes-gcm), or [AES-KW](/en-US/docs/Web/API/SubtleCrypto/wrapKey#aes-kw): pass an
+ [AES-GCM](/en-US/docs/Web/API/SubtleCrypto/encrypt#aes-gcm), or [AES-KW](/en-US/docs/Web/API/SubtleCrypto/wrapKey#aes-kw), pass an
[`AesKeyGenParams`](/en-US/docs/Web/API/AesKeyGenParams) object.
+ - For [HKDF](#hkdf), pass an [`HkdfParams`](/en-US/docs/Web/API/HkdfParams) object.
+ - For [PBKDF2](#pbkdf2), pass a [`Pbkdf2Params`](/en-US/docs/Web/API/Pbkdf2Params) object.
- `extractable`
- : A boolean value indicating whether it
will be possible to export the key using {{domxref("SubtleCrypto.exportKey()")}} or
@@ -242,6 +242,51 @@ async function encrypt(plaintext, salt, iv) {
}
```
+### HKDF
+
+In this example, we encrypt a message `plainText` given a shared secret `secret`, which might itself have been derived using an algorithm such as ECDH. Instead of using the shared secret directly, we use it as key material for the HKDF function, to derive an AES-GCM encryption key, which we then use to encrypt the message. [See the complete code on GitHub.](https://github.com/mdn/dom-examples/blob/main/web-crypto/derive-key/hkdf.js)
+
+```js
+/*
+ Given some key material and some random salt,
+ derive an AES-GCM key using HKDF.
+ */
+function getKey(keyMaterial, salt) {
+ return window.crypto.subtle.deriveKey(
+ {
+ name: "HKDF",
+ salt: salt,
+ info: new Uint8Array("Encryption example"),
+ hash: "SHA-256",
+ },
+ keyMaterial,
+ { name: "AES-GCM", length: 256 },
+ true,
+ ["encrypt", "decrypt"],
+ );
+}
+
+async function encrypt(secret, plainText) {
+ const message = {
+ salt: window.crypto.getRandomValues(new Uint8Array(16)),
+ iv: window.crypto.getRandomValues(new Uint8Array(12)),
+ };
+
+ const key = await getKey(secret, message.salt);
+
+ message.ciphertext = await window.crypto.subtle.encrypt(
+ {
+ name: "AES-GCM",
+ iv: message.iv,
+ },
+ key,
+ plainText,
+ );
+
+ return message;
+}
+```
+
## Specifications
{{Specifications}}
diff --git a/files/en-us/web/api/textmetrics/index.md b/files/en-us/web/api/textmetrics/index.md
index ec7a43963356580..2ebfeedbce25530 100644
--- a/files/en-us/web/api/textmetrics/index.md
+++ b/files/en-us/web/api/textmetrics/index.md
@@ -76,10 +76,15 @@ baselines.forEach((baseline, index) => {
const y = 50 + index * 50;
ctx.beginPath();
ctx.fillText(text, 0, y);
- let lineY = y - Math.abs(textMetrics[baseline]);
- if (baselinesBelowAlphabetic.includes(baseline)) {
- lineY = y + Math.abs(textMetrics[baseline]);
+
+ const baselineMetricValue = textMetrics[baseline];
+ if (baselineMetricValue === undefined) {
+ return;
}
+
+ const lineY = baselinesBelowAlphabetic.includes(baseline)
+ ? y + Math.abs(baselineMetricValue)
+ : y - Math.abs(baselineMetricValue);
ctx.moveTo(0, lineY);
ctx.lineTo(550, lineY);
ctx.stroke();
diff --git a/files/en-us/web/api/treewalker/currentnode/index.md b/files/en-us/web/api/treewalker/currentnode/index.md
index 0921ead7998dcbe..73477308ecf9d76 100644
--- a/files/en-us/web/api/treewalker/currentnode/index.md
+++ b/files/en-us/web/api/treewalker/currentnode/index.md
@@ -26,7 +26,6 @@ const treeWalker = document.createTreeWalker(
return NodeFilter.FILTER_ACCEPT;
},
},
- false,
);
root = treeWalker.currentNode; // the root element as it is the first element!
```
diff --git a/files/en-us/web/api/trustedhtml/index.md b/files/en-us/web/api/trustedhtml/index.md
index fddcbcbfa0acd6b..4a0ffc6081188b8 100644
--- a/files/en-us/web/api/trustedhtml/index.md
+++ b/files/en-us/web/api/trustedhtml/index.md
@@ -30,7 +30,7 @@ The sanitized value can then be used with {{domxref("Element.innerHTML")}} to en
```js
const escapeHTMLPolicy = trustedTypes.createPolicy("myEscapePolicy", {
- createHTML: (string) => string.replace(/>/g, "<"),
+ createHTML: (string) => string.replace(/ **Note:** This method returns the query string without the question
-> mark. This is different from [window.location.search](/en-US/docs/Web/API/HTMLAnchorElement/search),
-> which includes it.
+> **Note:** This method returns the query string without the question mark. This is different from [`Location.search`](/en-US/docs/Web/API/Location/search), [`HTMLAnchorElement.search`](/en-US/docs/Web/API/HTMLAnchorElement/search), and [`URL.search`](/en-US/docs/Web/API/URL/search), which all include the question mark.
{{availableinworkers}}
diff --git a/files/en-us/web/api/usbdevice/vendorid/index.md b/files/en-us/web/api/usbdevice/vendorid/index.md
index bcc752845c56c3e..d07745ec728fd03 100644
--- a/files/en-us/web/api/usbdevice/vendorid/index.md
+++ b/files/en-us/web/api/usbdevice/vendorid/index.md
@@ -11,7 +11,7 @@ browser-compat: api.USBDevice.vendorId
{{SeeCompatTable}}{{APIRef("WebUSB API")}}
The **`vendorId`** read only property of the
-{{domxref("USBDevice")}} interface is the official usg.org-assigned vendor ID.
+{{domxref("USBDevice")}} interface is the official usb.org-assigned vendor ID.
## Value
diff --git a/files/en-us/web/api/videoencoder/configure/index.md b/files/en-us/web/api/videoencoder/configure/index.md
index cdcc9167a401553..8bc62b2c7257624 100644
--- a/files/en-us/web/api/videoencoder/configure/index.md
+++ b/files/en-us/web/api/videoencoder/configure/index.md
@@ -47,14 +47,20 @@ configure(config)
- `"keep"`
- `scalabilityMode`
- : A string containing an encoding scalability mode identifier as defined in [WebRTC](https://w3c.github.io/webrtc-svc/#scalabilitymodes*).
- - `bitrateMode`
+ - `bitrateMode` {{optional_inline}}
- : A string containing a bitrate mode. One of:
- `"constant"`
+ - : The encoder will target constant bitrate.
- `"variable"` (default)
- - `latencyMode`
+ - : The encoder will target a variable bitrate, allowing more space to be used for complex signals and less space for less complex signals.
+ - `"quantizer"`
+ - : The encoder will disregard the `bitrate` option and instead it will use codec-specific quantizer values specified for each frame in the `options` parameter to {{domxref("VideoEncoder.encode()")}}.
+ - `latencyMode` {{optional_inline}}
- : A string containing a value that configures the latency behavior of this codec. One of:
- `"quality"` (default)
+ - : The encoder should optimize for encoding quality.
- `"realtime"`
+ - : The encoder should optimize for low latency and may even drop frames to honor `framerate`.
### Return value
diff --git a/files/en-us/web/api/videoencoder/encode/index.md b/files/en-us/web/api/videoencoder/encode/index.md
index a5ec814fc08a56b..e9ad1a9e2efdb3e 100644
--- a/files/en-us/web/api/videoencoder/encode/index.md
+++ b/files/en-us/web/api/videoencoder/encode/index.md
@@ -23,9 +23,21 @@ encode(frame, options)
- `frame`
- : A {{domxref("VideoFrame")}} object.
- `options` {{optional_inline}}
- - : An object containing the following member:
- - `keyFrame`
+ - : An object containing the following members:
+ - `keyFrame` {{optional_inline}}
- : A {{jsxref("boolean")}}, defaulting to `false` giving the user agent flexibility to decide if this frame should be encoded as a key frame. If `true` this indicates that the given frame must be encoded as a key frame.
+ - `vp9` {{optional_inline}}
+ - : Encode options for the [VP9](/en-US/docs/Web/Media/Formats/Video_codecs#vp9) codec.
+ - `quantizer`
+ - : Frame quantizer value 0 to 63. Only effective if {{domxref("VideoEncoder")}} was configured with `quantizer` bitrate mode.
+ - `av1` {{optional_inline}}
+ - : Encode options for the [AV1](/en-US/docs/Web/Media/Formats/Video_codecs#av1) codec.
+ - `quantizer`
+ - : Frame quantizer value 0 to 63. Only effective if {{domxref("VideoEncoder")}} was configured with `quantizer` bitrate mode.
+ - `avc` {{optional_inline}}
+ - : Encode options for the [AVC (H.264)](/en-US/docs/Web/Media/Formats/Video_codecs#avc_h.264) codec.
+ - `quantizer`
+ - : Frame quantizer value 0 to 51. Only effective if {{domxref("VideoEncoder")}} was configured with `quantizer` bitrate mode.
### Return value
@@ -46,6 +58,34 @@ In the following example `encode` is passed a `VideoFrame`, and the options para
encoder.encode(frame, { keyFrame: true });
```
+Setting per-frame QP value for encoding individual frames.
+
+```js
+const encoder = new VideoEncoder(init);
+const encoderConfig = {
+ codec: "vp09.00.10.08",
+ width: 800,
+ height: 600,
+ bitrateMode: "quantizer",
+ framerate: 30,
+ latencyMode: "realtime",
+};
+encoder.configure(encoderConfig);
+
+const encodeOptions = { keyFrame: false };
+const qp = calculateQp(codec, frame);
+
+if (codec.includes("vp09")) {
+ encodeOptions.vp9 = { quantizer: qp };
+} else if (codec.includes("av01")) {
+ encodeOptions.av1 = { quantizer: qp };
+} else if (codec.includes("avc")) {
+ encodeOptions.avc = { quantizer: qp };
+}
+
+encoder.encode(frame, encodeOptions);
+```
+
## Specifications
{{Specifications}}
diff --git a/files/en-us/web/api/visibilitystateentry/index.md b/files/en-us/web/api/visibilitystateentry/index.md
index 4d8dff3d7ed7bc3..27f6e0f90ab2a7b 100644
--- a/files/en-us/web/api/visibilitystateentry/index.md
+++ b/files/en-us/web/api/visibilitystateentry/index.md
@@ -28,13 +28,13 @@ The performance timeline will always have a "`visibility-state`" entry with a `s
This interface has no properties but it extends the properties of {{domxref("PerformanceEntry")}} by qualifying and constraining them as follows:
-- {{domxref("PerformanceEntry.entryType")}}
+- {{domxref("PerformanceEntry.entryType")}} {{experimental_inline}}
- : Returns "`visibility-state`".
-- {{domxref("PerformanceEntry.name")}}
+- {{domxref("PerformanceEntry.name")}} {{experimental_inline}}
- : Returns either `"visible"` or `"hidden"`.
-- {{domxref("PerformanceEntry.startTime")}}
+- {{domxref("PerformanceEntry.startTime")}} {{experimental_inline}}
- : Returns the {{domxref("DOMHighResTimeStamp","timestamp")}} when the visibility state change occurred.
-- {{domxref("PerformanceEntry.duration")}}
+- {{domxref("PerformanceEntry.duration")}} {{experimental_inline}}
- : Returns 0.
## Instance methods
diff --git a/files/en-us/web/api/web_audio_api/using_audioworklet/index.md b/files/en-us/web/api/web_audio_api/using_audioworklet/index.md
index 196978f57c5a8ef..ef66d812d7b79a3 100644
--- a/files/en-us/web/api/web_audio_api/using_audioworklet/index.md
+++ b/files/en-us/web/api/web_audio_api/using_audioworklet/index.md
@@ -207,7 +207,7 @@ In order to ensure the context is usable, this starts by creating the context if
You can then create a new audio processor node by doing this:
```js
-let newProcessorNode = createMyAudioProcessor();
+let newProcessorNode = await createMyAudioProcessor();
```
If the returned value, `newProcessorNode`, is non-`null`, we have a valid audio context with its hiss processor node in place and ready to use.
diff --git a/files/en-us/web/api/web_authentication_api/index.md b/files/en-us/web/api/web_authentication_api/index.md
index 53dbc8ce313da6c..bedda39c5e49c11 100644
--- a/files/en-us/web/api/web_authentication_api/index.md
+++ b/files/en-us/web/api/web_authentication_api/index.md
@@ -34,7 +34,11 @@ In their most basic forms, both `create()` and `get()` receive a very large rand
To illustrate how the credential creation process works, let's describe the typical flow that occurs when a user wants to register a credential to a relying party:
-1. The relying party server sends user and relying party information to the web app handling the registration process, along with the "challenge", using an appropriate secure mechanism (for example [XMLHttpRequest](/en-US/docs/Web/API/XMLHttpRequest) or [Fetch](/en-US/docs/Web/API/Fetch_API)).
+1. The relying party server sends user and relying party information to the web app handling the registration process, along with the "challenge", using an appropriate secure mechanism (for example [Fetch](/en-US/docs/Web/API/Fetch_API) or [XMLHttpRequest](/en-US/docs/Web/API/XMLHttpRequest)).
+
+ > **Note:** The format for sharing information between the relying party server and the web app is up to the application.
+ > A recommended approach is to exchange {{glossary("JSON type representation")}} objects for credentials and credential options.
+ > Convenience methods have been created in `PublicKeyCredential` for converting from the JSON representations to the form required by the authentication APIs: {{domxref("PublicKeyCredential.parseCreationOptionsFromJSON_static", "parseCreationOptionsFromJSON()")}}, {{domxref("PublicKeyCredential.parseRequestOptionsFromJSON_static", "parseRequestOptionsFromJSON()")}} and {{domxref("PublicKeyCredential.toJSON()")}}.
2. The web app initiates generation of a new credential via the authenticator, on behalf of the relying party, via a {{domxref("CredentialsContainer.create()", "navigator.credentials.create()")}} call. This call is passed a `publicKey` option specifying device capabilities, e.g., whether the device provides its own user authentication (for example with biometrics).
diff --git a/files/en-us/web/api/web_authentication_api/webauthn_extensions/index.md b/files/en-us/web/api/web_authentication_api/webauthn_extensions/index.md
index 9d30e0d06fa524e..8492ec332804cd5 100644
--- a/files/en-us/web/api/web_authentication_api/webauthn_extensions/index.md
+++ b/files/en-us/web/api/web_authentication_api/webauthn_extensions/index.md
@@ -19,7 +19,7 @@ Behind the scenes, the inputs are processed by the user agent and/or the authent
For example, in a `publicKey` object for a `create()` call, we might want to request the use of two extensions:
-1. The `credProps` extension. Relying parties set `credProps` to request that the browser tells the relying party whether the credential is resident/discoverable after registration. This is useful when calling `create()` with `publicKey.authenticatorSelection.residentKey = "preferred"`. To request it, you also need to set `publicKey.extensions.credProps = true` when. The browser makes a credential and, depending on the type of authenticator used, it will be discoverable (for example, the FIDO2 authenticator would typically make it discoverable; FIDO1/U2F security key would be non-discoverable). `credProps` is processed by the user agent only.
+1. The `credProps` extension. Relying parties set `credProps` to request that the browser tells the relying party whether the credential is resident/discoverable after registration. This is useful when calling `create()` with `publicKey.authenticatorSelection.residentKey = "preferred"`. To request it, you also need to set `publicKey.extensions.credProps = true` when the browser makes a credential and, depending on the type of authenticator used, it will be discoverable (for example, the FIDO2 authenticator would typically make it discoverable; FIDO1/U2F security key would be non-discoverable). `credProps` is processed by the user agent only.
2. The `minPinLength` extension allows relying parties to request the authenticator's minimum PIN length. This requires `extensions.minPinLength` to be set to `true`. `minPinLength` is processed by the authenticator, with the user agent only serving to pass the input data along to it.
```js
diff --git a/files/en-us/web/api/web_components/using_custom_elements/index.md b/files/en-us/web/api/web_components/using_custom_elements/index.md
index 7abcd1a7427099c..ed2330b3cfc1bd8 100644
--- a/files/en-us/web/api/web_components/using_custom_elements/index.md
+++ b/files/en-us/web/api/web_components/using_custom_elements/index.md
@@ -6,109 +6,276 @@ page-type: guide
{{DefaultAPISidebar("Web Components")}}
-One of the key features of the Web Components standard is the ability to create custom elements that encapsulate your functionality on an HTML page, rather than having to make do with a long, nested batch of elements that together provide a custom page feature. This article introduces the use of the Custom Elements API.
+One of the key features of web components is the ability to create _custom elements_: that is, HTML elements whose behavior is defined by the web developer, that extend the set of elements available in the browser.
-## High-level view
+This article introduces custom elements, and walks through some examples.
-The controller of custom elements on a web document is the {{domxref("CustomElementRegistry")}} object — this object allows you to register a custom element on the page, return information on what custom elements are registered, etc.
+## Types of custom element
-To register a custom element on the page, you use the {{domxref("CustomElementRegistry.define()")}} method. This takes as its arguments:
+There are two types of custom element:
-- A string representing the name you are giving to the element. Note that custom element names [require a dash to be used in them](https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name) ({{Glossary("kebab_case", "kebab-case")}}); they can't be single words.
-- A [class](/en-US/docs/Web/JavaScript/Reference/Classes) object that defines the behavior of the element.
-- {{optional_inline}} An options object containing an `extends` property, which specifies the built-in element your element inherits from, if any (only relevant to customized built-in elements; see the definition below).
+- **Customized built-in elements** inherit from standard HTML elements such as {{domxref("HTMLImageElement")}} or {{domxref("HTMLParagraphElement")}}. Their implementation customizes the behavior of the standard element.
+- **Autonomous custom elements** inherit from the HTML element base class {{domxref("HTMLElement")}}. You have to implement their behavior from scratch.
-There are two types of custom elements:
+## Implementing a custom element
-- **Autonomous custom elements** are standalone — they don't inherit from standard HTML elements. You use these on a page by literally writing them out as an HTML element. For example ``, or `document.createElement("popup-info")`.
-- **Customized built-in elements** inherit from basic HTML elements. To create one of these, you have to specify which element they extend (as implied in the examples above), and they are used by writing out the basic element but specifying the name of the custom element in the [`is`](/en-US/docs/Web/HTML/Global_attributes/is) attribute (or property). For example ``, or `document.createElement("p", { is: "word-count" })`.
+A custom element is implemented as a [class](/en-US/docs/Web/JavaScript/Reference/Classes) which extends {{domxref("HTMLElement")}} (in the case of automonous elements) or the interface you want to customize (in the case of customized built-in elements).
-So for example, we can define a **customized built-in** [word-count element](https://mdn.github.io/web-components-examples/word-count-web-component/) like this:
+Here's the implementation of a minimal custom element that customizes the {{HTMLElement("p")}} element:
```js
-customElements.define("word-count", WordCount, { extends: "p" });
+class WordCount extends HTMLParagraphElement {
+ constructor() {
+ super();
+ }
+ // Element functionality written in here
+}
+```
+
+Here's the implementation of a minimal autonomous custom element:
+
+```js
+class PopupInfo extends HTMLElement {
+ constructor() {
+ super();
+ }
+ // Element functionality written in here
+}
```
-The element is called `word-count`, its class object is `WordCount`, and it extends the {{htmlelement("p")}} element and, as such, it can only be constructed by setting the `is` attribute in the extended element. For example: `
`, or `document.createElement("p", { is: "word-count" })`.
+In the class [constructor](/en-US/docs/Web/JavaScript/Reference/Classes/constructor), you can set up initial state and default values, register event listeners and perhaps create a shadow root. At this point, you should not inspect the element's attributes or children, or add new attributes or children. See [Requirements for custom element constructors and reactions](https://html.spec.whatwg.org/multipage/custom-elements.html#custom-element-conformance) for the complete set of requirements.
-A custom element's class object is written using the `class` syntax. For example, `WordCount` is structured like so:
+### Custom element lifecycle callbacks
+
+Once your custom element is registered, the browser will call certain methods of your class when code in the page interacts with your custom element in certain ways. By providing an implementation of these methods, which the specification calls _lifecycle callbacks_, you can run code in response to these events.
+
+Custom element lifecycle callbacks include:
+
+- `connectedCallback()`: called each time the element is added to the document. The specification recommends that, as far as possible, developers should implement custom element setup in this callback rather than the constructor.
+- `disconnectedCallback()`: called each time the element is removed from the document.
+- `adoptedCallback()`: called each time the element is moved to a new document.
+- `attributeChangedCallback()`: called when attributes are changed, added, removed, or replaced. See [Responding to attribute changes](#responding_to_attribute_changes) for more details about this callback.
+
+Here's a minimal custom element that logs these lifecycle events:
```js
-class WordCount extends HTMLParagraphElement {
+// Create a class for the element
+class MyCustomElement extends HTMLElement {
+ static observedAttributes = ["color", "size"];
+
constructor() {
// Always call super first in constructor
super();
+ }
- // Element functionality written in here
+ connectedCallback() {
+ console.log("Custom element added to page.");
+ }
+
+ disconnectedCallback() {
+ console.log("Custom element removed from page.");
+ }
+
+ adoptedCallback() {
+ console.log("Custom element moved to new page.");
+ }
+
+ attributeChangedCallback(name, oldValue, newValue) {
+ console.log(`Attribute ${name} has changed.`);
}
}
+
+customElements.define("my-custom-element", MyCustomElement);
+```
+
+## Registering a custom element
+
+To make a custom element available in a page, call the {{domxref("CustomElementRegistry.define()", "define()")}} method of {{domxref("Window.customElements")}}.
+
+The `define()` method takes the following arguments:
+
+- `name`
+ - : The name of the element. This must start with a lowercase letter, contain a hyphen, and satisfy certain other rules listed in the specification's [definition of a valid name](https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name).
+- `constructor`
+ - : The custom element's constructor function.
+- `options`
+ - : Only included for customized built-in elements, this is an object containing a single property `extends`, which is a string naming the built-in element to extend.
+
+For example, this code registers the `WordCount` customized built-in element:
+
+```js
+customElements.define("word-count", WordCount, { extends: "p" });
+```
+
+This code registers the `PopupInfo` autonomous custom element:
+
+```js
+customElements.define("popup-info", PopupInfo);
+```
+
+## Using a custom element
+
+Once you've defined and registered a custom element, you can use it in your code.
+
+To use a customized built-in element, use the built-in element but with the custom name as the value of the [`is`](/en-US/docs/Web/HTML/Global_attributes/is) attribute:
+
+```html
+
```
-> **Note:** Find the [full JavaScript source](https://github.com/mdn/web-components-examples/blob/main/word-count-web-component/main.js) here.
+To use an autonomous custom element, use the custom name just like a built-in HTML element:
-This is just a simple example, but there is more you can do here. It is possible to define specific lifecycle callbacks inside the class, which run at specific points in the element's lifecycle. For example, `connectedCallback` is invoked each time the custom element is appended into a document-connected element, while `attributeChangedCallback` is invoked when one of the custom element's attributes is added, removed, or changed.
+```html
+
+
+
+```
-You'll learn more about these in the [Using the lifecycle callbacks](#using_the_lifecycle_callbacks) section below.
+## Responding to attribute changes
-## Working through some simple examples
+Like built-in elements, custom elements can use HTML attributes to configure the element's behavior. To use attributes effectively, an element has to be able to respond to changes in an attribute's value. To do this, a custom element needs to add the following members to the class that implements the custom element:
-At this point, let's go through some more simple examples to show you how custom elements are created in more detail.
+- A static property named `observedAttributes`. This must be an array containing the names of all attributes for which the element needs change notifications.
+- An implementation of the `attributeChangedCallback()` lifecycle callback.
-### Autonomous custom elements
+The `attributeChangedCallback()` callback is then called whenever an attribute whose name is listed in the element's `observedAttributes` property is added, modified, removed, or replaced.
-Let's have a look at an example of an autonomous custom element — [``](https://github.com/mdn/web-components-examples/tree/main/popup-info-box-web-component) (see a [live example](https://mdn.github.io/web-components-examples/popup-info-box-web-component/)). This takes an image icon and a text string, and embeds the icon into the page. When the icon is focused, it displays the text in a pop up information box to provide further in-context information.
+The callback is passed three arguments:
-To begin with, the JavaScript file defines a class called `PopupInfo`, which extends the generic {{domxref("HTMLElement")}} class.
+- The name of the attribute which changed.
+- The attribute's old value.
+- The attribute's new value.
+
+For example, this autonomous element will observe a `size` attribute, and log the old and new values when they change:
```js
-class PopupInfo extends HTMLElement {
+// Create a class for the element
+class MyCustomElement extends HTMLElement {
+ static observedAttributes = ["size"];
+
constructor() {
- // Always call super first in constructor
super();
+ }
- // write element functionality in here
+ attributeChangedCallback(name, oldValue, newValue) {
+ console.log(
+ `Attribute ${name} has changed from ${oldValue} to ${newValue}.`,
+ );
}
}
+
+customElements.define("my-custom-element", MyCustomElement);
+```
+
+Note that if the element's HTML declaration includes an observed attribute, then `attributeChangedCallback()` will be called after the attribute is initialized, when the element's declaration is parsed for the first time. So in the following example, `attributeChangedCallback()` will be called when the DOM is parsed, even if the attribute is never changed again:
+
+```html
+
```
-The preceding code snippet contains the [`constructor()`](/en-US/docs/Web/JavaScript/Reference/Classes/constructor) definition for the class, which always starts by calling [`super()`](/en-US/docs/Web/JavaScript/Reference/Operators/super) so that the correct prototype chain is established.
+For a complete example showing the use of `attributeChangedCallback()`, see [Lifecycle callbacks](#lifecycle_callbacks) in this page.
+
+## Examples
+
+In the rest of this guide we'll look at a few example custom elements. You can find the source for all these examples, and more, in the [web-components-examples](https://github.com/mdn/web-components-examples) repository, and you can see them all live at .
+
+### An autonomous custom element
-Inside the method connectedCallback, we define all the functionality the element will have when the element is connected to the DOM. In this case we attach a shadow root to the custom element, use some DOM manipulation to create the element's internal shadow DOM structure — which is then attached to the shadow root — and finally attach some CSS to the shadow root to style it. We don't use `constructor()` because an element's attributes are unavailable until connected to the DOM.
+First, we'll look at an autonomous custom element. The `` custom element takes an image icon and a text string as attributes, and embeds the icon into the page. When the icon is focused, it displays the text in a pop up information box to provide further in-context information.
+
+- [See the example running live](https://mdn.github.io/web-components-examples/popup-info-box-web-component)
+- [See the source code](https://github.com/mdn/web-components-examples/tree/main/popup-info-box-web-component)
+
+To begin with, the JavaScript file defines a class called `PopupInfo`, which extends the {{domxref("HTMLElement")}} class.
```js
-// Create a shadow root
-this.attachShadow({ mode: "open" }); // sets and returns 'this.shadowRoot'
-
-// Create (nested) span elements
-const wrapper = document.createElement("span");
-wrapper.setAttribute("class", "wrapper");
-const icon = wrapper.appendChild(document.createElement("span"));
-icon.setAttribute("class", "icon");
-icon.setAttribute("tabindex", 0);
-// Insert icon from defined attribute or default icon
-const img = icon.appendChild(document.createElement("img"));
-img.src = this.hasAttribute("img")
- ? this.getAttribute("img")
- : "img/default.png";
-// Always include descriptive text when adding an image
-img.alt = this.hasAttribute("alt") ? this.getAttribute("alt") : "";
-
-const info = wrapper.appendChild(document.createElement("span"));
-info.setAttribute("class", "info");
-// Take attribute content and put it inside the info span
-info.textContent = this.getAttribute("data-text");
-
-// Create some CSS to apply to the shadow DOM
-const style = document.createElement("style");
-style.textContent = `.wrapper {
- /* CSS truncated for brevity */
-}`;
-
-// attach the created elements to the shadow DOM
-this.shadowRoot.append(style, wrapper);
+// Create a class for the element
+class PopupInfo extends HTMLElement {
+ constructor() {
+ // Always call super first in constructor
+ super();
+ }
+
+ connectedCallback() {
+ // Create a shadow root
+ const shadow = this.attachShadow({ mode: "open" });
+
+ // Create spans
+ const wrapper = document.createElement("span");
+ wrapper.setAttribute("class", "wrapper");
+
+ const icon = document.createElement("span");
+ icon.setAttribute("class", "icon");
+ icon.setAttribute("tabindex", 0);
+
+ const info = document.createElement("span");
+ info.setAttribute("class", "info");
+
+ // Take attribute content and put it inside the info span
+ const text = this.getAttribute("data-text");
+ info.textContent = text;
+
+ // Insert icon
+ let imgUrl;
+ if (this.hasAttribute("img")) {
+ imgUrl = this.getAttribute("img");
+ } else {
+ imgUrl = "img/default.png";
+ }
+
+ const img = document.createElement("img");
+ img.src = imgUrl;
+ icon.appendChild(img);
+
+ // Create some CSS to apply to the shadow dom
+ const style = document.createElement("style");
+ console.log(style.isConnected);
+
+ style.textContent = `
+ .wrapper {
+ position: relative;
+ }
+
+ .info {
+ font-size: 0.8rem;
+ width: 200px;
+ display: inline-block;
+ border: 1px solid black;
+ padding: 10px;
+ background: white;
+ border-radius: 10px;
+ opacity: 0;
+ transition: 0.6s all;
+ position: absolute;
+ bottom: 20px;
+ left: 10px;
+ z-index: 3;
+ }
+
+ img {
+ width: 1.2rem;
+ }
+
+ .icon:hover + .info, .icon:focus + .info {
+ opacity: 1;
+ }
+ `;
+
+ // Attach the created elements to the shadow dom
+ shadow.appendChild(style);
+ console.log(style.isConnected);
+ shadow.appendChild(wrapper);
+ wrapper.appendChild(icon);
+ wrapper.appendChild(info);
+ }
+}
```
-Finally, we register our custom element on the `CustomElementRegistry` using the `define()` method we mentioned earlier — in the parameters we specify the element name, and then the class name that defines its functionality:
+The class definition contains the [`constructor()`](/en-US/docs/Web/JavaScript/Reference/Classes/constructor) for the class, which always starts by calling [`super()`](/en-US/docs/Web/JavaScript/Reference/Operators/super) so that the correct prototype chain is established.
+
+Inside the method `connectedCallback()`, we define all the functionality the element will have when the element is connected to the DOM. In this case we attach a shadow root to the custom element, use some DOM manipulation to create the element's internal shadow DOM structure — which is then attached to the shadow root — and finally attach some CSS to the shadow root to style it. We don't do this work in the constructor because an element's attributes are unavailable until it is connected to the DOM.
+
+Finally, we register our custom element in the `CustomElementRegistry` using the `define()` method we mentioned earlier — in the parameters we specify the element name, and then the class name that defines its functionality:
```js
customElements.define("popup-info", PopupInfo);
@@ -124,46 +291,146 @@ It is now available to use on our page. Over in our HTML, we use it like so:
back of your card.">
```
-> **Note:** You can see the [full JavaScript source](https://github.com/mdn/web-components-examples/blob/main/popup-info-box-web-component/main.js) code here.
+### Referencing external styles
-### Internal vs. external styles
+In the above example we apply styles to the shadow DOM using a {{htmlelement("style")}} element, but you can reference an external stylesheet from a {{htmlelement("link")}} element instead. In this example we'll modify the `` custom element to use an external stylesheet.
-In the above example we apply style to the Shadow DOM using a {{htmlelement("style")}} element, but it is perfectly possible to do it by referencing an external stylesheet from a {{htmlelement("link")}} element instead.
+- [See the example running live](https://mdn.github.io/web-components-examples/popup-info-box-external-stylesheet/)
+- [See the source code](https://github.com/mdn/web-components-examples/blob/main/popup-info-box-external-stylesheet/)
-For example, take a look at this code from our [popup-info-box-external-stylesheet](https://mdn.github.io/web-components-examples/popup-info-box-external-stylesheet/) example (see the [source code](https://github.com/mdn/web-components-examples/blob/main/popup-info-box-external-stylesheet/main.js)):
+Here's the class definition:
```js
-// Apply external styles to the shadow DOM
-const linkElem = document.createElement("link");
-linkElem.setAttribute("rel", "stylesheet");
-linkElem.setAttribute("href", "style.css");
+// Create a class for the element
+class PopupInfo extends HTMLElement {
+ constructor() {
+ // Always call super first in constructor
+ super();
+ }
+
+ connectedCallback() {
+ // Create a shadow root
+ const shadow = this.attachShadow({ mode: "open" });
+
+ // Create spans
+ const wrapper = document.createElement("span");
+ wrapper.setAttribute("class", "wrapper");
-// Attach the created element to the shadow DOM
-shadow.appendChild(linkElem);
+ const icon = document.createElement("span");
+ icon.setAttribute("class", "icon");
+ icon.setAttribute("tabindex", 0);
+
+ const info = document.createElement("span");
+ info.setAttribute("class", "info");
+
+ // Take attribute content and put it inside the info span
+ const text = this.getAttribute("data-text");
+ info.textContent = text;
+
+ // Insert icon
+ let imgUrl;
+ if (this.hasAttribute("img")) {
+ imgUrl = this.getAttribute("img");
+ } else {
+ imgUrl = "img/default.png";
+ }
+
+ const img = document.createElement("img");
+ img.src = imgUrl;
+ icon.appendChild(img);
+
+ // Apply external styles to the shadow dom
+ const linkElem = document.createElement("link");
+ linkElem.setAttribute("rel", "stylesheet");
+ linkElem.setAttribute("href", "style.css");
+
+ // Attach the created elements to the shadow dom
+ shadow.appendChild(linkElem);
+ shadow.appendChild(wrapper);
+ wrapper.appendChild(icon);
+ wrapper.appendChild(info);
+ }
+}
```
+It's just like the original `` example, except that we link to an external stylesheet using a {{HTMLElement("link")}} element, which we add to the shadow DOM.
+
Note that {{htmlelement("link")}} elements do not block paint of the shadow root, so there may be a flash of unstyled content (FOUC) while the stylesheet loads.
Many modern browsers implement an optimization for {{htmlelement("style")}} tags either cloned from a common node or that have identical text, to allow them to share a single backing stylesheet. With this optimization the performance of external and internal styles should be similar.
### Customized built-in elements
-Now let's have a look at another customized built in element example — [expanding-list](https://github.com/mdn/web-components-examples/tree/main/expanding-list-web-component) ([see it live also](https://mdn.github.io/web-components-examples/expanding-list-web-component/)). This turns any unordered list into an expanding/collapsing menu.
+Now let's have a look at a customized built in element example. This example extends the built-in {{HTMLElement("ul")}} element to support expanding and collapsing the list items.
-First of all, we define our element's class, in the same manner as before:
+- [See the example running live](https://mdn.github.io/web-components-examples/expanding-list-web-component/)
+- [See the source code](https://github.com/mdn/web-components-examples/tree/main/expanding-list-web-component)
+
+First of all, we define our element's class:
```js
+// Create a class for the element
class ExpandingList extends HTMLUListElement {
constructor() {
// Always call super first in constructor
- super();
+ // Return value from super() is a reference to this element
+ self = super();
+ }
- // write element functionality in here
+ connectedCallback() {
+ // Get ul and li elements that are a child of this custom ul element
+ // li elements can be containers if they have uls within them
+ const uls = Array.from(self.querySelectorAll("ul"));
+ const lis = Array.from(self.querySelectorAll("li"));
+ // Hide all child uls
+ // These lists will be shown when the user clicks a higher level container
+ uls.forEach((ul) => {
+ ul.style.display = "none";
+ });
+
+ // Look through each li element in the ul
+ lis.forEach((li) => {
+ // If this li has a ul as a child, decorate it and add a click handler
+ if (li.querySelectorAll("ul").length > 0) {
+ // Add an attribute which can be used by the style
+ // to show an open or closed icon
+ li.setAttribute("class", "closed");
+
+ // Wrap the li element's text in a new span element
+ // so we can assign style and event handlers to the span
+ const childText = li.childNodes[0];
+ const newSpan = document.createElement("span");
+
+ // Copy text from li to span, set cursor style
+ newSpan.textContent = childText.textContent;
+ newSpan.style.cursor = "pointer";
+
+ // Add click handler to this span
+ newSpan.addEventListener("click", (e) => {
+ // next sibling to the span should be the ul
+ const nextul = e.target.nextElementSibling;
+
+ // Toggle visible state and update class attribute on ul
+ if (nextul.style.display == "block") {
+ nextul.style.display = "none";
+ nextul.parentNode.setAttribute("class", "closed");
+ } else {
+ nextul.style.display = "block";
+ nextul.parentNode.setAttribute("class", "open");
+ }
+ });
+ // Add the span and remove the bare text node from the li
+ childText.parentNode.insertBefore(newSpan, childText);
+ childText.parentNode.removeChild(childText);
+ }
+ });
}
}
```
-We will not explain the element functionality in any detail here, but you can discover how it works by checking out the source code. The only real difference here is that our element is extending the {{domxref("HTMLUListElement")}} interface, and not {{domxref("HTMLElement")}}. So it has all the characteristics of a {{htmlelement("ul")}} element with the functionality we define built on top, rather than being a standalone element. This is what makes it a customized built-in, rather than an autonomous element.
+Note that this time we extend {{domxref("HTMLUListElement")}}, rather than {{domxref("HTMLElement")}}. This means that we get the default behavior of a list, and only have to implement our own customizations.
+
+As before, most of the code is in the `connectedCallback()` lifecycle callback.
Next, we register the element using the `define()` method as before, except that this time it also includes an options object that details what element our custom element inherits from:
@@ -181,35 +448,33 @@ Using the built-in element in a web document also looks somewhat different:
You use a `` element as normal, but specify the name of the custom element inside the `is` attribute.
-> **Note:** Again, you can see the full [JavaScript source code](https://github.com/mdn/web-components-examples/blob/main/expanding-list-web-component/main.js) here.
-
-## Using the lifecycle callbacks
+Note that in this case we must ensure that the script defining our custom element is executed after the DOM has been fully parsed, because `connectedCallback()` is called as soon as the expanding list is added to the DOM, and at that point its children have not been added yet, so the `querySelectorAll()` calls will not find any items. One way to ensure this is to add the [defer](/en-US/docs/Web/HTML/Element/script#defer) attribute to the line that includes the script:
-You can define several different callbacks inside a custom element's class definition, which fire at different points in the element's lifecycle:
-
-- `connectedCallback`: Invoked each time the custom element is appended into a document-connected element. This will happen each time the node is moved, and may happen before the element's contents have been fully parsed.
-
- > **Note:** `connectedCallback` may be called once your element is no longer connected, use {{domxref("Node.isConnected")}} to make sure.
+```html
+
+```
-- `disconnectedCallback`: Invoked each time the custom element is disconnected from the document's DOM.
-- `adoptedCallback`: Invoked each time the custom element is moved to a new document.
-- `attributeChangedCallback`: Invoked each time one of the custom element's attributes is added, removed, or changed. Which attributes to notice change for is specified in a static get `observedAttributes` method
+### Lifecycle callbacks
-Let's look at an example of these in use. The code below is taken from the [life-cycle-callbacks](https://github.com/mdn/web-components-examples/tree/main/life-cycle-callbacks) example ([see it running live](https://mdn.github.io/web-components-examples/life-cycle-callbacks/)). This is a trivial example that generates a colored square of a fixed size on the page. The custom element looks like this:
+So far we've seen only one lifecycle callback in action: `connectedCallback()`. In the final example, ``, we'll see some of the others. The `` autonomous custom element draws a square whose size and color are determined by two attributes, named `"size"` and `"color"`.
-```html
-
-```
+- [See the example running live](https://mdn.github.io/web-components-examples/life-cycle-callbacks/)
+- [See the source code](https://github.com/mdn/web-components-examples/tree/main/life-cycle-callbacks)
-The class constructor is really simple — here we attach a shadow DOM to the element, then attach empty {{htmlelement("div")}} and {{htmlelement("style")}} elements to the shadow root:
+In the class constructor, we attach a shadow DOM to the element, then attach empty {{htmlelement("div")}} and {{htmlelement("style")}} elements to the shadow root:
```js
-const shadow = this.attachShadow({ mode: "open" });
+constructor() {
+ // Always call super first in constructor
+ super();
-const div = document.createElement("div");
-const style = document.createElement("style");
-shadow.appendChild(style);
-shadow.appendChild(div);
+ const shadow = this.attachShadow({ mode: "open" });
+
+ const div = document.createElement("div");
+ const style = document.createElement("style");
+ shadow.appendChild(style);
+ shadow.appendChild(div);
+}
```
The key function in this example is `updateStyle()` — this takes an element, gets its shadow root, finds its `
+ I'm in the shadow DOM
+
+
+
+I'm not in the shadow DOM
```
-### Internal versus external styles
+In the JavaScript, we will create the shadow DOM and add the content of the `` to it:
+
+```js
+const host = document.querySelector("#host");
+const shadow = host.attachShadow({ mode: "open" });
+const template = document.getElementById("my-element");
-In the above example we apply style to the Shadow DOM using a {{htmlelement("style")}} element, but it is perfectly possible to do it by referencing an external stylesheet from a {{htmlelement("link")}} element instead.
+shadow.appendChild(template.content);
+```
-For example, take a look at this code from our [popup-info-box-external-stylesheet](https://mdn.github.io/web-components-examples/popup-info-box-external-stylesheet/) example (see the [source code](https://github.com/mdn/web-components-examples/blob/main/popup-info-box-external-stylesheet/main.js)):
+Again, the styles defined in the `` are applied only within the shadow DOM tree, and not in the rest of the page:
+
+{{EmbedLiveSample("adding_style_elements_in_template_declarations")}}
+
+### Choosing between programmatic and declarative options
+
+Which of these options to use is dependent on your application and personal preference.
+
+Creating a `CSSStyleSheet` and assigning it to the shadow root using `adoptedStyleSheets` allows you to create a single stylesheet and share it among many DOM trees. For example, a component library could create a single stylesheet and then share it among all the custom elements belonging to that library. The browser will parse that stylesheet once. Also, you can make dynamic changes to the stylesheet and have them propagate to all components that use the sheet.
+
+The approach of attaching a `1 0.5 0 0.5 1 Output progress Input progress step-start step-start
+1 0.5 0 0.5 1 Output progress Input progress step-start step-end
diff --git a/files/en-us/web/css/filter-function/drop-shadow/index.md b/files/en-us/web/css/filter-function/drop-shadow/index.md
index 7159f8c8aad6d03..495ac7f1c7ebee0 100644
--- a/files/en-us/web/css/filter-function/drop-shadow/index.md
+++ b/files/en-us/web/css/filter-function/drop-shadow/index.md
@@ -18,38 +18,87 @@ A drop shadow is effectively a blurred, offset version of the input image's alph
## Syntax
```css
-drop-shadow(offset-x offset-y blur-radius color)
+/* Two length values */
+/* drop-shadow( ) */
+drop-shadow(5px 5px)
+
+/* Three length values */
+/* drop-shadow( ) */
+drop-shadow(5px 5px 15px)
+
+/* Two length values and a color */
+/* drop-shadow( ) */
+drop-shadow(5px 5px red)
+
+/* Three length values and a color */
+/* drop-shadow( ) */
+drop-shadow(5px 5px 15px red)
+
+/* The order of color and length values can be changed */
+/* drop-shadow( ) */
+drop-shadow(#e23 0.5rem 0.5rem 1rem)
```
The `drop-shadow()` function accepts a parameter of type `` (defined in the {{cssxref("box-shadow")}} property), with the exception that the `inset` keyword and `spread` parameters are not allowed.
### Parameters
-- `offset-x` (required)
- - : The horizontal offset for the shadow, specified as a {{cssxref("<length>")}} value. Negative values place the shadow to the left of the element.
-- `offset-y` (required)
- - : The vertical offset for the shadow, specified as a {{cssxref("<length>")}} value. Negative values place the shadow above the element.
-- `blur-radius` (optional)
- - : The shadow's blur radius, specified as a {{cssxref("<length>")}}. The larger the value, the larger and more blurred the shadow becomes. If unspecified, it defaults to `0`, resulting in a sharp, unblurred edge. Negative values are not allowed.
-- `color` (optional)
- - : The color of the shadow, specified as a {{cssxref("<color>")}}. If unspecified, the value of the {{cssxref("color")}} property is used.
+- `` {{optional_inline}}
+
+ - : Specifies the color for the shadow. If not specified, the value of the {{cssxref("color")}} property defined in the parent element is used.
+
+- ``
+ - : Specifies the offset length of the shadow. This parameter accepts two or three values. If two values are specified, they are interpreted as `` (horizontal offset) and `` (vertical offset) values. Negative `` value places the shadow to the left of the element. Negative `` value places the shadow above the element. If not specified, the value of `0` is used for the missing length. If a third value is specified, it is interpreted as ``, which is the value of the standard deviation to the [Gaussian blur](https://en.wikipedia.org/wiki/Gaussian_blur) function. A larger `` value creates a larger and more blurred shadow. Negative values for `` are not allowed.
+
+## Formal syntax
+
+{{CSSSyntax}}
## Examples
-### Setting a drop shadow using pixel offsets and blur radius
+### Setting a drop shadow
-```css
-/* Black shadow with 10px blur */
-drop-shadow(16px 16px 10px black)
+```html
+drop-shadow(16px 16px)
+drop-shadow(16px 16px red)
+drop-shadow(red 1rem 1rem 10px)
+drop-shadow(-16px -16px red)
```
-### Setting a drop shadow using rem offsets and blur radius
-
```css
-/* Reddish shadow with 1rem blur */
-drop-shadow(.5rem .5rem 1rem #e23)
+div {
+ display: inline-block;
+ margin: 0 0.5rem 2rem 1rem;
+ padding: 0.5rem;
+ height: 100px;
+ width: 190px;
+ vertical-align: top;
+ background-color: #222;
+
+ color: lime;
+}
+
+div:nth-child(1) {
+ filter: drop-shadow(16px 16px);
+}
+
+div:nth-child(2) {
+ filter: drop-shadow(16px 16px red);
+}
+
+div:nth-child(3) {
+ filter: drop-shadow(red 1rem 1rem 10px);
+}
+
+div:nth-child(4) {
+ filter: drop-shadow(-16px -6px red);
+}
```
+{{EmbedLiveSample("Setting a drop shadow", "100%", "300px")}}
+
+In the absence of a `` value in the `drop-shadow()` function in the first box, the shadow uses the value of the `color` property from the element (`lime`). The second and third shadows illustrate that the length and color values can be specified in any order. The third shadow shows the blurring effect when a third `` value is specified. The fourth shadow uses negative offsets which shifts shadow to the left and top.
+
## Specifications
{{Specifications}}
diff --git a/files/en-us/web/css/font-feature-settings/index.md b/files/en-us/web/css/font-feature-settings/index.md
index 933ea89e92757ed..320bfbe6a5cc5ab 100644
--- a/files/en-us/web/css/font-feature-settings/index.md
+++ b/files/en-us/web/css/font-feature-settings/index.md
@@ -129,4 +129,5 @@ td.tabular {
- {{cssxref("@font-face/font-variation-settings", "font-variation-settings")}}
- {{cssxref("@font-face/src", "src")}}
- {{cssxref("@font-face/unicode-range", "unicode-range")}}
-- [OpenType Feature Tags](https://docs.microsoft.com/typography/opentype/spec/featurelist) list
+- [OpenType feature tags](https://docs.microsoft.com/typography/opentype/spec/featurelist) list
+- [OpenType features in CSS](https://sparanoid.com/lab/opentype-features/)
diff --git a/files/en-us/web/css/font-size-adjust/index.md b/files/en-us/web/css/font-size-adjust/index.md
index cc09d75e07b5b5e..d9cc68a47a541fb 100644
--- a/files/en-us/web/css/font-size-adjust/index.md
+++ b/files/en-us/web/css/font-size-adjust/index.md
@@ -7,31 +7,23 @@ browser-compat: css.properties.font-size-adjust
{{CSSRef}}
-The **`font-size-adjust`** [CSS](/en-US/docs/Web/CSS) property sets the size of lower-case letters relative to the current font size (which defines the size of upper-case letters).
+The **`font-size-adjust`** [CSS](/en-US/docs/Web/CSS) property provides a way to modify the size of lowercase letters relative to the size of uppercase letters, which defines the overall {{cssxref("font-size")}}. This property is useful for situations where font fallback can occur.
-The property is useful since the legibility of fonts, especially at small sizes, is determined more by the size of lowercase letters than by the size of capital letters. Legibility can become an issue when the first-choice {{ Cssxref("font-family") }} is unavailable and its replacement has a significantly different aspect ratio (the ratio of the size of lowercase letters to the size of the font).
-
-To use this property in a way that is compatible with browsers that do not support `font-size-adjust`, it is specified as a number that the {{ Cssxref("font-size") }} property is multiplied by. This means the value specified for the property should generally be the aspect ratio of the first choice font. For example, consider this style sheet:
-
-```css
-font-size: 14px;
-font-size-adjust: 0.5;
-```
-
-It is really specifying that the lowercase letters of the font should be `7px` high (0.5 × 14px). This will still produce reasonable results in browsers that do not support `font-size-adjust`, where a `14px` font will be used.
+Legibility can become an issue when the first-choice {{ Cssxref("font-family") }} is unavailable and its replacement fallback font has a significantly different aspect value (height of lowercase letters divided by font size). Legibility of fonts, especially at small font sizes, is determined more by the size of lowercase letters than by the size of uppercase letters. The `font-size-adjust` property is useful for adjusting the font size of fallback fonts to keep the aspect value across fonts consistent, ensuring that the text appears similar regardless of the font used.
## Syntax
```css
-/* Use the specified font size */
+/* Keyword */
font-size-adjust: none;
-/* Use a font size that makes lowercase
- letters half the specified font size */
+/* One value: or from-font */
font-size-adjust: 0.5;
+font-size-adjust: from-font;
-/* Two values - added in the Level 5 spec */
+/* Two values */
font-size-adjust: ex-height 0.5;
+font-size-adjust: ch-width from-font;
/* Global values */
font-size-adjust: inherit;
@@ -43,30 +35,53 @@ font-size-adjust: unset;
### Values
+The `font-size-adjust` property takes as its value the keyword `none`, one (`` or `from-font`), or two (`` and either `` or `from-font`) values.
+
- `none`
- - : Choose the size of the font based only on the {{ Cssxref("font-size") }} property.
-- ex-height | cap-height | ch-width | ic-width | ic-height
+ - : No adjustment is applied to the `font-size` value for the fallback font.
+- `` {{optional_inline}}
- - : Specifies the font metric to normalize on. Defaults to `ex-height`. One of:
+ - : Specifies the first-choice font metric to use for adjusting the font size of the fallback font. This parameter accepts one of the keywords listed below. It is an optional parameter, and `ex-height` is used if no `` is specified.
- `ex-height`
- - : Normalize the aspect value of the fonts, using the x-height divided by the font size.
+ - : Uses the ratio of x-height (height of lowercase "x" in a font) to font size (aspect value) to adjust the fallback font size. This keyword value is used to normalize lowercase letters across fonts.
- `cap-height`
- - : Normalize the cap-height of the fonts, using the cap-height by the font size.
+ - : Uses the ratio of cap-height (height of uppercase letters) to font size to adjust fallback font size. This keyword value is used to normalize uppercase letters across fonts.
- `ch-width`
- - : Normalize the horizontal narrow pitch of the fonts, using the advance width of "0" (ZERO, U+0030) divided by the font size.
+ - : Uses the ratio of the advance width (horizontal space taken up by a character in a font) of the character "0" (ZERO, U+0030) to font size. This keyword value is used to normalize horizontal narrow pitch of fonts.
- `ic-width`
- - : Normalize the horizontal wide pitch of the font, using the advance width of "水" (CJK water ideograph, U+6C34) divided by the font size.
+ - : Uses the ratio of the advance width of the character "水" (CJK water ideograph, U+6C34) to font size. This keyword value is used to normalize horizontal wide pitch of fonts, particularly those that include CJK (Chinese, Japanese, Korean) characters.
- `ic-height`
- - : Normalize the vertical wide pitch of the font, using the advance height of "水" (CJK water ideograph, U+6C34) divided by the font size.
+ - : Uses the ratio of the advance height (vertical space taken up by a character in a font) of the character "水" (CJK water ideograph, U+6C34) to font size. This keyword value is used to normalize vertical wide pitch of fonts, particularly those that include CJK characters.
- {{cssxref("<number>")}}
- - : Choose the size of the font so that its lowercase letters (as determined by the x-height of the font) are the specified number times the {{ Cssxref("font-size") }}.
+ - : Adjusts the font size used depending on the specified ``. When no `` is specified (in which case the default value `ex-height` is used), the `` value adjusts the font size of the fallback font so that its x-height is the specified multiple of the font size. This value should generally match the aspect value (ratio of x-height to font size) of the first-choice font. This means that the first-choice font, when available, will display consistently across browsers, regardless of their support for `font-size-adjust`.
+
+ When a `` value is specified, the `` value adjusts the font size as per the chosen `` to maintain a consistent appearance for the specified font metric across different fonts.
+
+ The `` value accepts any number from `0` to infinity. `0` yields text of zero height (that is, the text is hidden). Negative values are invalid.
+
+- `from-font`
+ - : Uses the `` value for the specified `` from the first available font.
+
+## Description
+
+To ensure compatibility with browsers that don't support `font-size-adjust`, this property is specified as a numeric multiplier of the {{cssxref("font-size")}} property. This number should generally match the aspect value of the first-choice font.
+
+> **Note:** If the specified `` has been overridden in [`@font-face`](/en-US/docs/Web/CSS/@font-face), e.g., by using the [`size-adjust`](/en-US/docs/Web/CSS/@font-face/size-adjust) descriptor, then the overridden metric will be used in the `font-size-adjust` calculation. This means that when `font-size-adjust` and `size-adjust` are applied together, `size-adjust` does not have any effect.
+
+The adjusted font size is calculated using the formula `u = ( m / m′ ) s`, where:
- The number specified should generally be the aspect ratio (ratio of x-height to font size) of the first choice {{ Cssxref("font-family") }}. This means that the first-choice font, when available, will appear the same size in browsers, whether or not they support `font-size-adjust`.
+- `m` is the ratio of the specified `` to the first-choice font size.
- `0` yields text of zero height (hidden text).
+- `m′` is the ratio of the corresponding `` to the fallback font size.
+
+- `s` is the value of the `font-size` property.
+
+- `u` is the new, adjusted font size for the fallback font.
+
+Consider this example to see how the adjusted font size is calculated. A first-choice font has a `font-size` of `12px` (`s`), and the ratio of `cap-height` to font size is `0.20` (`m`). The `cap-height` to font size ratio in the fallback font is `0.15` (`m′`). The `font-size-adjust` value has been specified as `cap-height 0.20`. If the primary font is unavailable, the adjusted font size of the fallback font will be calculated to be `16px` (`(0.20 / 0.15) * 12`). This will ensure that the `cap-height` of the fallback font is similar to that of the first-choice font when displayed.
## Formal definition
@@ -78,47 +93,127 @@ font-size-adjust: unset;
## Examples
-### Adjusting lower-case letter sizes
+### Normalizing font size by lowercase and uppercase letters
+
+This example demonstrates how the `font-size-adjust` property can be used to retain the same aspect value across fonts. The Verdana font has a relatively high aspect value of `0.545`, which means that the lowercase letters are relatively tall compared to uppercase letters. This makes the text in small font sizes appear legible. However, the Times font has a lower aspect value of `0.447`, so the text is less legible at small sizes. If Verdana is the first-choice font and Times is the fallback font, specifying the `font-size-adjust` property can help to retain the same aspect value in Times. So if the font falls back to Times, the text will maintain a similar level of legibility as it would have with Verdana.
-#### HTML
+Similarly, the cap-height to font size ratio in Verdana is `0.73` and that in Times is `0.66`. When `font-size-adjust` property is applied to Times to adjust its uppercase letters to match the ratio in Verdana, the Times font displays in adjusted font size ((0.73 / 0.66) \* 14) `15.48px`.
```html
+
+ A: This text uses the Verdana font (14px), which has relatively large
+ lowercase letters.
+
- This text uses the Times font (10px), which is hard to read in small sizes.
+ B: This text uses the Times font (14px), which is hard to read in small sizes.
-
- This text uses the Verdana font (10px), which has relatively large lowercase
- letters.
+
+ C: This text in 14px Times font is adjusted to the same aspect value as the
+ Verdana font, so lowercase letters are normalized across the two fonts.
-
- This is the 10px Times, but now adjusted to the same aspect ratio as the
- Verdana.
+
+ D: This text in 14px Times font is adjusted to the same cap-height to font
+ size ratio as the Verdana font, so uppercase letters are normalized across the
+ two fonts.
```
-#### CSS
-
```css
.times {
font-family: Times, serif;
- font-size: 10px;
+ font-size: 14px;
}
.verdana {
font-family: Verdana, sans-serif;
- font-size: 10px;
+ font-size: 14px;
}
-.adjtimes {
- font-family: Times, serif;
- font-size-adjust: ex-height 0.58;
- font-size: 10px;
+.adjtimesexheight {
+ font-size-adjust: 0.545;
+}
+
+.adjtimescapheight {
+ font-size-adjust: cap-height 0.73;
+}
+```
+
+{{ EmbedLiveSample('Normalizing font size by lowercase and uppercase letters', 500, 200) }}
+
+Without `font-size-adjust` in `B`, the switch from Verdana font to Times font could result in a noticeable decrease in legibility due to its lower aspect value.
+In `C`, notice that only one value is specified for the `font-size-adjust` property, so the default `` value `ex-height` is used. `D` shows how the font would look compared to `A` if its uppercase letter height is adjusted.
+
+### Determining the aspect value of a font
+
+For a given font, the same content in two side-by-side [``](/en-US/docs/Web/HTML/Element/span) elements can be used to determine the font's aspect value. If the same font size is used for content in both spans, the spans will match when the `font-size-adjust` value in one span is accurate for the given font.
+
+In the example below, there are three pairs of side-by-side `` elements, each containing the letter "b". The goal is to adjust the `font-size-adjust` property for the right `` in each pair until the borders around the two letters align. The resulting `font-size-adjust` value can be considered the aspect value for the font.
+
+Starting at `0.6` in the first pair and adjusting to `0.5` in the second, we continue adjusting the `font-size-adjust` property value until the borders around the "b" letters align perfectly in the third pair. In this example, the aspect value is determined to be `0.482`.
+
+```html
+
+
+
+
+
+```
+
+```css hidden
+body {
+ display: flex;
+}
+
+div {
+ text-align: center;
+}
+
+p {
+ margin: 0 30px 10px 30px;
}
```
-#### Results
+```css
+body {
+ display: flex;
+}
+
+div {
+ text-align: center;
+}
+
+p {
+ font-family: Futura;
+ font-size: 50px;
+}
+
+span {
+ border: solid 1px red;
+}
+
+.adjust1 {
+ font-size-adjust: 0.6;
+}
+
+.adjust2 {
+ font-size-adjust: 0.5;
+}
+
+.adjust3 {
+ font-size-adjust: 0.482;
+}
+```
-{{ EmbedLiveSample('Adjusting_lower-case_letter_sizes', 500, 200) }}
+{{ EmbedLiveSample('Determining the aspect value of a font', 500, 120) }}
## Specifications
@@ -132,5 +227,5 @@ font-size-adjust: unset;
- {{cssxref("font-size")}}
- {{cssxref("font-weight")}}
-- [Fundamental text and font styling](/en-US/docs/Learn/CSS/Styling_text/Fundamentals)
-- {{cssxref("@font-face/size-adjust", "size-adjust")}} descriptor
+- {{cssxref("@font-face/size-adjust", "size-adjust")}} `@font-face` descriptor
+- [Learn: Fundamental text and font styling](/en-US/docs/Learn/CSS/Styling_text/Fundamentals)
diff --git a/files/en-us/web/css/font-synthesis-position/index.md b/files/en-us/web/css/font-synthesis-position/index.md
new file mode 100644
index 000000000000000..a0dc032626c201c
--- /dev/null
+++ b/files/en-us/web/css/font-synthesis-position/index.md
@@ -0,0 +1,103 @@
+---
+title: font-synthesis-position
+slug: Web/CSS/font-synthesis-position
+page-type: css-property
+browser-compat: css.properties.font-synthesis-position
+---
+
+{{CSSRef}}
+
+The **`font-synthesis-position`** [CSS](/en-US/docs/Web/CSS) property lets you specify whether or not a browser may synthesize the subscript and superscript "position" typefaces when they are missing in a font family, while using {{cssxref("font-variant-position")}} to set the positions.
+
+The **`font-synthesis-position`** property has no effect when using the {{htmlelement("sup")}} and {{htmlelement("sub")}} elements.
+
+It is often convenient to use the shorthand property {{cssxref("font-synthesis")}} to control all typeface synthesis values.
+
+## Syntax
+
+```css
+/* Keyword values */
+font-synthesis-position: auto;
+font-synthesis-position: none;
+
+/* Global values */
+font-synthesis-position: inherit;
+font-synthesis-position: initial;
+font-synthesis-position: revert;
+font-synthesis-position: revert-layer;
+font-synthesis-position: unset;
+```
+
+### Values
+
+- `auto`
+ - : Indicates that a missing position typeface may be synthesized by the browser if needed.
+- `none`
+ - : Indicates that the synthesis of a missing position typeface by the browser is not allowed.
+
+## Formal definition
+
+{{cssinfo}}
+
+## Formal syntax
+
+{{csssyntax}}
+
+## Examples
+
+### Disabling synthesis of position typeface
+
+This example shows turning off synthesis of the superscript and subscript typefaces by the browser in the `Montserrat` font.
+
+#### HTML
+
+```html
+
+ These are the default position superscript ,
+ position subscript , bold and
+ oblique typefaces.
+
+
+
+ The positions superscript and
+ subscript typeface is turned off here but not the
+ bold and oblique typefaces (on browsers that support
+ font-synthesis-position
).
+
+```
+
+#### CSS
+
+```css
+@import url("https://fonts.googleapis.com/css2?family=Montserrat&display=swap");
+
+* {
+ font-family: "Montserrat", sans-serif;
+}
+.super {
+ font-variant-position: super;
+}
+.sub {
+ font-variant-position: sub;
+}
+.no-syn {
+ font-synthesis-position: none;
+}
+```
+
+#### Result
+
+{{EmbedLiveSample('Disabling synthesis of position typeface', '', '100')}}
+
+## Specifications
+
+{{Specifications}}
+
+## Browser compatibility
+
+{{Compat}}
+
+## See also
+
+- {{cssxref("font-synthesis")}} shorthand, {{cssxref("font-synthesis-style")}}, {{cssxref("font-synthesis-weight")}}
+- {{cssxref("font-style")}}, {{cssxref("font-variant")}}, {{cssxref("font-variant-position")}}, {{cssxref("font-weight")}}
diff --git a/files/en-us/web/css/font-synthesis/index.md b/files/en-us/web/css/font-synthesis/index.md
index 2dd05b16a1c071f..ce253721e4d74e4 100644
--- a/files/en-us/web/css/font-synthesis/index.md
+++ b/files/en-us/web/css/font-synthesis/index.md
@@ -7,7 +7,7 @@ browser-compat: css.properties.font-synthesis
{{CSSRef}}
-The **`font-synthesis`** [shorthand](/en-US/docs/Web/CSS/Shorthand_properties) [CSS](/en-US/docs/Web/CSS) property lets you specify whether or not the browser may synthesize the bold, italic, and/or small-caps typefaces when they are missing in the specified font-family.
+The **`font-synthesis`** [shorthand](/en-US/docs/Web/CSS/Shorthand_properties) [CSS](/en-US/docs/Web/CSS) property lets you specify whether or not the browser may synthesize the bold, italic, small-caps, and/or subscript and superscript typefaces when they are missing in the specified font-family.
{{EmbedInteractiveExample("pages/css/font-synthesis.html")}}
@@ -18,6 +18,7 @@ This property is a shorthand for the following CSS properties:
- [font-synthesis-weight](/en-US/docs/Web/CSS/font-synthesis-weight)
- [font-synthesis-style](/en-US/docs/Web/CSS/font-synthesis-style)
- [font-synthesis-small-caps](/en-US/docs/Web/CSS/font-synthesis-small-caps)
+- [font-synthesis-position](/en-US/docs/Web/CSS/font-synthesis-position)
## Syntax
@@ -26,8 +27,9 @@ This property is a shorthand for the following CSS properties:
font-synthesis: none;
font-synthesis: weight;
font-synthesis: style;
+font-synthesis: position;
font-synthesis: small-caps style; /* property values can be in any order */
-font-synthesis: style small-caps weight; /* property values can be in any order */
+font-synthesis: style small-caps weight position; /* property values can be in any order */
/* Global values */
font-synthesis: inherit;
@@ -44,13 +46,15 @@ font-synthesis: unset;
- `weight`
- : Indicates that the missing bold typeface may be synthesized by the browser if needed.
- `style`
- - : Indicates that the missing italic typeface may be synthesized by the browser if needed.
+ - : Indicates that the italic typeface may be synthesized by the browser if needed.
- `small-caps`
- - : Indicates that the missing small-caps typeface may be synthesized by the browser if needed.
+ - : Indicates that the small-caps typeface may be synthesized by the browser if needed.
+- `position`
+ - : Indicates that the subscript and superscript typeface may be synthesized by the browser, if needed, when using {{cssxref("font-variant-position")}}.
## Description
-Most standard Western fonts include italic and bold variants, and some fonts include a small-caps variant. However, many fonts do not. Fonts used for Chinese, Japanese, Korean and other logographic scripts tend not to include these variants and synthesizing them might impede the legibility or change the meaning of the text. In these cases, it may be desirable to switch off the browser's default font-synthesis.
+Most standard Western fonts include italic and bold variants, and some fonts include a small-caps and subscript/superscript variants. However, many fonts do not. Fonts used for Chinese, Japanese, Korean and other logographic scripts tend not to include these variants and synthesizing them might impede the legibility or change the meaning of the text. In these cases, it may be desirable to switch off the browser's default font-synthesis.
For example, using the [:lang()](/en-US/docs/Web/CSS/:lang) pseudo-class, you can disable the browser from synthesizing bold and oblique characters for a language, in this case Arabic:
@@ -62,16 +66,23 @@ For example, using the [:lang()](/en-US/docs/Web/CSS/:lang) pseudo-class, you ca
The table below shows how a value of the shorthand `font-synthesis` property maps to the constituent longhand properties.
-| font-synthesis value | [font-synthesis-weight](/en-US/docs/Web/CSS/font-synthesis-weight) value | [font-synthesis-style](/en-US/docs/Web/CSS/font-synthesis-style) value | [font-synthesis-small-caps](/en-US/docs/Web/CSS/font-synthesis-small-caps) value |
-| :------------------------ | :----------------------------------------------------------------------- | :--------------------------------------------------------------------- | :------------------------------------------------------------------------------- |
-| `none` | `none` | `none` | `none` |
-| `weight` | `auto` | `none` | `none` |
-| `style` | `none` | `auto` | `none` |
-| `small-caps` | `none` | `none` | `auto` |
-| `weight style` | `auto` | `auto` | `none` |
-| `weight small-caps` | `auto` | `none` | `auto` |
-| `style small-caps` | `none` | `auto` | `auto` |
-| `weight style small-caps` | `auto` | `auto` | `auto` |
+| font-synthesis value | [font-synthesis-weight](/en-US/docs/Web/CSS/font-synthesis-weight) value | [font-synthesis-style](/en-US/docs/Web/CSS/font-synthesis-style) value | [font-synthesis-small-caps](/en-US/docs/Web/CSS/font-synthesis-small-caps) value | [font-synthesis-position](/en-US/docs/Web/CSS/font-synthesis-position) value |
+| :--------------------------------- | :----------------------------------------------------------------------- | :--------------------------------------------------------------------- | :------------------------------------------------------------------------------- | :--------------------------------------------------------------------------- |
+| `none` | `none` | `none` | `none` | `none` |
+| `weight` | `auto` | `none` | `none` | `none` |
+| `style` | `none` | `auto` | `none` | `none` |
+| `small-caps` | `none` | `none` | `auto` | `none` |
+| `position` | `none` | `none` | `none` | `auto` |
+| `weight style` | `auto` | `auto` | `none` | `none` |
+| `weight small-caps` | `auto` | `none` | `auto` | `none` |
+| `weight position` | `auto` | `none` | `none` | `auto` |
+| `style small-caps` | `none` | `auto` | `auto` | `none` |
+| `style position` | `none` | `auto` | `none` | `auto` |
+| `weight style small-caps` | `auto` | `auto` | `auto` | `none` |
+| `weight style position` | `auto` | `auto` | `none` | `auto` |
+| `weight small-caps position` | `auto` | `none` | `auto` | `auto` |
+| `style small-caps position` | `none` | `auto` | `auto` | `auto` |
+| `weight style small-caps position` | `auto` | `auto` | `auto` | `auto` |
## Formal definition
@@ -147,3 +158,5 @@ This example shows the browser's default font-synthesis behavior and compares it
- {{cssxref("font-style")}}
- {{cssxref("font-weight")}}
+- {{cssxref("font-variant-caps")}}
+- {{cssxref("font-variant-position")}}
diff --git a/files/en-us/web/css/font-variant-caps/index.md b/files/en-us/web/css/font-variant-caps/index.md
index fc092a7da0a781b..6d5264ee20489da 100644
--- a/files/en-us/web/css/font-variant-caps/index.md
+++ b/files/en-us/web/css/font-variant-caps/index.md
@@ -50,7 +50,7 @@ The `font-variant-caps` property is specified using a single keyword value from
- `normal`
- : Deactivates of the use of alternate glyphs.
- `small-caps`
- - : Enables display of small capitals (OpenType feature: `smcp`). Small-caps glyphs typically use the form of uppercase letters but are reduced to the size of lowercase letters.
+ - : Enables display of small capitals (OpenType feature: `smcp`). Small-caps glyphs typically use the form of uppercase letters but are displayed using the same size as lowercase letters.
- `all-small-caps`
- : Enables display of small capitals for both upper and lowercase letters (OpenType features: `c2sc`, `smcp`).
- `petite-caps`
diff --git a/files/en-us/web/css/general_sibling_combinator/index.md b/files/en-us/web/css/general_sibling_combinator/index.md
deleted file mode 100644
index d07bf8b5cad9ef1..000000000000000
--- a/files/en-us/web/css/general_sibling_combinator/index.md
+++ /dev/null
@@ -1,74 +0,0 @@
----
-title: General sibling combinator
-slug: Web/CSS/General_sibling_combinator
-page-type: css-combinator
-browser-compat: css.selectors.general_sibling
----
-
-{{CSSRef("Selectors")}}
-
-The **general sibling combinator** (`~`) separates two selectors and matches _all iterations_ of the second element, that are following the first element (though not necessarily immediately), and are children of the same parent {{Glossary("element")}}.
-
-```css
-/* Paragraphs that are siblings of and
- subsequent to any image */
-img ~ p {
- color: red;
-}
-```
-
-## Syntax
-
-```css-nolint
-/* The white space around the ~ combinator is optional but recommended. */
-former_element ~ target_element { style properties }
-```
-
-## Examples
-
-### CSS
-
-```css
-p ~ span {
- color: red;
-}
-```
-
-### HTML
-
-```html
-
- This is not red because it appears before any paragraph.
- Here is a paragraph.
- Here is some code.
-
- This span is red because it appears after the paragraph, even though there
- are other nodes in between
-
- Whatever it may be, keep smiling.
- Dream big
-
- Doesn't matter how many or what kind of nodes are in between, all spans from
- the same parent after a paragraph are red.
-
-
-
- This span is not red because it doesn't share a parent with a paragraph
-
-```
-
-### Result
-
-{{EmbedLiveSample("Examples", "auto", 300)}}
-
-## Specifications
-
-{{Specifications}}
-
-## Browser compatibility
-
-{{Compat}}
-
-## See also
-
-- [Adjacent sibling combinator](/en-US/docs/Web/CSS/Adjacent_sibling_combinator)
diff --git a/files/en-us/web/css/hue-interpolation-method/index.md b/files/en-us/web/css/hue-interpolation-method/index.md
index e094ba8ca468ff3..186b079013ea899 100644
--- a/files/en-us/web/css/hue-interpolation-method/index.md
+++ b/files/en-us/web/css/hue-interpolation-method/index.md
@@ -19,7 +19,7 @@ The **``** [CSS](/en-US/docs/Web/CSS) [data type](/en-
The interpolation method specifies how to find a midpoint between two hue values based on a color wheel.
It is used as a component of the {{CSSXref("<color-interpolation-method>")}} data type.
-When interpolating `` values, the hue interpolation algorithm defaults to [`shorter`](#values).
+When interpolating `` values, the hue interpolation algorithm defaults to [`shorter`](#shorter).
## Syntax
@@ -34,11 +34,11 @@ decreasing hue
### Values
-Any pair of hue angles `θ1` and `θ2` correspond to two radii on the {{Glossary("color wheel")}}, which cut the circumference into two possible arcs for interpolation. Both arcs start at the first radius and end at the second radius, but one goes clockwise and the other goes counterclockwise.
+Any pair of hue angles correspond to two radii on the {{Glossary("color wheel")}}, which cut the circumference into two possible arcs for interpolation. Both arcs start at the first radius and end at the second radius, but one goes clockwise and the other goes counterclockwise.
> **Note:** The following descriptions and illustrations are based on color wheels in which hue angles increase in a clockwise direction. Be aware that there are color wheels where an increase in angles will be a counterclockwise operation.
-There are four algorithms to determine which arc is used:
+For a pair of hue angles `θ1` and `θ2` normalized to the range `[0deg, 360deg)`, there are four algorithms to determine which arc is used when interpolating from `θ1` to `θ2`:
- `shorter`
@@ -47,9 +47,9 @@ There are four algorithms to determine which arc is used:
- If `θ1 < θ2`, use the clockwise arc;
- If `θ1 > θ2`, use the counterclockwise arc.
- | `θ1 = 45deg`, `θ2 = 135deg` | `θ1 = -225deg`, `θ2 = 45deg` |
- | ------------------------------------------------------------------ | ------------------------------------------------------------------- |
- | ![shorter with θ1 = 45deg and θ2 = 135deg](shorter_increasing.png) | ![shorter with θ1 = -225deg and θ2 = 45deg](shorter_decreasing.png) |
+ | `θ1 = 45deg`, `θ2 = 135deg` | `θ1 = 135deg`, `θ2 = 45deg` |
+ | ------------------------------------------------------------------ | ------------------------------------------------------------------ |
+ | ![shorter with θ1 = 45deg and θ2 = 135deg](shorter_increasing.png) | ![shorter with θ1 = 135deg and θ2 = 45deg](shorter_decreasing.png) |
- `longer`
@@ -63,31 +63,32 @@ There are four algorithms to determine which arc is used:
- If `θ1 < θ2`, use the clockwise arc;
- If `θ1 > θ2`, use the counterclockwise arc.
- | `θ1 = 45deg`, `θ2 = -225deg` | `θ1 = 135deg`, `θ2 = 45deg` |
- | ----------------------------------------------------------------- | ---------------------------------------------------------------- |
- | ![longer with θ1 = 45deg and θ2 = -225deg](longer_decreasing.png) | ![longer with θ1 = 135deg and θ2 = 45deg](longer_increasing.png) |
+ | `θ1 = 45deg`, `θ2 = 135deg` | `θ1 = 135deg`, `θ2 = 45deg` |
+ | ---------------------------------------------------------------- | ---------------------------------------------------------------- |
+ | ![longer with θ1 = 45deg and θ2 = 135deg](longer_decreasing.png) | ![longer with θ1 = 135deg and θ2 = 45deg](longer_increasing.png) |
- `increasing`
- - : Use the clockwise arc. When the two radii coincide:
+ - : Use the clockwise arc. When the two radii coincide, the arc degenerates to a single point.
- - If `θ1 < θ2`, the arc becomes the full circumference with a clockwise orientation.
- - If `θ1 ≥ θ2`, the arc degenerates to a single point.
-
- | `θ1 = 45deg`, `θ2 = 135deg` | `θ1 = 495deg`, `θ2 = 45deg` |
+ | `θ1 = 45deg`, `θ2 = 135deg` | `θ1 = 135deg`, `θ2 = 45deg` |
| --------------------------------------------------------------------- | -------------------------------------------------------------------- |
- | ![increasing with θ1 = 45deg and θ2 = 135deg](shorter_increasing.png) | ![increasing with θ1 = 495deg and θ2 = 45deg](longer_increasing.png) |
+ | ![increasing with θ1 = 45deg and θ2 = 135deg](shorter_increasing.png) | ![increasing with θ1 = 135deg and θ2 = 45deg](longer_increasing.png) |
- `decreasing`
- - : Use the counterclockwise arc. When the two radii coincide:
-
- - If `θ1 ≤ θ2`, the arc degenerates to a single point.
- - If `θ1 > θ2`, the arc becomes the full circumference with a counterclockwise orientation.
+ - : Use the counterclockwise arc. When the two radii coincide, the arc degenerates to a single point.
- | `θ1 = 45deg`, `θ2 = 495deg` | `θ1 = 135deg`, `θ2 = 45deg` |
+ | `θ1 = 45deg`, `θ2 = 135deg` | `θ1 = 135deg`, `θ2 = 45deg` |
| -------------------------------------------------------------------- | --------------------------------------------------------------------- |
- | ![decreasing with θ1 = 45deg and θ2 = 495deg](longer_decreasing.png) | ![decreasing with θ1 = 135deg and θ2 = 45deg](shorter_decreasing.png) |
+ | ![decreasing with θ1 = 45deg and θ2 = 135deg](longer_decreasing.png) | ![decreasing with θ1 = 135deg and θ2 = 45deg](shorter_decreasing.png) |
+
+As there are only two arcs to choose from, these algorithms are pairwise equivalent under certain circumstances. Specifically:
+
+- If `0deg < θ2 - θ1 < 180deg` or `θ2 - θ1 < -180deg`, `shorter` and `increasing` are equivalent, whereas `longer` and `decreasing` are equivalent.
+- If `-180deg < θ2 - θ1 < 0deg` or `θ2 - θ1 > 180deg`, `shorter` and `decreasing` are equivalent, whereas `longer` and `increasing` are equivalent.
+
+A notable feature of `increasing` and `decreasing` is that when the hue angle difference passes through `180deg` during transition or animation, the arc will not flip to the other side like `shorter` and `longer` do.
## Formal syntax
@@ -111,6 +112,9 @@ The following example shows the effect of using different hue interpolation algo
+
@@ -135,6 +139,54 @@ p {
color: white;
margin: 6px;
}
+
+/* Fallback styles */
+.hsl,
+.hsl-shorter,
+.hsl-named {
+ background: linear-gradient(
+ to right,
+ hsl(39 100% 50%),
+ hsl(46 100% 50%),
+ hsl(53 100% 50%),
+ hsl(60 100% 50%)
+ );
+}
+.hsl-increasing {
+ background: linear-gradient(
+ to right,
+ hsl(190 100% 50%),
+ hsl(225 100% 50%),
+ hsl(260 100% 50%),
+ hsl(295 100% 50%),
+ hsl(330 100% 50%),
+ hsl(365 100% 50%),
+ hsl(400 100% 50%),
+ hsl(435 100% 50%),
+ hsl(470 100% 50%),
+ hsl(505 100% 50%),
+ hsl(540 100% 50%)
+ );
+}
+.hsl-decreasing,
+.hsl-longer,
+.hsl-named-longer {
+ background: linear-gradient(
+ to right,
+ hsl(399 100% 50%),
+ hsl(368 100% 50%),
+ hsl(337 100% 50%),
+ hsl(307 100% 50%),
+ hsl(276 100% 50%),
+ hsl(245 100% 50%),
+ hsl(214 100% 50%),
+ hsl(183 100% 50%),
+ hsl(152 100% 50%),
+ hsl(122 100% 50%),
+ hsl(91 100% 50%),
+ hsl(60 100% 50%)
+ );
+}
```
```css
@@ -159,6 +211,13 @@ p {
hsl(60deg 100% 50%)
);
}
+.hsl-shorter {
+ background: linear-gradient(
+ to right in hsl shorter hue,
+ hsl(39deg 100% 50%),
+ hsl(60deg 100% 50%)
+ );
+}
.hsl-longer {
background: linear-gradient(
to right in hsl longer hue,
@@ -176,7 +235,7 @@ p {
#### Result
-{{EmbedLiveSample("comparing_hue_interpolation_methods", "100%", 400)}}
+{{EmbedLiveSample("comparing_hue_interpolation_methods", "100%", 500)}}
## Specifications
diff --git a/files/en-us/web/css/hue/index.md b/files/en-us/web/css/hue/index.md
index f1fe3212f52d95b..c045468ff93b727 100644
--- a/files/en-us/web/css/hue/index.md
+++ b/files/en-us/web/css/hue/index.md
@@ -113,7 +113,7 @@ A `` can be either an `` or a ``.
- ``
- : A real number, representing degrees of the hue angle.
-As an `` is periodic, `` is normalized to the range `[0deg, 360deg]`. It implicitly wraps around such that `480deg` is the same as `120deg`, `-120deg` is the same as `240deg`, `-1turn` is the same as `1turn`, and so on.
+As an `` is periodic, `` is normalized to the range `[0deg, 360deg)`. It implicitly wraps around such that `480deg` is the same as `120deg`, `-120deg` is the same as `240deg`, `-1turn` is the same as `1turn`, and so on.
### Interpolation
diff --git a/files/en-us/web/css/layout_cookbook/center_an_element/index.md b/files/en-us/web/css/layout_cookbook/center_an_element/index.md
index 088189ac74967ca..10b8fefcf6c27b0 100644
--- a/files/en-us/web/css/layout_cookbook/center_an_element/index.md
+++ b/files/en-us/web/css/layout_cookbook/center_an_element/index.md
@@ -9,7 +9,7 @@ browser-compat:
{{CSSRef}}
-In this recipe you will see how to center one box inside another. Centering both horizontally and vertically was difficult before flexbox, with the Box Alignment properties it is now straightforward.
+In this recipe, you will see how to center one box inside another by using [flexbox](#using_flexbox) and [grid](#using_grid). Centering both horizontally and vertically is simple and straightforward.
![an element centered inside a larger box](cookbook-center.png)
@@ -25,11 +25,74 @@ To place an item into the center of another box horizontally and vertically.
>
> [Download this example](https://github.com/mdn/css-examples/blob/main/css-cookbook/center--download.html)
-## Choices made
+## Using flexbox
-To center one box inside another we make the containing box a flex container. Then set {{cssxref("align-items")}} to center to perform centering on the block axis, and {{cssxref("justify-content")}} to center to perform centering on the inline axis.
+To center a box within another box, first turn the containing box into a [flex container](/en-US/docs/Web/CSS/CSS_flexible_box_layout/Basic_concepts_of_flexbox#the_flex_container) by setting its {{cssxref("display")}} property to `flex`. Then set {{cssxref("align-items")}} to `center` for vertical centering (on the block axis) and {{cssxref("justify-content")}} to `center` for horizontal centering (on the inline axis). And that's all it takes to center one box inside another!
-In the future we may be able to center elements without needing to turn the parent into a flex container, as the Box Alignment properties used here are specified to apply to block layout too. However, support is currently limited for box alignment properties on block layout, so currently centering using Flexbox is the most robust way to achieve this.
+### HTML
+
+```html
+
+```
+
+### CSS
+
+```css
+div {
+ border: solid 3px;
+ padding: 1em;
+ max-width: 75%;
+}
+.container {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ height: 8em;
+}
+```
+
+We set a height for the container to demonstrate that the inner item is indeed vertically centered within the container.
+
+### Result
+
+{{ EmbedLiveSample('Using_flexbox', 400, 200) }}
+
+Instead of applying `align-items: center;` on the container, you can also vertically center the inner item by setting {{cssxref("align-self")}} to `center` on the inner item itself.
+
+## Using grid
+
+Another method you can use for centering one box inside another is to first make the containing box a [grid container](/en-US/docs/Web/CSS/CSS_grid_layout/Basic_concepts_of_grid_layout#grid_container) and then set its {{cssxref("place-items")}} property to `center` to center align its items on both the block and inline axes.
+
+### HTML
+
+```html
+
+```
+
+### CSS
+
+```css
+div {
+ border: solid 3px;
+ padding: 1em;
+ max-width: 75%;
+}
+.container {
+ display: grid;
+ place-items: center;
+ height: 8em;
+}
+```
+
+### Result
+
+{{ EmbedLiveSample('Using_grid', 400, 200) }}
+
+Instead of applying `place-items: center;` on the container, you can achieve the same centering by setting {{cssxref("place-content", "place-content: center;")}} on the container or by applying either {{cssxref("place-self", "place-self: center")}} or {{cssxref("margin", "margin: auto;")}} on the inner item itself.
## Browser compatibility
diff --git a/files/en-us/web/css/list-style/index.md b/files/en-us/web/css/list-style/index.md
index 00dce1ceb846fcc..d41aea3b44943b0 100644
--- a/files/en-us/web/css/list-style/index.md
+++ b/files/en-us/web/css/list-style/index.md
@@ -11,7 +11,7 @@ The **`list-style`** CSS [shorthand property](/en-US/docs/Web/CSS/Shorthand_prop
{{EmbedInteractiveExample("pages/css/list-style.html")}}
-> **Note:** This property is applied to list items, i.e., elements with `{{cssxref("display")}}: list-item;`. [By default](https://html.spec.whatwg.org/multipage/rendering.html#lists) this includes {{HTMLElement("li")}} elements. Because this property is inherited, it can be set on a parent element (normally {{HTMLElement("ol")}} or {{HTMLElement("ul")}}) to make the same list styling apply to all the items inside.
+The values of this property are applied to list items, including {{HTMLElement("li")}} elements and elements with `{{cssxref("display")}}: list-item;`. Because this property is inherited, it can be set on a parent element (normally {{HTMLElement("ol")}} or {{HTMLElement("ul")}}) to make the same list styling apply to all the nested items.
## Constituent properties
@@ -33,11 +33,12 @@ list-style: url("../img/shape.png");
/* position */
list-style: inside;
-/* type | position */
-list-style: georgian inside;
+/* two values */
+list-style: georgian outside;
+list-style: url("img/pip.svg") inside;
-/* type | image | position */
-list-style: lower-roman url("../img/shape.png") outside;
+/* three values */
+list-style: lower-roman url("img/shape.png") outside;
/* Keyword value */
list-style: none;
@@ -50,86 +51,19 @@ list-style: revert-layer;
list-style: unset;
```
-The `list-style` property is specified as one, two, or three keywords in any order. If {{cssxref("list-style-type")}} and {{cssxref("list-style-image")}} are both set, then `list-style-type` is used as a fallback if the image is unavailable.
+The `list-style` property is specified as one, two, or three values in any order. If {{cssxref("list-style-type")}} and {{cssxref("list-style-image")}} are both set, the `list-style-type` is used as a fallback if the image is unavailable.
### Values
- {{cssxref("list-style-type")}}
- - : See {{cssxref("list-style-type")}}.
+ - : A ``, {{cssxref("string")}}, or `none`. If omitted in the shorthand, the default `disc` value is used. See {{cssxref("list-style-type")}}.
- {{cssxref("list-style-image")}}
- - : See {{cssxref("list-style-image")}}.
+ - : An {{cssxref("image")}} or `none`. If omitted, the default `none` value is used. See {{cssxref("list-style-image")}}.
- {{cssxref("list-style-position")}}
- - : See {{cssxref("list-style-position")}}.
+ - : Either `inside` or `outside`. If omitted, the default `outside` value is used. See {{cssxref("list-style-position")}}.
- `none`
- : No list style is used.
-## Accessibility concerns
-
-In a notable exception, Safari will not recognize an ordered or unordered list as a list in the accessibility tree if it has a `list-style` value of `none`. This [behavior is intentional](https://webkit.org/b/170179#c1) and not considered a bug.
-
-The most straightforward way to address this is to add an explicit `role="list"` to the `` or `` element in the markup. This will restore the list semantics without affecting the design:
-
-```html
-
- An item
- Another item
-
-```
-
-A CSS-only workaround is also available for those who do not have access to the markup: Adding [pseudo-content](/en-US/docs/Web/CSS/content) before each list item can restore list semantics:
-
-```css
-ul {
- list-style: none;
-}
-
-ul li::before {
- content: "+ ";
-}
-```
-
-The added pseudo-content is tested by Safari to determine if it should be accessible or ignored. Accessible pseudo-content restores list semantics, while ignored pseudo-content does not.
-
-Generally, text or images are determined to be things that should be accessible, which is why the `content: "+ ";` declaration in the previous example works.
-
-A declaration of `content: "";` (an empty string) is ignored, as are `content` values that contain only spaces, such as `content: " ";`, so these do not work.
-
-If the intent is to keep list item markers visually hidden, this can often be managed with a [zero-width space](https://en.wikipedia.org/wiki/Zero-width_space), ``, which is `\200B` in CSS and `\u200B` in JavaScript:
-
-```css
-ul {
- list-style: none;
-}
-
-ul li::before {
- content: "\200B";
-}
-```
-
-Another visually hidden approach is to apply an {{cssxref("<image>")}} to the `list-style` property:
-
-```css
-nav ol,
-nav ul {
- list-style: none;
-}
-
-/* becomes */
-
-nav ol,
-nav ul {
- list-style: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'/%3E");
-}
-```
-
-These CSS workarounds should be used only when the HTML solution is not available, and only after testing to ensure that they don't result in unexpected behaviors that may negatively impact users' experiences.
-
-- [Bug #170179 | WebKit Bugzilla](https://webkit.org/b/170179)
-- ['Fixing' Lists](https://www.scottohara.me/blog/2019/01/12/lists-and-safari.html)
-- [VoiceOver and list-style-type: none](https://gerardkcohen.me/writing/2017/voiceover-list-style-type.html)
-- [MDN Understanding WCAG, Guideline 1.3 explanations](/en-US/docs/Web/Accessibility/Understanding_WCAG/Perceivable#Guideline_1.3_%E2%80%94_Create_content_that_can_be_presented_in_different_ways)
-- [Understanding Success Criterion 1.3.1: Info and Relationships | WCAG 2.1](https://www.w3.org/WAI/WCAG21/Understanding/info-and-relationships.html)
-
## Formal definition
{{cssinfo}}
@@ -173,7 +107,41 @@ List 2
#### Result
-{{EmbedLiveSample('Setting_list_style_type_and_position', 'auto', 220)}}
+{{EmbedLiveSample('Setting_list_style_type_and_position', 'auto', 240)}}
+
+## Accessibility concerns
+
+Safari does not recognize ordered or unordered lists as lists in the accessibility tree if they have a `list-style` value of `none`, unless the list is nested within the {{HTMLElement("nav")}} navigation element. This [behavior is intentional](https://webkit.org/b/170179#c1) and is not considered a bug.
+
+To ensure lists are announced as lists, include [`role="list"`](/en-US/docs/Web/Accessibility/ARIA/Roles/list_role) to {{HTMLElement("ol")}} and {{HTMLElement("ul")}} elements, especially if the list is not nested in a ``. This restores list semantics without affecting the design:
+
+```html
+
+ An item
+ Another item
+
+```
+
+If an ARIA `role` is not an option for your code, CSS can be used instead. Adding non-empty [pseudo-content](/en-US/docs/Web/CSS/content) such as text or images before each list item can restore list semantics, but impacts the visual appearance. Safari determines if the added pseudo-content suffices as accessible content, restoring list semantics if so. Generally, Safari considers text and images as sufficient, which is why the `content: "+ ";` shown below works (but requires additional styling to not affect the design).
+
+```css
+ul {
+ list-style: none;
+}
+
+ul li::before {
+ content: "+ ";
+}
+```
+
+A declaration of `content: "";` (an empty string) is ignored, as are `content` values that contain only spaces, such as `content: " ";`.
+
+These CSS workarounds should only be used when an HTML solution is unavailable, and only after testing to ensure that they don't result in unexpected behaviors that may negatively impact user experience.
+
+- ['Fixing' Lists](https://www.scottohara.me/blog/2019/01/12/lists-and-safari.html) (2023)
+- [VoiceOver and list-style-type: none](https://gerardkcohen.me/writing/2017/voiceover-list-style-type.html) (2017)
+- [Understanding WCAG: Create content that can be presented in different ways](/en-US/docs/Web/Accessibility/Understanding_WCAG/Perceivable#Guideline_1.3_%E2%80%94_Create_content_that_can_be_presented_in_different_ways)
+- [Understanding success criterion 1.3.1: Info and relationships | WCAG 2.1](https://www.w3.org/WAI/WCAG21/Understanding/info-and-relationships.html)
## Specifications
@@ -185,4 +153,7 @@ List 2
## See also
-- {{Cssxref("list-style-type")}}, {{Cssxref("list-style-image")}}, {{Cssxref("list-style-position")}}
+- {{Cssxref("list-style-type")}}, {{Cssxref("list-style-image")}}, and {{Cssxref("list-style-position")}} properties
+- {{Cssxref("::marker")}} pseudo-element
+- [CSS lists](/en-US/docs/Web/CSS/CSS_lists) module
+- [CSS counter styles](/en-US/docs/Web/CSS/CSS_counter_styles) module
diff --git a/files/en-us/web/css/min-content/index.md b/files/en-us/web/css/min-content/index.md
index 6cb68fd3ec3694c..900956c4b5539dd 100644
--- a/files/en-us/web/css/min-content/index.md
+++ b/files/en-us/web/css/min-content/index.md
@@ -91,3 +91,7 @@ grid-template-columns: 200px 1fr min-content;
## Browser compatibility
{{Compat}}
+
+## See also
+
+- Related sizing keywords: {{cssxref("max-content")}}, {{cssxref("fit-content")}}
diff --git a/files/en-us/web/css/nesting_selector/index.md b/files/en-us/web/css/nesting_selector/index.md
index 0aa319b8c47ce2c..d2760f7e5c14abe 100644
--- a/files/en-us/web/css/nesting_selector/index.md
+++ b/files/en-us/web/css/nesting_selector/index.md
@@ -183,7 +183,7 @@ This example uses nested CSS styling.
.example {
font-family: system-ui;
font-size: 1.2rem;
- & a {
+ & > a {
color: tomato;
&:hover,
&:focus {
diff --git a/files/en-us/web/css/adjacent_sibling_combinator/index.md b/files/en-us/web/css/next-sibling_combinator/index.md
similarity index 59%
rename from files/en-us/web/css/adjacent_sibling_combinator/index.md
rename to files/en-us/web/css/next-sibling_combinator/index.md
index fca32c336843462..187766f910244ee 100644
--- a/files/en-us/web/css/adjacent_sibling_combinator/index.md
+++ b/files/en-us/web/css/next-sibling_combinator/index.md
@@ -1,13 +1,13 @@
---
-title: Adjacent sibling combinator
-slug: Web/CSS/Adjacent_sibling_combinator
+title: Next-sibling combinator
+slug: Web/CSS/Next-sibling_combinator
page-type: css-combinator
-browser-compat: css.selectors.adjacent_sibling
+browser-compat: css.selectors.next-sibling
---
{{CSSRef("Selectors")}}
-The **adjacent sibling combinator** (`+`) separates two selectors and matches the second element only if it _immediately_ follows the first element, and both are children of the same parent {{DOMxRef("element")}}.
+The **next-sibling combinator** (`+`) separates two selectors and matches the second element only if it _immediately_ follows the first element, and both are children of the same parent {{DOMxRef("element")}}.
```css
/* Paragraphs that come immediately after any image */
@@ -57,4 +57,4 @@ li:first-of-type + li {
## See also
-- [General sibling combinator](/en-US/docs/Web/CSS/General_sibling_combinator)
+- [Subsequent-sibling combinator](/en-US/docs/Web/CSS/Subsequent-sibling_combinator)
diff --git a/files/en-us/web/css/offset-path/index.md b/files/en-us/web/css/offset-path/index.md
index c21b0c1668a4dca..4ea1cdcbd51e9bf 100644
--- a/files/en-us/web/css/offset-path/index.md
+++ b/files/en-us/web/css/offset-path/index.md
@@ -52,7 +52,7 @@ offset-path: unset;
### Values
-The `offset-path` takes as it's value an `` value, a [``](/en-US/docs/Web/CSS/box-edge#values) value, or both, or the `none` keyword. The `` value is a {{cssxref("ray","ray()")}}, a {{cssxref("url","url()")}}, or a [``](/en-US/docs/Web/CSS/basic-shape) value.
+The `offset-path` property takes as its value an `` value, a [``](/en-US/docs/Web/CSS/box-edge#values) value, or both, or the `none` keyword. The `` value is a {{cssxref("ray","ray()")}} function, a {{cssxref("url","<url>")}} value, or a [``](/en-US/docs/Web/CSS/basic-shape) value.
- `none`
@@ -60,15 +60,15 @@ The `offset-path` takes as it's value an `` value, a [``
- ``
- - : A `ray()`, a `url()`, or a `` specifying the geometrical offset path. If omitted, the path shape for the `` value is `inset(0 round X)`, where `X` is the value of {{cssxref("border-radius")}} of the element that establishes the [containing block](/en-US/docs/Web/CSS/Containing_block).
+ - : A `ray()` function, a `` value, or a `` value that specifies the geometrical offset path. If omitted, the path shape for the `` value is `inset(0 round X)`, where `X` is the value of {{cssxref("border-radius")}} of the element that establishes the [containing block](/en-US/docs/Web/CSS/Containing_block).
- {{cssxref("ray","ray()")}}
- : Defines a line starting at a set position, of a set length, and extending at the specified angle. The `ray()` function accepts up to four parameters – an {{CSSxRef("angle")}}, an optional size value, the optional keyword `contain`, and an optional `at `.
- - {{cssxref("url","url()")}}
+ - {{cssxref("url","<url>")}}
- - : Specifies the URL reference of a shape element with an SVG. The path is the shape of the SVG {{SVGElement("circle")}}, {{SVGElement("ellipse")}}, {{SVGElement("line")}}, {{SVGElement("path")}}, {{SVGElement("polygon")}}, or {{SVGElement("rect")}} element referenced by the`id` in the `url()` parameter. If the URL does not reference a shape element or is otherwise invalid, the resolved value for the offset path is `path("M0,0")` (which is a valid `` value).
+ - : Specifies the ID of an [SVG shape element](/en-US/docs/Web/SVG/Tutorial/Basic_Shapes). The path is the shape of the SVG {{SVGElement("circle")}}, {{SVGElement("ellipse")}}, {{SVGElement("line")}}, {{SVGElement("path")}}, {{SVGElement("polygon")}}, {{SVGElement("polyline")}}, or {{SVGElement("rect")}} element referenced by its `id` in the `url()` function. If the URL does not reference a shape element or is otherwise invalid, the resolved value for the offset path is `path("M0,0")` (which is a valid `` value).
- [``](/en-US/docs/Web/CSS/basic-shape)
@@ -76,7 +76,7 @@ The `offset-path` takes as it's value an `` value, a [``
- [``](/en-US/docs/Web/CSS/box-edge#values)
- - : Specifies the size information of the [reference box](/en-US/docs/Web/CSS/CSS_shapes/Basic_shapes#the_reference_box) containing the path. The reference box is derived from the element that establishes the containing block for this element. This parameter is optional. If not specified, the default value is `border-box`. In SVG, the value is treated as `view-box`. If `ray()` or `` is used to define the offset path, the `` value provides the reference box for the ray or the ``, respectively. If `url()` is used to define the offset path, the `` value provides the viewport and user coordinate system for the shape element, with the origin (`0 0`) at the top left corner and size being `1px`.
+ - : Specifies the size information of the [reference box](/en-US/docs/Web/CSS/CSS_shapes/Basic_shapes#the_reference_box) containing the path. The reference box is derived from the element that establishes the containing block for this element. This parameter is optional. If not specified, the default value is `border-box` in CSS contexts. In SVG contexts, the value is treated as `view-box`. If `ray()` or `` is used to define the offset path, the `` value provides the reference box for the ray or the ``, respectively. If `` is used to define the offset path, the `` value provides the viewport and user coordinate system for the shape element, with the origin (`0 0`) at the top left corner and size being `1px`.
## Description
@@ -104,7 +104,7 @@ This example demonstrates using various `` values in the `offset-path
```
-```css
+```css hidden
body {
width: 300px;
height: 200px;
@@ -114,7 +114,9 @@ body {
padding: 25px;
margin: 50px;
}
+```
+```css
.box {
width: 40px;
height: 20px;
@@ -159,15 +161,11 @@ In this example, the margin, border, and padding have been purposely given large
{{EmbedLiveSample('Creating an offset-path using box-edge positioning', '100%', 400)}}
-### Animating an element with offset-path
-
-In the CSS code in this example, the `offset-path` property defines a path that is identical to the `` element in the SVG. The path of the SVG animation is a line drawing of a house with a chimney.
+### Creating an offset-path using path()
-#### SVG
+In this example, the {{svgelement("svg")}} element creates a house with a chimney and also defines two halves of a scissor. The house and chimney are composed of rectangles and polygons, and the scissor halves are represented by two distinct path elements. In the CSS code, the `offset-path` property is used to specify a path to follow for the two scissor halves. This CSS-defined path is identical to the one represented by the `` element in the SVG, which is the outline of the house including the chimney.
-The top and bottom halves of the scissors would appear in the top left of the canvas were they not positioned along the starting point of the path defined by `offset-path`.
-
-```html
+```html live-sample___offset_path_path
```
-```css
+```css live-sample___offset_path_path
.scissorHalf {
offset-path: path(
"M900,190 L993,245 V201 A11,11 0 0,1 1004,190 H1075 A11,11 0 0,1 1086,201 V300 L1294,423 H1216 A11,11 0 0,0 1205,434 V789 A11,11 0 0,1 1194,800 H606 A11,11 0 0,1 595,789 V434 A11,11 0 0,0 584,423 H506 L900,190"
@@ -227,7 +225,60 @@ The top and bottom halves of the scissors would appear in the top left of the ca
#### Result
-{{EmbedLiveSample('Animating_an_element_with_offset-path', '100%', '450')}}
+Without the `offset-path` property, the two halves of the scissors would default to the top-left corner of the canvas. However, by using `offset-path`, the two scissor halves are aligned with the starting point of the SVG path, allowing them to move along it.
+
+{{EmbedLiveSample('offset_path_path', '100%', '450')}}
+
+### Creating an offset-path using url()
+
+This example illustrates how to refer to an SVG shape to define the shape of the path that an element can follow. The green circle (defined by `.target`) follows the path of a rectangle, which is defined by passing the SVG shape's ID (`svgRect`) to the `offset-path` property by using `url()`.
+
+The SVG rectangle that defines the path shape is shown here only to visually demonstrate that the green circle is indeed following the path defined by this rectangle.
+
+```html live-sample___offset_path_url
+
+
+
+
+
+```
+
+```css hidden live-sample___offset_path_url
+.outer {
+ position: absolute;
+}
+```
+
+```css live-sample___offset_path_url
+.target {
+ width: 50px;
+ height: 50px;
+ border-radius: 50%;
+ background-color: green;
+ offset-path: url(#svgRect);
+ offset-anchor: auto;
+ animation: move 5s linear infinite;
+}
+
+#svgRect {
+ fill: antiquewhite;
+ stroke: black;
+ stroke-width: 2;
+}
+
+@keyframes move {
+ 0% {
+ offset-distance: 0%;
+ }
+ 100% {
+ offset-distance: 100%;
+ }
+}
+```
+
+{{EmbedLiveSample('live-sample___offset_path_url', '100%', '250')}}
## Specifications
diff --git a/files/en-us/web/css/opacity/index.md b/files/en-us/web/css/opacity/index.md
index 8c6d0671e3d4b06..9035478a5e90c0e 100644
--- a/files/en-us/web/css/opacity/index.md
+++ b/files/en-us/web/css/opacity/index.md
@@ -43,6 +43,8 @@ opacity: unset;
Using `opacity` with a value other than `1` places the element in a new [stacking context](/en-US/docs/Web/CSS/CSS_positioned_layout/Understanding_z-index/Stacking_context).
+When `opacity` value is set to `0`, the element and all of its children are not visible; however, they still register [pointer events](/en-US/docs/Web/API/Pointer_events). This can be controlled with the CSS [`pointer-events`](/en-US/docs/Web/CSS/pointer-events) property.
+
To change the opacity of a background only, use the {{cssxref("background")}} property with a {{cssxref("color_value", "color value")}} that allows for an alpha channel. For example:
```css
diff --git a/files/en-us/web/css/overflow-x/index.md b/files/en-us/web/css/overflow-x/index.md
index 65c0cb0955056a3..40ee948d1c8e96e 100644
--- a/files/en-us/web/css/overflow-x/index.md
+++ b/files/en-us/web/css/overflow-x/index.md
@@ -45,7 +45,8 @@ If {{cssxref("overflow-y")}} is `hidden`, `scroll`, or `auto`, and the `overflow
- : Overflow content is clipped if necessary to fit horizontally inside the element's padding box. Browsers display scroll bars in the horizontal direction whether or not any content is actually clipped. (This prevents scroll bars from appearing or disappearing when the content changes.) Printers may still print overflowing content.
- `auto`
- : Overflow content is clipped at the element's padding box, and overflow content can be scrolled into view. Unlike `scroll`, user agents display scroll bars _only if_ the content is overflowing and hide scroll bars by default. If content fits inside the element's padding box, it looks the same as with `visible`, but still establishes a new block-formatting context. Desktop browsers provide scroll bars if content overflows.
- > **Note:** The keyword value `overlay` is a legacy value alias for `auto`. With `overlay`, the scroll bars are drawn on top of the content instead of taking up space.
+
+> **Note:** The keyword value `overlay` is a legacy value alias for `auto`. With `overlay`, the scroll bars are drawn on top of the content instead of taking up space.
## Formal definition
diff --git a/files/en-us/web/css/overscroll-behavior-block/index.md b/files/en-us/web/css/overscroll-behavior-block/index.md
index 6797047c40341ea..ead4ca56b550127 100644
--- a/files/en-us/web/css/overscroll-behavior-block/index.md
+++ b/files/en-us/web/css/overscroll-behavior-block/index.md
@@ -34,7 +34,7 @@ The `overscroll-behavior-block` property is specified as a keyword chosen from t
- `auto`
- : The default scroll overflow behavior occurs as normal.
- `contain`
- - : Default scroll overflow behavior is observed inside the element this value is set on (e.g. "bounce" effects or refreshes), but no scroll chaining occurs to neighboring scrolling areas, e.g. underlying elements will not scroll.
+ - : Default scroll overflow behavior (e.g., "bounce" effects) is observed inside the element where this value is set. However, no scroll chaining occurs on neighboring scrolling areas; the underlying elements will not scroll. The `contain` value disables native browser navigation, including the vertical pull-to-refresh gesture and horizontal swipe navigation.
- `none`
- : No scroll chaining occurs to neighboring scrolling areas, and default scroll overflow behavior is prevented.
diff --git a/files/en-us/web/css/overscroll-behavior-inline/index.md b/files/en-us/web/css/overscroll-behavior-inline/index.md
index 925d9313bb03514..e85287e888e0139 100644
--- a/files/en-us/web/css/overscroll-behavior-inline/index.md
+++ b/files/en-us/web/css/overscroll-behavior-inline/index.md
@@ -34,7 +34,7 @@ The `overscroll-behavior-inline` property is specified as a keyword chosen from
- `auto`
- : The default scroll overflow behavior occurs as normal.
- `contain`
- - : Default scroll overflow behavior is observed inside the element this value is set on (e.g. "bounce" effects or refreshes), but no scroll chaining occurs to neighboring scrolling areas, e.g. underlying elements will not scroll.
+ - : Default scroll overflow behavior (e.g., "bounce" effects) is observed inside the element where this value is set. However, no scroll chaining occurs on neighboring scrolling areas; the underlying elements will not scroll. The `contain` value disables native browser navigation, including the vertical pull-to-refresh gesture and horizontal swipe navigation.
- `none`
- : No scroll chaining occurs to neighboring scrolling areas, and default scroll overflow behavior is prevented.
diff --git a/files/en-us/web/css/overscroll-behavior-x/index.md b/files/en-us/web/css/overscroll-behavior-x/index.md
index 4658060ad603846..eb5d3e0f58a5d94 100644
--- a/files/en-us/web/css/overscroll-behavior-x/index.md
+++ b/files/en-us/web/css/overscroll-behavior-x/index.md
@@ -34,7 +34,7 @@ The `overscroll-behavior-x` property is specified as a keyword chosen from the l
- `auto`
- : The default scroll overflow behavior occurs as normal.
- `contain`
- - : Default scroll overflow behavior is observed inside the element this value is set on (e.g. "bounce" effects or refreshes), but no scroll chaining occurs to neighboring scrolling areas, e.g. underlying elements will not scroll.
+ - : Default scroll overflow behavior (e.g., "bounce" effects) is observed inside the element where this value is set. However, no scroll chaining occurs on neighboring scrolling areas; the underlying elements will not scroll. The `contain` value disables native browser navigation, including the vertical pull-to-refresh gesture and horizontal swipe navigation.
- `none`
- : No scroll chaining occurs to neighboring scrolling areas, and default scroll overflow behavior is prevented.
diff --git a/files/en-us/web/css/overscroll-behavior-y/index.md b/files/en-us/web/css/overscroll-behavior-y/index.md
index 4f5f576bca7afe5..eabaca89ce442cd 100644
--- a/files/en-us/web/css/overscroll-behavior-y/index.md
+++ b/files/en-us/web/css/overscroll-behavior-y/index.md
@@ -34,7 +34,7 @@ The `overscroll-behavior-y` property is specified as a keyword chosen from the l
- `auto`
- : The default scroll overflow behavior occurs as normal.
- `contain`
- - : Default scroll overflow behavior is observed inside the element this value is set on (e.g. "bounce" effects or refreshes), but no scroll chaining occurs to neighboring scrolling areas, e.g. underlying elements will not scroll.
+ - : Default scroll overflow behavior (e.g., "bounce" effects) is observed inside the element where this value is set. However, no scroll chaining occurs on neighboring scrolling areas; the underlying elements will not scroll. The `contain` value disables native browser navigation, including the vertical pull-to-refresh gesture and horizontal swipe navigation.
- `none`
- : No scroll chaining occurs to neighboring scrolling areas, and default scroll overflow behavior is prevented.
diff --git a/files/en-us/web/css/overscroll-behavior/index.md b/files/en-us/web/css/overscroll-behavior/index.md
index 2d5fd2e570581ac..1d7dfd0345e0508 100644
--- a/files/en-us/web/css/overscroll-behavior/index.md
+++ b/files/en-us/web/css/overscroll-behavior/index.md
@@ -46,7 +46,7 @@ Two keywords specifies the `overscroll-behavior` value on the `x` and `y` axes r
- `auto`
- : The default scroll overflow behavior occurs as normal.
- `contain`
- - : Default scroll overflow behavior is observed inside the element this value is set on (e.g. "bounce" effects or refreshes), but no scroll chaining occurs to neighboring scrolling areas, e.g. underlying elements will not scroll.
+ - : Default scroll overflow behavior (e.g., "bounce" effects) is observed inside the element where this value is set. However, no scroll chaining occurs on neighboring scrolling areas; the underlying elements will not scroll. The `contain` value disables native browser navigation, including the vertical pull-to-refresh gesture and horizontal swipe navigation.
- `none`
- : No scroll chaining occurs to neighboring scrolling areas, and default scroll overflow behavior is prevented.
diff --git a/files/en-us/web/css/position_value/index.md b/files/en-us/web/css/position_value/index.md
index 157f2dc30a9ef01..2ddc18bf06f6a43 100644
--- a/files/en-us/web/css/position_value/index.md
+++ b/files/en-us/web/css/position_value/index.md
@@ -7,7 +7,7 @@ browser-compat: css.types.position
{{CSSRef}}
-The **`