Skip to content

Return NodeList from querySelectorAll - #680

Merged
olavoasantos merged 1 commit into
correct-selector-matchingfrom
return-polyfill-node-list
Sep 16, 2026
Merged

olavoasantos merged 1 commit into
correct-selector-matchingfrom
return-polyfill-node-list

Conversation

@olavoasantos

@olavoasantos olavoasantos commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Problem

querySelectorAll() collected matches in a plain Array, even though the polyfill provides a NodeList collection with the DOM-compatible item() method. Array indexing, length, iteration, and forEach() happened to work, but callers using the standard NodeList.item() shape received a collection without that method.

Impact

Minor. Feature-compatible selector code that calls .item() on a querySelectorAll() result threw at runtime instead of receiving the matching element or null for an out-of-range index.

Reproduction

const container = document.createElement('div');
container.innerHTML = '<p class="text">First paragraph</p>';

const matches = container.querySelectorAll('.text');
matches.item(0);

Before this change, the final call failed because matches was a plain array. It now returns the paragraph; matches.item(-1) and matches.item(matches.length) return null. The captured result remains static after a later matching node is appended.

Change

Construct selector results as NodeList<Element> rather than Element[], and make NodeList generic so the public and internal query APIs retain element item types. The accompanying type refinements keep childNodes, children, and element collection helpers aligned with that collection shape.

Tests

Adds runtime coverage for the returned NodeList instance, indexing, iteration, forEach(), in-range and out-of-range item(), and static result behavior. Type assertions cover instance and standalone selector results as NodeList<Element> and .item() as Element | null.

Stack

Validation

Fresh GitHub CI on restacked head e08f0af passes:

  • lint;
  • type-check and build;
  • unit tests with coverage;
  • bundle-size checks;
  • Playwright end-to-end tests;
  • the classified Web Platform Test suite.

@olavoasantos
olavoasantos force-pushed the return-polyfill-node-list branch from df1cc5d to 61e85fe Compare September 3, 2026 15:29
@olavoasantos
olavoasantos force-pushed the return-polyfill-node-list branch from 61e85fe to e08f0af Compare September 4, 2026 14:30
@olavoasantos
olavoasantos marked this pull request as ready for review September 14, 2026 18:41
@henrytao-me

Copy link
Copy Markdown
Member

The scoped production change looks good at e08f0af against #679 1204f44. I found one test correction to make before landing:

selectors.test.ts:243-260 assigns className to the newly appended paragraph:

container.appendChild(document.createElement('p')).className = 'text';

This polyfill does not implement className reflection. The assignment creates a plain property, leaves getAttribute('class') as null, and even a fresh query still returns three matches. Native's fresh query returns four. The test never adds a matching node, so the unchanged saved result does not demonstrate static membership.

Please use the supported attribute API and assert both the fresh and saved collections:

container
  .appendChild(document.createElement('p'))
  .setAttribute('class', 'text');

expect(container.querySelectorAll('.text')).toHaveLength(4);
expect(matches).toHaveLength(3);

Keep the existing order/iteration assertions. I independently verified this repaired fixture gives old 3 / fresh 4 on native, base, and head. No className implementation belongs in this narrow PR.

Otherwise, .item() now works for ordinary indices, result membership stays static while preserving node references, and existing live children/childNodes bookkeeping is unchanged. The shared NodeList helper still has pre-existing index-conversion and array-backed-shape limitations; this is not a request to bundle those broader corrections here.

Validation: 288 polyfill tests and 59 focused core tests pass; all three new tests fail the actual base. The locked-TypeScript polyfill check passes head, while base plus the new tests has four expected type errors. All 30 scoped native collection/snapshot/bookkeeping checks match, and 279 selector-result cases are unchanged from #679, including its known gaps. Formatting and reported CI are green. Full-repository/live validation and upstream corrections remain separate gates. No source fix or approval performed.

@olavoasantos
olavoasantos force-pushed the return-polyfill-node-list branch from e08f0af to 914a468 Compare September 16, 2026 13:56
@olavoasantos
olavoasantos force-pushed the return-polyfill-node-list branch from 914a468 to 00b081c Compare September 16, 2026 14:08
@olavoasantos

Copy link
Copy Markdown
Contributor Author

yea, the fixture wasn't adding a matching node. i switched it to setAttribute('class', 'text') and now assert the fresh query has four results while the saved NodeList remains at three. No className implementation is added here.

stack merge was automatically disabled September 16, 2026 18:51

Pull Request is not mergeable

stack merge was automatically disabled September 16, 2026 19:07

Pull Request is not mergeable

stack merge was automatically disabled September 16, 2026 19:35

Pull Request is not mergeable

stack merge was automatically disabled September 16, 2026 19:45

Pull Request is not mergeable

@olavoasantos
olavoasantos force-pushed the return-polyfill-node-list branch from 00b081c to b87b664 Compare September 16, 2026 19:56
@olavoasantos
olavoasantos force-pushed the return-polyfill-node-list branch from b87b664 to 5466415 Compare September 16, 2026 20:13
@olavoasantos
olavoasantos removed this pull request from stack #698 September 16, 2026 20:14
@olavoasantos
olavoasantos added this pull request to stack #715 September 16, 2026 20:14
@olavoasantos
olavoasantos merged commit 28955a5 into main Sep 16, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants