Skip to content

Commit 7dd1998

Browse files
fix: only attempt to patch HTMLFormElement if it is defined
1 parent e7da030 commit 7dd1998

File tree

2 files changed

+59
-41
lines changed

2 files changed

+59
-41
lines changed

Diff for: src/element-internals.ts

+9-41
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import { ValidityState, reconcileValidity, setValid } from './ValidityState';
2626
import { deferUpgrade, observerCallback, observerConfig } from './mutation-observers';
2727
import { IElementInternals, ICustomElement, LabelsList } from './types';
2828
import { CustomStateSet } from './CustomStateSet';
29-
import { HTMLFormControlsCollection } from './HTMLFormControlsCollection';
29+
import { patchFormPrototype } from './patch-form-prototype';
3030

3131
export class ElementInternals implements IElementInternals {
3232
ariaAtomic: string;
@@ -348,24 +348,14 @@ if (!isElementInternalsSupported()) {
348348
return shadowRoot;
349349
}
350350

351-
function checkValidityOverride(...args): boolean {
352-
let returnValue = checkValidity.apply(this, args);
353-
return overrideFormMethod(this, returnValue, 'checkValidity');
354-
}
355-
356-
function reportValidityOverride(...args): boolean {
357-
let returnValue = reportValidity.apply(this, args);
358-
return overrideFormMethod(this, returnValue, 'reportValidity');
359-
}
360-
361351
/**
362352
* Attaches an ElementInternals instance to a custom element. Calling this method
363353
* on a built-in element will throw an error.
364354
*/
365355
HTMLElement.prototype.attachInternals = function(): IElementInternals {
366356
if (!this.tagName) {
367357
/** This happens in the LitSSR environment. Here we can generally ignore internals for now */
368-
return {} as unknown as IElementInternals;
358+
return {} as object as IElementInternals;
369359
} else if (this.tagName.indexOf('-') === -1) {
370360
throw new Error(`Failed to execute 'attachInternals' on 'HTMLElement': Unable to attach ElementInternals to non-custom elements.`);
371361
}
@@ -381,35 +371,13 @@ if (!isElementInternalsSupported()) {
381371
const documentObserver = new MutationObserver(observerCallback);
382372
documentObserver.observe(document.documentElement, observerConfig);
383373

384-
const checkValidity = HTMLFormElement.prototype.checkValidity;
385-
HTMLFormElement.prototype.checkValidity = checkValidityOverride;
386-
387-
const reportValidity = HTMLFormElement.prototype.reportValidity;
388-
HTMLFormElement.prototype.reportValidity = reportValidityOverride;
389-
390-
const { get } = Object.getOwnPropertyDescriptor(HTMLFormElement.prototype, 'elements');
391-
Object.defineProperty(HTMLFormElement.prototype, 'elements', {
392-
get(...args) {
393-
const elements = get.call(this, ...args);
394-
const polyfilledElements = Array.from(formElementsMap.get(this) || []);
395-
396-
// If there are no polyfilled elements, return the native elements collection
397-
if (polyfilledElements.length === 0) {
398-
return elements;
399-
}
400-
401-
// Merge the native elements with the polyfilled elements
402-
// and order them by their position in the DOM
403-
const orderedElements = Array.from(elements).concat(polyfilledElements).sort((a: Element, b: Element) => {
404-
if (a.compareDocumentPosition) {
405-
return a.compareDocumentPosition(b) & 2 ? 1 : -1;
406-
}
407-
return 0;
408-
});
409-
410-
return new HTMLFormControlsCollection(orderedElements);
411-
},
412-
});
374+
/**
375+
* Keeps the polyfill from throwing in environments where HTMLFormElement
376+
* is undefined like in a server environment
377+
*/
378+
if (HTMLFormElement) {
379+
patchFormPrototype();
380+
}
413381

414382
if (!window.CustomStateSet) {
415383
window.CustomStateSet = CustomStateSet;

Diff for: src/patch-form-prototype.ts

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { HTMLFormControlsCollection } from './HTMLFormControlsCollection';
2+
import { formElementsMap } from "./maps";
3+
import { overrideFormMethod } from "./utils";
4+
5+
/**
6+
* Patch the HTMLElement prototype
7+
*
8+
* This function patches checkValidity, reportValidity and elements
9+
*/
10+
export function patchFormPrototype(): void {
11+
const checkValidity = HTMLFormElement.prototype.checkValidity;
12+
HTMLFormElement.prototype.checkValidity = checkValidityOverride;
13+
14+
const reportValidity = HTMLFormElement.prototype.reportValidity;
15+
HTMLFormElement.prototype.reportValidity = reportValidityOverride;
16+
17+
function checkValidityOverride(...args): boolean {
18+
let returnValue = checkValidity.apply(this, args);
19+
return overrideFormMethod(this, returnValue, 'checkValidity');
20+
}
21+
22+
function reportValidityOverride(...args): boolean {
23+
let returnValue = reportValidity.apply(this, args);
24+
return overrideFormMethod(this, returnValue, 'reportValidity');
25+
}
26+
27+
const { get } = Object.getOwnPropertyDescriptor(HTMLFormElement.prototype, 'elements');
28+
Object.defineProperty(HTMLFormElement.prototype, 'elements', {
29+
get(...args) {
30+
const elements = get.call(this, ...args);
31+
const polyfilledElements = Array.from(formElementsMap.get(this) || []);
32+
33+
// If there are no polyfilled elements, return the native elements collection
34+
if (polyfilledElements.length === 0) {
35+
return elements;
36+
}
37+
38+
// Merge the native elements with the polyfilled elements
39+
// and order them by their position in the DOM
40+
const orderedElements = Array.from(elements).concat(polyfilledElements).sort((a: Element, b: Element) => {
41+
if (a.compareDocumentPosition) {
42+
return a.compareDocumentPosition(b) & 2 ? 1 : -1;
43+
}
44+
return 0;
45+
});
46+
47+
return new HTMLFormControlsCollection(orderedElements);
48+
},
49+
});
50+
}

0 commit comments

Comments
 (0)