简体   繁体   中英

Why is document.body null with my readyState logic?

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....

  1. Is the issue the default case? Should I add the event listener there? I could modify the order of the cases so that the event listener is default?
  2. Is it possible body is null on the DOMContentLoaded event?
  3. Is it possible there are other values of 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:

  1. The event name (string)
  2. 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM