0

I've been having trouble loading connect.facebook.net/en_GB/all.js. I'm not sure if it's happening to anyone else, but this script takes forever to load (sometimes doesn't load). It doesn't even timeout. I have a PHP script which loads a few scripts asynchronously as shown below. Even after doing this, the problem persists. document.readyState remains as "interactive" as long as the script is loading, so my domcontentloaded event never fires.

This problem occurs randomly on different sites, computers and browsers that I have tried in the past few days.

<script>(function(d, s, f){
    var a = d.getElementsByTagName(s)[0], i = f.length;
    while (i--) {
        j = d.createElement(s);
        j.src = f[i];
        j.async = true;
        a.parentNode.insertBefore(j, a);
    }
}(document, 'script', ['//connect.facebook.net/en_GB/all.js', '//platform.twitter.com/widgets.js', '//www.google-analytics.com/ga.js']));</script>

This is the resulting script tag:

<script src="//connect.facebook.net/en_GB/all.js" async=""></script>
1
  • The problem is, as soon as the page reaches readyState == "interactive" , it has already completed loading so you won't get any events any more even after the script may finally have loaded. Please note that since you are marking the script to be loaded as asynchronous, that isn't taken into account any more when determining a page's readyState, but you should still be able to check any status code for the script in the browser's console. If you want to make sure that you still get the events, see my polyfill below.
    – Robidu
    Commented Mar 3, 2024 at 14:16

2 Answers 2

0

Since I haven't had any response for a while, I'll just say what I did. I still couldn't solve the problem but I had a workaround. Rather than loading the script immediately, I loaded it after the DOM loaded. My DOMContentLoaded would fire but onload still wouldn't fire.

0

This problem can be circumvented with a polyfill:

(function (window, document, undefined) {
const orig_add_ev_listener = Object.getOwnPropertyDescriptor(EventTarget.prototype, 'addEventListener').value;

var dom_avail = false;
var page_loaded = false;

function exec_event(p_event, p_handler, p_param)
  {
var l_install_hand = true;
var l_custom_ev;

  if(typeof p_event != 'string')
    throw new TypeError('string expected');
  if(typeof p_handler != 'function')
    throw new TypeError('function expected');
  if(typeof p_param == 'undefined')
    p_param = { capture: false };
  if(typeof p_param == 'boolean')
    p_param = { capture: p_param };
  if(typeof p_param != 'object' || Array.isArray(p_param))
    throw new TypeError('boolean or non-array object expected');

  switch(p_event)
    {
    case 'DOMContentLoaded':
      if(this == document && dom_avail)
        {
        l_custom_ev = new Event('DOMContentLoaded');
        l_install_hand = false;
        }
      break;

    case 'load':
      if(this == window && page_loaded)
        {
        l_custom_ev = new Event('load');
        l_install_hand = false;
        }
      break;
    }
  if(l_install_hand)
    orig_add_ev_listener.call(this, p_event, p_handler, p_param);
  else
    queueMicrotask(p_handler.bind(this, l_custom_ev));
  }

document.addEventListener('DOMContentLoaded', p_event => { dom_avail = true; }, { once: true });
window.addEventListener('load', p_event => { page_loaded = true; }, { once: true });
Object.defineProperty(EventTarget.prototype, 'addEventListener', { value: exec_event });
})(window, document);

What this polyfill is doing is simple: The original function addEventListener is stored, two flags for the two events that we are interested in (i. e. DOMContentLoaded and load) and a wrapper function to the EventTarget.prototype.addEventListener are created. After this two event handlers are registered, each of which gets invoked on a different event from the list that we are interested in. Finally the native addEventListener is replaced by our wrapper function.

So whenever you are loading a script asynchronously, the wrapper essentially checks which state the document is currently in when attaching one of the two problematic event handlers.

  1. The document is still loading.
    If the asynchronously loaded script is started during this stage (that is, neither the DOMContentLoaded nor the load event have fired so far), any handlers to these two events are registered as per usual so they get invoked when the browser is firing the events.
  2. The DOM is available, but not all assets have been loaded.
    Now the DOMContentLoaded event already has fired so an event handler registered there won't be triggered any more.
    What we are doing now is artificially create a DOMContentLoaded event, bind this and the newly created event to the event handler that we have received and add it to the browser's microtask queue. So once the thread that called addEventListener completes, the added handler is immediately executed.
  3. The document has completed loading.
    Here both events have already fired so any registered handlers for either of the two won't be executed any more. In order to handle this problem, both events are added to the microtask queue as per #2.

If you are registering any other events, those calls are passed straight to the native addEventListener function.

With this polyfill (make sure to load this script synchronously as the very first script in this list!) you can now add async scripts without even having to modify them. That is, if you have a script that you are loading synchronously or with a delay (i. e. with the defer attribute set), but you decide to switch to asynchronous loading later on, all you need to do is change the attribute without any need to rerig the scripts.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.