2

Question: is there anything wrong with using the following method to defer running code until after jQuery has loaded (or until another fixed point)?

Bonus question: is there a better way to do it? (E.g. not having to name the functions in window.deferred, like window.deferred.afunction)

In my page (before jQuery):

<script>
window.deferred = window.deferred || {};
window.deferred.afunction = function() {
  console.log('afunction is running');
};

window.deferred.anotherfunction = function() {
  console.log('anotherfunction is running');
};
</script>

In my JS (after jQuery when I'm ready to execute my deferred functions):

/* Run deferred functions */
if(window.deferred && typeof window.deferred === 'object') {
  for (var fn in window.deferred) {
    if (typeof window.deferred[fn] == 'function') {
        window.deferred[fn]();
    }
  }
}

Demo: https://jsfiddle.net/j4sqy5kf/4/

As far as I can tell it works fine. But I consider myself amateur at JavaScript - I can write it but don't always understand how it's working.

So far all the ways I've found to do this (e.g. http://snipplr.com/view/54863/) rely on having setTimeout or setInterval and calling a check function, which seems quite inelegant to me.

There's also Require.js, which I believe could be used, but I think it's overkill to achieve exactly what I want as it primarily serves a different purpose.

Background

I'm writing a post for my blog in Markdown, and I'm writing a little JavaScript inside the page to manage some hidden content. I want to use jQuery in my script, but jQuery is loaded in the footer - so, I want an elegant way to defer execution of my inline script until after jQuery has loaded.

I have jQuery in my footer and a general.js script, in which most of the rest of my JavaScript runs. I'd like to run the 'In my page' bits inside each blog post, and leave the 'In my JS' bit in general.js so I never have to worry about it again.

I know I could define but not execute the functions, but I don't want to have to update general.js each time (e.g. add function1(), function2(), etc.)

I want it to automatically loop through all deferred functions.

2 Answers2

0

An easier way of achieving what you want is by using DOMContentLoaded.

<script>
  window.addEventListener('DOMContentLoaded', function() {
    $( document ).ready(function() {
      console.log('my other scripts including jquery has loaded now');
    });
  });
</script>

But I would stop and think about the reason for preferring inline scripts instead of your general.js, as inline scripts has a negative impact on performance, and prevents caching. If it is code separation you're after, you can achieve it by other means, depending on your setup.

Community
  • 1
  • 1
DJ_Icebear
  • 203
  • 2
  • 8
  • At the moment, this is the only instance of this code that I will use on my blog (this page only) so I thought it would be better to inline the script than to add it to my general.js, which runs on every page. When you say prevents caching, you mean the inline script won't be cached across multiple pages right (as it would if it were an external script)? You don't mean it will block caching of the page it's running on..? Does DOMContentLoaded wait for external scripts to load? It would be neater but I imagine there might still be situations where my code would be useful. – Jamie Goodwin May 07 '17 at 13:56
0

I don't really see anything wrong with doing it this way. I would however use an array instead of an object:

/* Run deferred functions */
if(window.deferred && Array.isArray(window.deferred)) {
  window.deferred.forEach(function (fn) {
  if (typeof fn === 'function') {
        fn();
    }
  });
}
.hello-world {
  color: #f00;
  font-size: 3em;
  font-family: verdana;
}

.jq {
  color: #00f;
}
<script>
window.deferred = window.deferred || [];
window.deferred.push(function aFunction() {
  console.log('aFunction is running');
});

window.deferred.push(function anotherfunction () {
  console.log('anotherfunction is running');
});

window.deferred.push(function doSomethingWithJquery () {
  jQuery('<div class="hello-world">Hello from <span class="jq">jQuery</span>!</div>').appendTo('body');
});

</script>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

If you ever are looping over an object with for...in like your original code does, you should use hasOwnProperty to check that the object actually contains that property and you are not getting a property from the prototype chain.

Useless Code
  • 12,123
  • 5
  • 35
  • 40
  • 1
    That's much neater, and if I use an array I can avoid naming the functions so I won't accidentally overwrite them: ```window.deferred.push(function() {``` – Jamie Goodwin May 07 '17 at 13:39