linkedom
linkedom copied to clipboard
`document.querySelector()` doesn't return specific class
Hi,
For some elements, the document.querySelector() method doesn't return the specific class, but a HTMLElement. For example with the <meta> element whose object returned isn't an HTMLMetaElement.
import * as linkedom from 'linkedom';
const { document } = linkedom.parseHTML(`
<html>
<head>
<meta name="foo" content="bar">
</head>
<body>
<button>Baz/<button>
<span>Qux/span>
<a>Quux</a>
</body>
</html>
`);
console.log(document.querySelector("meta") instanceof linkedom.HTMLMetaElement); // false
console.log(document.querySelector("button") instanceof linkedom.HTMLButtonElement); // true
console.log(document.querySelector("span") instanceof linkedom.HTMLSpanElement); // false
console.log(document.querySelector("a") instanceof linkedom.HTMLAnchorElement); // true
Maybe the problem happens only on the class that doesn't call registerHTMLClass().
meta is not registered as special tag IIRC and I have no plan to register all elements as special tags ... after reading all other issues you are having, I might suggest you are better off with JSDOM if you expect everything in the HTML standard to be implemented in here. This maybe will save you time for future issues: it's not a goal of this project to have the whole HTML standard working ... it actually tries to care the minimal amount of time to render SSR, not to replace JSDOM or a real browser and the read path is not super interesting in here as you always have regular workaround via getAttribute or other primitives.
I'm porting my Chrome extension to Manifest V3 which requires the use of a service worker. But in the service worker, the DOMParser object isn't available. So I want to use LinkeDOM as polyfill with the worker version 👍. I only need to parse HTML and find values with querySelector().
I find a bug on the dataset (uhyphen#1). And my other problems are the lack of properties: HTMLScriptElement.text #182, HTMLMetaElement.content #183, HTMLBlockquoteElement.cite, HTMLVideoElement.poster and HTMLEmbedElement.type. I created only two issues because then I thought that it was not the purpose of this project to have all properties.
I have a workaround:
import * as linkedom from "./lib/linkedom.js";
if (!("DOMParser" in globalThis)) {
globalThis.DOMParser = linkedom.DOMParser;
Object.defineProperty(linkedom.HTMLScriptElement.prototype, "text", {
get() {
return this.textContent;
},
});
}
But for content, I can't use HTMLMetaElement because querySelector() returns a HTMLElement. I can add the property in HTMLElement prototype, but it adds the property on all HTML elements.
Object.defineProperty(linkedom.HTMLElement.prototype, "content", {
get() {
return this.getAttribute("content") ?? "";
},
});
it's a web extension though, you can do whatever you want and nothing else should be affected? to have classes exposed and elements upgraded (it's like builtin extends in LinkeDOM even for native classes) you need to register the class to the parser, then you cna polyfill by your own.
these attributes are for today, every other for tomorrow ... you can likely overcome all of them through getAttribute after knowing what you are selecting, or use textContent instead of text for scripts ... not sure why you can't workaround these cases but heck, web extensions envs don't affect other sandboxes, do whatever you like there?
P.S. a way to export <option> is here, similarly to how you'd need to export all other special tags in your case, meta included, plus attributes https://github.com/WebReflection/linkedom/blob/155dfb9de84c672a0b2f4476d14f68d4b9a5054e/esm/html/option-element.js
I'm not blocked by all the issues I have opened. I can modify my code to use getAttribute() or override the classes provided by LinkeDOM.
By fixing directly in LinkeDOM, if other users have the same problems as me: they will benefit from the fixes.
I understand that you don't want to add an infinite of properties. You can close the issues I opened if you want.
the option one had a very valid use case because in SSR you want to write option.value = 'thing' and see that reflected, as much as selected, if true ... others look just like read-only accessors or not too interesting as SSR use cases when you can just use setAttribute or getAttribute so if a use-case is pretty common and compelling, happy to put it in there, if I have to accept every single weird accessor, we can say goodbye to performance in various ultra-common scenarios, reason these requests have been filed only today, after 1+ year this project has been adopted already by many. I don't see your requests as really necessary for daily usage, a bit too specialized, imho ... but maybe I am wrong?
P.S. if nothing, it's very possible I should think about a way to export pluggable in features/accessors/classes to recognize when querySelector is used so that patching these on demand is possible