S
SolidJS3mo ago
_JS

onLoad attribute on a link tag in entry-server not running

I'm on @solidjs/start 0.7.7 and trying to add an onload="this.onload=null;this.rel='stylesheet'" on a link tag. The purpose is more widely explained here. Problem with this approach is that typescript keeps shouting Type 'string' is not assignable to type 'EventHandlerUnion<HTMLLinkElement, Event> | undefined'. So I rewrote the single liner above to the following:
<link
...
onLoad={function (this: HTMLLinkElement) {
console.log("I was run")
this.onload = null
this.rel = "stylesheet"
}}
/>
<link
...
onLoad={function (this: HTMLLinkElement) {
console.log("I was run")
this.onload = null
this.rel = "stylesheet"
}}
/>
And I don't see the console.log anywhere. I checked on the console tab, the terminal I ran "pnpm dev" on... nothing How should I handle this? I'd like to keep onload="this.onload=null;this.rel='stylesheet'", or accept any alternatives that work
web.dev
Defer non-critical CSS  |  Articles  |  web.dev
Learn how to defer non-critical CSS with the goal of optimizing the Critical Rendering Path, and improving First Contentful Paint (FCP).
8 Replies
_JS
_JS3mo ago
I'd also like to point out that the single liner works if we just ignore the typescript
peerreynders
peerreynders3mo ago
Have you tried
// @refresh reload
// src/entry-client.tsx
import { mount, StartClient } from '@solidjs/start/client';

mount(() => {
const link = document.querySelector('link[rel="preload"][as="style"]');
if (link instanceof HTMLLinkElement) link.rel = 'stylesheet';

return (<StartClient />);
}, document.getElementById('app')!);
// @refresh reload
// src/entry-client.tsx
import { mount, StartClient } from '@solidjs/start/client';

mount(() => {
const link = document.querySelector('link[rel="preload"][as="style"]');
if (link instanceof HTMLLinkElement) link.rel = 'stylesheet';

return (<StartClient />);
}, document.getElementById('app')!);
and
// src/entry-server.tsx

<link rel="preload" as="style" href="styles.css"/>
// src/entry-server.tsx

<link rel="preload" as="style" href="styles.css"/>
Another alternative
// file: src/entry-server.tsx
import { getRequestEvent } from 'solid-js/web';
import { createHandler, StartServer } from '@solidjs/start/server';

declare module '@solidjs/start/server' {
interface RequestEventLocals {
clientId: string;
}
}

const loadNonCritical = `
function loadNonCritical() {
var link = document.querySelector('link[rel="preload"][as="style"]');
if (link instanceof HTMLLinkElement) link.rel = 'stylesheet';
}

if (document.readyState === 'loading') {
document.addEventListener("DOMContentLoaded", loadNonCritical);
} else {
loadNonCritical();
}`;

export default createHandler(() => {
const event = getRequestEvent();
// @ts-ignore
const nonce = event.nonce;

return (
<StartServer
document={({ assets, children, scripts }) => (
<html lang="en">
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1"
/>
<link rel="icon" href="/favicon.ico" />
<link rel="preload" as="style" href="styles.css" />
<script {...(nonce ? { nonce } : {})} innerHTML={loadNonCritical} />
{assets}
</head>
<body>
<div id="app">{children}</div>
{scripts}
</body>
</html>
)}
/>
);
});
// file: src/entry-server.tsx
import { getRequestEvent } from 'solid-js/web';
import { createHandler, StartServer } from '@solidjs/start/server';

declare module '@solidjs/start/server' {
interface RequestEventLocals {
clientId: string;
}
}

const loadNonCritical = `
function loadNonCritical() {
var link = document.querySelector('link[rel="preload"][as="style"]');
if (link instanceof HTMLLinkElement) link.rel = 'stylesheet';
}

if (document.readyState === 'loading') {
document.addEventListener("DOMContentLoaded", loadNonCritical);
} else {
loadNonCritical();
}`;

export default createHandler(() => {
const event = getRequestEvent();
// @ts-ignore
const nonce = event.nonce;

return (
<StartServer
document={({ assets, children, scripts }) => (
<html lang="en">
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1"
/>
<link rel="icon" href="/favicon.ico" />
<link rel="preload" as="style" href="styles.css" />
<script {...(nonce ? { nonce } : {})} innerHTML={loadNonCritical} />
{assets}
</head>
<body>
<div id="app">{children}</div>
{scripts}
</body>
</html>
)}
/>
);
});
peerreynders
peerreynders3mo ago
Finally that article is from 2019. Under CSP inline event handlers will be ignored and only scripts with a valid CSP hash or nonce will be executed.
MDN Web Docs
Content Security Policy (CSP) - HTTP | MDN
Content Security Policy (CSP) is an added layer of security that helps to detect and mitigate certain types of attacks, including Cross-Site Scripting (XSS) and data injection attacks. These attacks are used for everything from data theft, to site defacement, to malware distribution.
MDN Web Docs
nonce - HTML: HyperText Markup Language | MDN
The nonce global attribute is a content attribute defining a cryptographic nonce ("number used once") which can be used by Content Security Policy to determine whether or not a given fetch will be allowed to proceed for a given element.
_JS
_JS3mo ago
@peerreynders Thank you for the suggestions! I guess @ts-ignore seems to be the way to go eh?
peerreynders
peerreynders3mo ago
You shouldn't be using the string "event handler" (inline event-handling HTML attibute) in the first place (which is what TypeScript is complaining about).
onload="this.onload=null;this.rel='stylesheet'"
onload="this.onload=null;this.rel='stylesheet'"
Chrome is notorious for all of a sudden cracking down on these type of things. So if updating the element in entry-client.tsx is too late, injecting a whitelisted inline script is the way to go.
_JS
_JS3mo ago
You shouldn't be using the string "event handler" (inline event-handling HTML attibute) in the first place (which is what TypeScript is complaining about).
I get that part but is it really such a big deal tho?
peerreynders
peerreynders3mo ago
https://stackoverflow.com/search?tab=newest&q=inline%20event%20handler%20CSP&searchOn=3 More often than not they cause avoidable grief like all of a sudden your site only loading its critical CSS.
And I don't see the console.log anywhere.
That's because the JSX uses addEventListener. That onload is run at element creation; so by the time addEventListener is run it's too late. The tactic is gaming the preload mechanic; delaying the switch to the time that DOM element is created. If you pick your critical CSS to suitably style the above the fold content of the page the link.rel = 'stylesheet' in entry-client.tsx should be fine.
Stack Overflow
Human verification
Stack Overflow | The World’s Largest Online Community for Developers
_JS
_JS3mo ago
gotcha. Thank you for the detailed explanation