-
Notifications
You must be signed in to change notification settings - Fork 48
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
attribute .href doesn't work #72
Comments
So far Deno DOM only implements the |
ok no worries. |
One complication here is that — in a browser — the document has a When a document is parsed from an HTML string using a DOMParser instance, there's not a way to attach the location information to the resulting document with the current API. This makes it non-trivial to get fully-qualified URLs from properties on elements within the trees of such parsed documents. However, this is both desirable and a common task, so I want to share two workaround approaches for resolving URLs from anchor element Functional approachThis is safer and has better type compatibility. Here's a commented example:
import {
DOMParser,
type Element,
} from "https://deno.land/x/[email protected]/deno-dom-wasm.ts";
import { assert } from "https://deno.land/[email protected]/testing/asserts.ts";
/** Functional form of `element.href` */
function resolveHref(element: Element, url: string | URL): string | undefined {
const href = element.getAttribute("href");
// ^? const href: string | null
if (!href) return undefined;
return new URL(href, url).href;
}
function main() {
const url = new URL("https://example.com/page/hello");
// Imagine the following HTML came from a fetch request to the URL above:
// const html = await (await fetch(url)).text();
const html = `
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>hello world</title>
</head>
<body>
<h1>hello world</h1>
<a class="external" href="https://en.wikipedia.org/wiki/Hello">wikipedia</a>
<a class="relative" href="about">about</a>
<a class="root" href="/account">account</a>
</body>
</html>
`;
const document = new DOMParser().parseFromString(html, "text/html");
// ^? const document: HTMLDocument | null
assert(document, "The document could not be parsed");
for (const className of ["external", "relative", "root"]) {
const anchor = document.querySelector(`a.${className}`);
// ^? const anchor: Element | null
assert(anchor, "Anchor element not found");
const hrefRaw = anchor.getAttribute("href");
// ^? const hrefRaw: string | null
const href = resolveHref(anchor, url);
// ^? const href: string | undefined
console.log({ hrefRaw, href });
}
}
if (import.meta.main) main();
Prototype manipulationThe previous approach could become tedious if there are lots of It allows for obtaining a URL string by directly accessing the
import {
DOMParser,
type Element,
type HTMLDocument,
} from "https://deno.land/x/[email protected]/deno-dom-wasm.ts";
import { assert } from "https://deno.land/[email protected]/testing/asserts.ts";
type HrefAttr = { href: string };
type ElementWithHref = Element & Partial<HrefAttr>;
type HTMLDocumentWithHref = HTMLDocument & { location: HrefAttr };
function createDocumentWithHref(
html: string,
url: string | URL,
): HTMLDocumentWithHref {
const document = new DOMParser().parseFromString(html, "text/html");
assert(document, "The document could not be parsed");
(document as HTMLDocumentWithHref).location = new URL(url);
const elementProto = Object.getPrototypeOf(document.createElement("a"));
Object.defineProperty(elementProto, "href", {
configurable: true,
enumerable: false,
get() {
const baseUrl = this.ownerDocument?.location?.href as string | undefined;
const href = this.getAttribute("href") as string | null;
if (!(baseUrl && href)) return undefined;
return new URL(href, baseUrl).href;
},
set(url: string) {
this.setAttribute("href", url);
},
});
return document as HTMLDocumentWithHref;
}
function main() {
const url = new URL("https://example.com/page/hello");
// Imagine the following HTML came from a fetch request to the URL above:
// const html = await (await fetch(url)).text();
const html = `
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>hello world</title>
</head>
<body>
<h1>hello world</h1>
<a class="external" href="https://en.wikipedia.org/wiki/Hello">wikipedia</a>
<a class="relative" href="about">about</a>
<a class="root" href="/account">account</a>
</body>
</html>
`;
const document = createDocumentWithHref(html, url);
for (const className of ["external", "relative", "root"]) {
const anchor = document.querySelector(`a.${className}`);
// ^? const anchor: Element | null
assert(anchor, "Anchor element not found");
const hrefRaw = anchor.getAttribute("href");
// ^? const hrefRaw: string | null
// The `.href` property doesn't exist on type Element,
// so trying to access it will create a compiler diagnostic error:
//
// anchor.href;
// ~~~~
// Property 'href' does not exist on type 'Element'.deno-ts(2339)
// Instead, you must assert that the Element is type ElementWithHref:
const href = (anchor as ElementWithHref).href;
// ^ (property) href?: string | undefined
console.log({ hrefRaw, href });
}
}
if (import.meta.main) main();
Both approaches result in the same outputs. |
does this library not support standard dom methods?
link.href
should give me thehref
attribute.The text was updated successfully, but these errors were encountered: