S
SolidJS2w ago
ali

onLoad event doesn't trigger

Hello everyone. I ran into the issue, that the onLoad event on an <img> does not trigger. Here is the code in question: https://stackblitz.com/edit/github-a9ukgq-25tgxa?file=src%2Fpages%2FHome.tsx,src%2Fcomponents%2Fcomp.tsx,src%2Fcomponents%2Fcomp.css
StackBlitz
Solid-start Basic Example (forked) - StackBlitz
Run official live example code for Solid-start Basic, created by Solidjs on StackBlitz
3 Replies
peerreynders
peerreynders2w ago
It looks like it works the very first time. However once the image is cached by the browser the image is already loaded (and complete is true) by the time the event listener is added, so onLoad never fires.
MDN Web Docs
HTMLImageElement: complete property - Web APIs | MDN
The read-only HTMLImageElement interface's complete attribute is a Boolean value which indicates whether or not the image has completely loaded.
ali
aliOP2w ago
I see, thanks!
peerreynders
peerreynders2w ago
One possible workaround:
import { createEffect, createSignal, For } from 'solid-js';
import './comp.css';
export default function Comp() {
const [images, setImages] = createSignal<string[]>([
// …
]);

let galleryRef: HTMLDivElement;
createEffect(() => {
const sources = images();
// By the time the effect runs the image tags should already exist
// and the listeners attached; now set the `src`
const list = galleryRef.querySelectorAll('img');
for (let i = 0; i < list.length; i += 1) {
const img = list.item(i);
img.src = sources[i];
}
});

return (
<div ref={(el) => (galleryRef = el)} class="masonry-gallery">
<For each={images()}>
{(img, index) => (
<div>
<img onLoad={(event) => console.log(index(), event)} />
</div>
)}
</For>
</div>
);
}
import { createEffect, createSignal, For } from 'solid-js';
import './comp.css';
export default function Comp() {
const [images, setImages] = createSignal<string[]>([
// …
]);

let galleryRef: HTMLDivElement;
createEffect(() => {
const sources = images();
// By the time the effect runs the image tags should already exist
// and the listeners attached; now set the `src`
const list = galleryRef.querySelectorAll('img');
for (let i = 0; i < list.length; i += 1) {
const img = list.item(i);
img.src = sources[i];
}
});

return (
<div ref={(el) => (galleryRef = el)} class="masonry-gallery">
<For each={images()}>
{(img, index) => (
<div>
<img onLoad={(event) => console.log(index(), event)} />
</div>
)}
</For>
</div>
);
}
Another one
import { createSignal, For } from 'solid-js';
import './comp.css';

function makeDeferSrc(source: string) {
return (element: HTMLImageElement) => {
const task = () => {
if (!element.isConnected) return void setTimeout(task);

// element is connected to the DOM
element.src = source;
};

task();
};
}

export default function Comp() {
const [images, setImages] = createSignal<string[]>([
// …
]);

return (
<div class="masonry-gallery">
<For each={images()}>
{(img, index) => (
<div>
<img
ref={makeDeferSrc(img)}
onLoad={(event) => console.log(index(), event)}
/>
</div>
)}
</For>
</div>
);
}
import { createSignal, For } from 'solid-js';
import './comp.css';

function makeDeferSrc(source: string) {
return (element: HTMLImageElement) => {
const task = () => {
if (!element.isConnected) return void setTimeout(task);

// element is connected to the DOM
element.src = source;
};

task();
};
}

export default function Comp() {
const [images, setImages] = createSignal<string[]>([
// …
]);

return (
<div class="masonry-gallery">
<For each={images()}>
{(img, index) => (
<div>
<img
ref={makeDeferSrc(img)}
onLoad={(event) => console.log(index(), event)}
/>
</div>
)}
</For>
</div>
);
}
Combining the ideas:
import { createSignal, For } from 'solid-js';
import './comp.css';

function deferredSrcLoad(element: HTMLElement) {
const task = () => {
if (!element.isConnected) return void setTimeout(task);

const list = element.querySelectorAll('img');
for (let i = 0; i < list.length; i += 1) {
const img = list.item(i);
const source = img.dataset.imageSource;
if (source) img.src = source;
}
};

task();
}

export default function Comp() {
const [images, setImages] = createSignal<string[]>([
// …
]);

return (
<div ref={deferredSrcLoad} class="masonry-gallery">
<For each={images()}>
{(img, index) => (
<div>
<img
data-image-source={img}
onLoad={(event) => console.log(index(), event)}
/>
</div>
)}
</For>
</div>
);
}
import { createSignal, For } from 'solid-js';
import './comp.css';

function deferredSrcLoad(element: HTMLElement) {
const task = () => {
if (!element.isConnected) return void setTimeout(task);

const list = element.querySelectorAll('img');
for (let i = 0; i < list.length; i += 1) {
const img = list.item(i);
const source = img.dataset.imageSource;
if (source) img.src = source;
}
};

task();
}

export default function Comp() {
const [images, setImages] = createSignal<string[]>([
// …
]);

return (
<div ref={deferredSrcLoad} class="masonry-gallery">
<For each={images()}>
{(img, index) => (
<div>
<img
data-image-source={img}
onLoad={(event) => console.log(index(), event)}
/>
</div>
)}
</For>
</div>
);
}

Did you find this page helpful?