Skip to content

Commit

Permalink
Fix issue 28752, part 3: update querySelector*
Browse files Browse the repository at this point in the history
  • Loading branch information
wbamberg committed Jul 26, 2024
1 parent 4419aed commit af5bd9b
Show file tree
Hide file tree
Showing 4 changed files with 341 additions and 64 deletions.
130 changes: 88 additions & 42 deletions files/en-us/web/api/document/queryselector/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,15 @@ browser-compat: api.Document.querySelector

The {{domxref("Document")}} method **`querySelector()`**
returns the first {{domxref("Element")}} within the document that matches the specified
selector, or group of selectors. If no matches are found, `null` is returned.
[CSS selector](/en-US/docs/Web/CSS/CSS_selectors), or group of CSS selectors. If no matches are found, `null` is returned.

> **Note:** The matching is done using depth-first pre-order traversal of
> the document's nodes starting with the first element in the document's markup and
> iterating through sequential nodes by order of the number of child nodes.
The matching is done using depth-first pre-order traversal of the document's nodes starting with the first element in the document's markup and iterating through sequential nodes by order of the number of child nodes.

If the specified selector matches an ID that is incorrectly used more than once in the
document, the first element with that ID is returned.

[CSS pseudo-elements](/en-US/docs/Web/CSS/Pseudo-elements) will never return
any elements, as specified in the [Selectors API](https://www.w3.org/TR/selectors-api/#grammar).

## Syntax

Expand All @@ -25,14 +29,12 @@ querySelector(selectors)
### Parameters

- `selectors`

- : A string containing one or more selectors to match. This string
must be a valid CSS selector string; if it isn't, a `SyntaxError` exception
is thrown. See [Locating DOM elements using selectors](/en-US/docs/Web/API/Document_Object_Model/Locating_DOM_elements_using_selectors) for more about selectors and how to manage them.
is thrown.

> **Note:** Characters that are not part of standard CSS syntax must be
> escaped using a backslash character. Since JavaScript also uses backslash escaping, be
> especially careful when writing string literals using these characters. See
> [Escaping special characters](#escaping_special_characters) for more information.
Note that the HTML specification does not require attribute values to be valid CSS identifiers. If a [`class`](/en-US/docs/Web/HTML/Global_attributes/class) or [`id`](/en-US/docs/Web/HTML/Global_attributes/id) attribute value is not a valid CSS identifier, than you must escape it before using it in a selector, either by calling {{domxref("CSS.escape_static", "CSS.escape()")}} on the value, or using one of the techniques described in [Escaping characters](/en-US/docs/Web/CSS/ident#escaping_characters). See [Escaping attribute values](#escaping_attribute_values) for an example.

### Return value

Expand All @@ -47,39 +49,6 @@ If you need a list of all elements matching the specified selectors, you should
- `SyntaxError` {{domxref("DOMException")}}
- : Thrown if the syntax of the specified _selectors_ is invalid.

## Usage notes

If the specified selector matches an ID that is incorrectly used more than once in the
document, the first element with that ID is returned.

[CSS pseudo-elements](/en-US/docs/Web/CSS/Pseudo-elements) will never return
any elements, as specified in the [Selectors API](https://www.w3.org/TR/selectors-api/#grammar).

### Escaping special characters

To match against an ID or selectors that do not follow standard CSS syntax (by using a
colon or space inappropriately, for example), you must escape the character with a
backslash ("`\`"). As the backslash is also an escape character in
JavaScript, if you are entering a literal string, you must escape it _twice_
(once for the JavaScript string, and another time for `querySelector()`):

```html
<div id="foo\bar"></div>
<div id="foo:bar"></div>

<script>
console.log("#foo\bar"); // "#fooar" (\b is the backspace control character)
document.querySelector("#foo\bar"); // Does not match anything
console.log("#foo\\bar"); // "#foo\bar"
console.log("#foo\\\\bar"); // "#foo\\bar"
document.querySelector("#foo\\\\bar"); // Match the first div
document.querySelector("#foo:bar"); // Does not match anything
document.querySelector("#foo\\:bar"); // Match the second div
</script>
```

## Examples

### Finding the first element matching a class
Expand Down Expand Up @@ -116,6 +85,83 @@ const el = document.querySelector(
This will select an input with a parent div with the `user-panel` class but
not the `main` class.

### Escaping attribute values

This example shows that if an HTML document contains an [`id`](/en-US/docs/Web/HTML/Global_attributes/id) which is not a valid [CSS identifier](/en-US/docs/Web/CSS/ident), then we must escape the attribute value before using it in `querySelector()`.

#### HTML

In the following code, a {{htmlelement("div")}} element has an `id` of `"this?element"`, which is not a valid CSS identifier, because the `"?"` character is not allowed in CSS identifiers.

We also have three buttons, and a {{htmlelement("pre")}} element for logging errors.

```html
<div id="this?element"></div>

<button id="no-escape">No escape</button>
<button id="css-escape">CSS.escape()</button>
<button id="manual-escape">Manual escape</button>

<pre id="log"></pre>
```

#### CSS

```css
div {
background-color: blue;
margin: 1rem 0;
height: 100px;
width: 200px;
}
```

#### JavaScript

All three buttons, when clicked, try to select the `<div>`, and then set its background color to a random value.

- The first button uses the `"this?element"` value directly.
- The second button escapes the value using {{domxref("CSS.escape_static", "CSS.escape()")}}.
- The third button explicitly escapes the `"?"` character using a backslash. Note that we must also escape the backslash itself, using another backslash, like: `"\\?"`.

```js
const log = document.querySelector("#log");

function random(number) {
return Math.floor(Math.random() * number);
}

function setBackgroundColor(id) {
log.textContent = "";

try {
const element = document.querySelector(`#${id}`);
const randomColor = `rgb(${random(255)} ${random(255)} ${random(255)})`;
element.style.backgroundColor = randomColor;
} catch (e) {
log.textContent = e;
}
}

document.querySelector("#no-escape").addEventListener("click", () => {
setBackgroundColor("this?element");
});

document.querySelector("#css-escape").addEventListener("click", () => {
setBackgroundColor(CSS.escape("this?element"));
});

document.querySelector("#manual-escape").addEventListener("click", () => {
setBackgroundColor("this\\?element");
});
```

#### Result

Clicking the first button gives an error, while the second and third buttons work properly.

{{embedlivesample("escaping_attribute_values", "", 200)}}

## Specifications

{{Specifications}}
Expand Down
90 changes: 82 additions & 8 deletions files/en-us/web/api/document/queryselectorall/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,12 @@ querySelectorAll(selectors)
### Parameters

- `selectors`
- : A string containing one or more selectors to match against. This
string must be a valid [CSS selector](/en-US/docs/Web/CSS/CSS_selectors)
string; if it's not, a `SyntaxError` exception is thrown. See [Locating DOM elements using selectors](/en-US/docs/Web/API/Document_Object_Model/Locating_DOM_elements_using_selectors) for more information about using selectors to
identify elements. Multiple selectors may be specified by separating them using
commas.

> **Note:** Characters which are not part of standard CSS syntax must be
> escaped using a backslash character. Since JavaScript also uses backslash escaping,
> special care must be taken when writing string literals using these characters. See [Escaping special characters](/en-US/docs/Web/API/Document/querySelector#escaping_special_characters) for more information.
- : A string containing one or more selectors to match. This string
must be a valid CSS selector string; if it isn't, a `SyntaxError` exception
is thrown.

Note that the HTML specification does not require attribute values to be valid CSS identifiers. If a [`class`](/en-US/docs/Web/HTML/Global_attributes/class) or [`id`](/en-US/docs/Web/HTML/Global_attributes/id) attribute value is not a valid CSS identifier, than you must escape it before using it in a selector, either by calling {{domxref("CSS.escape_static", "CSS.escape()")}} on the value, or using one of the techniques described in [Escaping characters](/en-US/docs/Web/CSS/ident#escaping_characters). See [Escaping attribute values](#escaping_attribute_values) for an example.

### Return value

Expand Down Expand Up @@ -105,6 +102,83 @@ highlightedItems.forEach((userItem) => {
});
```

### Escaping attribute values

This example shows that if an HTML document contains an [`id`](/en-US/docs/Web/HTML/Global_attributes/id) which is not a valid [CSS identifier](/en-US/docs/Web/CSS/ident), then we must escape the attribute value before using it in `querySelectorAll()`.

#### HTML

In the following code, a {{htmlelement("div")}} element has an `id` of `"this?element"`, which is not a valid CSS identifier, because the `"?"` character is not allowed in CSS identifiers.

We also have three buttons, and a {{htmlelement("pre")}} element for logging errors.

```html
<div id="this?element"></div>

<button id="no-escape">No escape</button>
<button id="css-escape">CSS.escape()</button>
<button id="manual-escape">Manual escape</button>

<pre id="log"></pre>
```

#### CSS

```css
div {
background-color: blue;
margin: 1rem 0;
height: 100px;
width: 200px;
}
```

#### JavaScript

All three buttons, when clicked, try to select the `<div>`, and then set its background color to a random value.

- The first button uses the `"this?element"` value directly.
- The second button escapes the value using {{domxref("CSS.escape_static", "CSS.escape()")}}.
- The third button explicitly escapes the `"?"` character using a backslash. Note that we must also escape the backslash itself, using another backslash, like: `"\\?"`.

```js
const log = document.querySelector("#log");

function random(number) {
return Math.floor(Math.random() * number);
}

function setBackgroundColor(id) {
log.textContent = "";

try {
const elements = document.querySelectorAll(`#${id}`);
const randomColor = `rgb(${random(255)} ${random(255)} ${random(255)})`;
elements[0].style.backgroundColor = randomColor;
} catch (e) {
log.textContent = e;
}
}

document.querySelector("#no-escape").addEventListener("click", () => {
setBackgroundColor("this?element");
});

document.querySelector("#css-escape").addEventListener("click", () => {
setBackgroundColor(CSS.escape("this?element"));
});

document.querySelector("#manual-escape").addEventListener("click", () => {
setBackgroundColor("this\\?element");
});
```

#### Result

Clicking the first button gives an error, while the second and third buttons work properly.

{{embedlivesample("escaping_attribute_values", "", 200)}}

## Specifications

{{Specifications}}
Expand Down
90 changes: 86 additions & 4 deletions files/en-us/web/api/element/queryselector/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@ querySelector(selectors)
### Parameters

- `selectors`
- : A group of [selectors](/en-US/docs/Learn/CSS/Building_blocks/Selectors) to match
the descendant elements of the {{domxref("Element")}} `baseElement`
against; this must be valid CSS syntax, or a `SyntaxError` exception will
occur. The first element found which matches this group of selectors is returned.

- : A string containing one or more selectors to match. This string
must be a valid CSS selector string; if it isn't, a `SyntaxError` exception
is thrown.

Note that the HTML specification does not require attribute values to be valid CSS identifiers. If a [`class`](/en-US/docs/Web/HTML/Global_attributes/class) or [`id`](/en-US/docs/Web/HTML/Global_attributes/id) attribute value is not a valid CSS identifier, than you must escape it before using it in a selector, either by calling {{domxref("CSS.escape_static", "CSS.escape()")}} on the value, or using one of the techniques described in [Escaping characters](/en-US/docs/Web/CSS/ident#escaping_characters). See [Escaping attribute values](#escaping_attribute_values) for an example.

### Return value

Expand Down Expand Up @@ -147,6 +149,86 @@ Notice how the `"div span"` selector still successfully matches the
do not include the {{HTMLElement("div")}} element (it is still part of the specified
selector).

### Escaping attribute values

This example shows that if an HTML document contains an [`id`](/en-US/docs/Web/HTML/Global_attributes/id) which is not a valid [CSS identifier](/en-US/docs/Web/CSS/ident), then we must escape the attribute value before using it in `querySelector()`.

#### HTML

In the following code, a {{htmlelement("div")}} element has an `id` of `"this?element"`, which is not a valid CSS identifier, because the `"?"` character is not allowed in CSS identifiers.

We also have three buttons, and a {{htmlelement("pre")}} element for logging errors.

```html
<div id="container">
<div id="this?element"></div>
</div>

<button id="no-escape">No escape</button>
<button id="css-escape">CSS.escape()</button>
<button id="manual-escape">Manual escape</button>

<pre id="log"></pre>
```

#### CSS

```css
div {
background-color: blue;
margin: 1rem 0;
height: 100px;
width: 200px;
}
```

#### JavaScript

All three buttons, when clicked, try to select the `<div>`, and then set its background color to a random value.

- The first button uses the `"this?element"` value directly.
- The second button escapes the value using {{domxref("CSS.escape_static", "CSS.escape()")}}.
- The third button explicitly escapes the `"?"` character using a backslash. Note that we must also escape the backslash itself, using another backslash, like: `"\\?"`.

```js
const container = document.querySelector("#container");
const log = document.querySelector("#log");

function random(number) {
return Math.floor(Math.random() * number);
}

function setBackgroundColor(id) {
log.textContent = "";

try {
const element = container.querySelector(`#${id}`);
const randomColor = `rgb(${random(255)} ${random(255)} ${random(255)})`;
element.style.backgroundColor = randomColor;
} catch (e) {
log.textContent = e;
}
}

document.querySelector("#no-escape").addEventListener("click", () => {
setBackgroundColor("this?element");
});

document.querySelector("#css-escape").addEventListener("click", () => {
setBackgroundColor(CSS.escape("this?element"));
});

document.querySelector("#manual-escape").addEventListener("click", () => {
setBackgroundColor("this\\?element");
});
```

#### Result

Clicking the first button gives an error, while the second and third buttons work properly.

{{embedlivesample("escaping_attribute_values", "", 200)}}

### More examples

See {{domxref("Document.querySelector()")}} for additional examples of the proper
Expand Down
Loading

0 comments on commit af5bd9b

Please sign in to comment.