I'm trying to append an iframe
element into document.body
as soon as possible.
Error reports are coming in across all 4 major browsers (Chrome, Safari, FF, IE --various versions), we see document.body
is null
. I think this happens when my JS file is cached and loads quickly.
Here is the logic to insert the iframe
:
private loadIFrame(): void {
switch (document.readyState) {
case 'uninitialized':
case 'loading':
case 'loaded':
window.addEventListener('DOMContentLoaded',
this.appendIFrame.bind(this)
)
break
case 'interactive':
case 'complete':
default:
this.appendIFrame()
}
}
private appendIFrame(): void {
if (document.getElementById('iframeId')) {
return
}
let iFrame: HTMLIFrameElement = document.createElement('iframe')
iFrame.src = document.location.protocol + this.ORIGIN + '/iframe.html'
iFrame.id = 'iframeId'
// document.body is null here
document.body.appendChild(iFrame)
}
I'm having a hard time reproducing the issue in a clean environment, which leaves me guessing how this happens out in the wild.
I originally tried this rreadyState
logic, but we saw document.body
was undefined
in IE when in the loading
state.
private loadIFrame(): void {
switch (document.readyState) {
case 'uninitialized':
case 'loading':
window.addEventListener('DOMContentLoaded',
this.appendIFrame.bind(this)
)
break
case 'loaded':
case 'interactive':
case 'complete':
default:
this.appendIFrame()
}
}
My current line of questioning....
default
case? Should I add the event listener there? I could modify the order of the cases so that the event listener is default? body
is null
on the DOMContentLoaded
event? document.readyState
that are falling through? First, if you are already checking the readyState
and have determined it to be loaded
, then why are you setting up an event handler for a moment that has already passed ( DOMContentLoaded
)?
You could just do:
private loadIFrame(): void {
switch (document.readyState) {
case 'uninitialized':
case 'loading':
case 'loaded':
this.appendIFrame.bind(this);
break;
case 'interactive':
case 'complete':
default:
this.appendIFrame();
}
}
Next, your event handler registration is wrong. There are 3 arguments to .addEventListener()
and the third can be one of two values:
The callback function (reference or inline function)
3a. Whether to hook into the capture phase (boolean - false
by default)
3b. An options object to configure characteristics (object)
And, addEventListener()
itself is called on the object that will receive the event ( window
in this case).
It should be:
window.addEventListener('DOMContentLoaded', function(){
this.appendIFrame.bind(this);
});
Also (FYI), you really should manually insert your end of statement semi-colons and not rely on automatic insertion as there are edge cases where that causes bugs.
I updated the logic, and I was able to almost eliminate the errors, but I am still seeing document.body
is null
when readyState
is interactive
.
I'll be updating this post once I assess the specific browser scenarios that cause the issue.
private loadIFrame(): void {
switch (document.readyState) {
case 'uninitialized':
case 'loading':
case 'loaded':
document.addEventListener('DOMContentLoaded',
this.appendIFrame.bind(this)
)
break
case 'interactive':
case 'complete':
default:
if(document.body) {
this.appendIFrame()
} else {
window.addEventListener('load',
this.appendIFrame.bind(this)
)
}
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.