Object Element Loading Is Weird
As the post implies the <object>
element has some…interesting rules about if it will load its data.
So in its simplest form we can make an <object>
load data by doing the following:
const obj = document.createElement("object")
obj.data = "<url>"
However, this will not load anything until you “connect” it to the DOM:
document.body.append(obj)
Now the browser will “fetch” the URL and either load or error.
The use case
Let’s talk about why I’m doing the above quickly.
In Rhino Editor
The “easiest” way to do this I thought was using an <object>
element.
PR: https://github.com/KonnorRogers/rhino-editor/pull/227
Issue: https://github.com/KonnorRogers/rhino-editor/issues/226
Where everything went wrong
So initially I tried to be clever and hide the <object>
element inside of a <template>
to prevent layout shifting, something like the following:
const template = document.createElement("template");
const obj = document.createElement("object");
template.append(obj);
obj.onload = () => {
template.remove()
this.progress = 100;
this.setUploadProgress();
this.element.dispatchEvent(new AttachmentUploadSucceedEvent(this));
this.element.dispatchEvent(new AttachmentUploadCompleteEvent(this));
};
obj.onerror = () => {
template.remove();
this.handleError();
};
obj.data = blobUrl;
// Needs to append to body for onerror / onload to fire.
document.body.append(obj);
As it turns out, Safari 17.5 will not load an <object>
element inside of a <template>
.
Firefox 132 and Chrome 131 will load an <object>
inside of a <template>
Trying display: none;
Okay. No biggie. Let’s try making it display: none;
we may hit CSP issues, but lets at least try it.
const obj = document.createElement("object");
obj.style.display = "none"
obj.onload = () => {
obj.remove()
this.progress = 100;
this.setUploadProgress();
this.element.dispatchEvent(new AttachmentUploadSucceedEvent(this));
this.element.dispatchEvent(new AttachmentUploadCompleteEvent(this));
};
obj.onerror = () => {
obj.remove();
this.handleError();
};
obj.data = blobUrl;
// Needs to body append to for onerror / onload to fire.
document.body.append(obj);
The following will not load in Chrome 131, Safari 17.5, but will load in Firefox 132.
Now I kind of expected this, but I was curious more than anything.
Why not just use fetch?
I tried it, and for some reason it still did not guarantee the file actually loaded. <object>
did however work with throttled connections.
<embed> , <iframe> , anything?
I also tried using the <embed>
and <iframe>
elements.
<embed>
will never fire the "load"
event for CSV files in Firefox / Chrome, even with proper mime types set.
<iframe>
will attempt to download the resource, which can be stopped with the sandbox
attribute, but in Chrome it never fires the load
event when I pass it a CSV.
Final thoughts
Browsers behaving slightly differently on this stuff is always annoying. I hope this saves someone some time in the future debugging why their <object>
won’t load.
It seems detecting uploads completed and are accessible via a loading element is a pipe dream, and fetch may just have to do, despite in my tests not always 100% working.