2

To improve performance of site I updated my javascript references to use defer, such as:

<script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous" defer></script>
<script src="https://ajax.aspnetcdn.com/ajax/jquery.validate/1.16.0/jquery.validate.min.js" defer></script>
<script src="https://ajax.aspnetcdn.com/ajax/jquery.validation.unobtrusive/3.2.6/jquery.validate.unobtrusive.min.js" defer></script>
<script src="js/plugins.js" defer></script>
<script src="js/main.js" defer></script>

This did improve page load performance. Unfortunately a side-effect is that jquery function that handled the form post no longer was done using AJAX, it tried to reload the whole page which resulted in just the returned content appearing on a freshed page, opposed to just updating a DIV.

Code:

<script>
    $(function () {
        $('form').submit(function () {
            if ($(this).valid()) {
                $.ajax({
                    url: this.action,
                    type: this.method,
                    data: $(this).serialize(),
                    success: function (result) {
                        $('#result').html(result);
                        document.getElementById('contactForm').style.display = "none";
                    }
                });
            }
            return false;
        });
    });
</script>

How do I update this jquery code to work correctly when the scripts are deferred?

crenshaw-dev
  • 7,504
  • 3
  • 45
  • 81
Josh
  • 8,219
  • 13
  • 76
  • 123
  • Don't defer jQuery. Everything relies on that being loaded first. While [there are ways to get around this](https://stackoverflow.com/questions/5852767/possible-to-defer-loading-of-jquery) they're, frankly, clunky and overcomplicated for most situations. – Rory McCrossan Apr 30 '18 at 14:31
  • @RoryMcCrossan you can in fact defer jQuery. You just have to make sure dependencies are deferred as well. – crenshaw-dev Apr 30 '18 at 14:32
  • How do make the function defer? – Josh Apr 30 '18 at 14:33
  • @mac9416 I'd be interested to hear your thoughts on how that could possibly work. If everything is deferred, you have no control over loading order; that's its entire point. – Rory McCrossan Apr 30 '18 at 14:33
  • @Josh you already are; with the `defer` attribute on the ` – Rory McCrossan Apr 30 '18 at 14:34
  • Acutally you can use async and have control over the loading order but if I post a link here they will just bash and down vote me. – ibrahim tanyalcin Apr 30 '18 at 14:35
  • @Josh unlike the `async` attribute, `defer` [preserves order](https://www.html5rocks.com/en/tutorials/speed/script-loading/). – crenshaw-dev Apr 30 '18 at 14:35
  • @ibowankenobi no we won't. Comments are the correct place for links. – Rory McCrossan Apr 30 '18 at 14:35
  • @mac9416 not in a reliable, cross browser way it doesn't. IE (as with most things) messes it up. – Rory McCrossan Apr 30 '18 at 14:37
  • There is a small library I wrote for async called taskq (http://taskq.ibrahimtanyalcin.com) to use it in your case, https://medium.com/@ibowankenobi/get-that-google-psi-score-higher-28a7c992966e . You might not want to use this, in that case why can't you go for requestAnimationFrame loop until $('form') is defined? – ibrahim tanyalcin Apr 30 '18 at 14:38
  • @ibowankenobi `async` executes immediately after the script is downloaded, ignoring order. `defer` executes in order immediately before `DOMContentLoaded`. – crenshaw-dev Apr 30 '18 at 14:38
  • @RoryMcCrossan source? Which version(s) of IE misbehave? – crenshaw-dev Apr 30 '18 at 14:39
  • 1
    Yes yes but I used a technique to trap function calls within async and execute them in order. This way the scripts are no longer render blocking because they are trapped within requestAnimationFrame + Promise (if supported, degrades to double rAF in ie9) – ibrahim tanyalcin Apr 30 '18 at 14:39
  • 1
    @mac9416 < IE10. Source: https://caniuse.com/#search=defer (see 'Known issues' tab). Also from your own link `IE < 10 says: I might execute 2.js halfway through the execution of 1.js. Isn’t that fun??` – Rory McCrossan Apr 30 '18 at 14:42
  • Removing defer from jquery fixed the issue. Lighthouse performance audit didn't notice a Performance impact. – Josh Apr 30 '18 at 14:43
  • @Josh glad you got it working. – Rory McCrossan Apr 30 '18 at 14:43
  • @RoryMcCrossan, thx for help. If you want to post it as an answer I credit you. – Josh Apr 30 '18 at 14:44
  • Which link whose link? – ibrahim tanyalcin Apr 30 '18 at 14:45
  • @Josh no problem, added it for you – Rory McCrossan Apr 30 '18 at 14:46

2 Answers2

1

I'd suggest not using defer on the jQuery.js reference. jQuery plugins rely on the main jQuery library being loaded first.

While there are ways to get around this they're, frankly, clunky and overcomplicated for most situations.

Rory McCrossan
  • 331,213
  • 40
  • 305
  • 339
1

In your case jQuery can be deferred easily, if you're okay with breaking compatibility for some (very small number of) browsers. There are several options:

1) Control execution order with an event handler

Use a non-jQuery method to attach to DOMContentLoaded. i.e. instead of

$(function() {
    // handle form work
});

You would use

document.addEventListener("DOMContentLoaded", function(event) {
    // handle form work
});

The second snippet accomplishes the same thing as $(function() {}) but does so without requiring jQuery to be loaded while the page is being parsed. So when the listener executes, jQuery will be available.

Attaching to DOMContentLoaded is supported by 98% of browsers. (Though you're still using defer, which limits you to 95% support, max.)

There are also polyfills for older browsers.

2) Control execution order by moving some code out of the page

You could move your $('form') event handler into an external script, deferred to load after jQuery. As Rory pointed out, defer has some compatibility issues. But at 95% support, it's still a pretty good option.

3) Control execution order by bundling

As Rory pointed out, you could also bundle (and minify, for better performance) and defer all your JS. That would ensure execution order (the browser would execute the single JS file from top to bottom). Browsers that don't support defer may not receive all the performance benefits, but at least the JS could not be broken by mis-ordering separate scripts.

Cross-origin, blocking, external scripts can be a huge performance drain. You'll have to weigh browser compatibility vs. performance. But if your target audience has mostly modern browsers, I would strongly suggest trying to defer jQuery.

crenshaw-dev
  • 7,504
  • 3
  • 45
  • 81
  • 1
    Good answer. I'd also suggest that a third option would be to bundle & minify your JS files on the server and include a single reference to them which can then be deferred without concern over execution order. – Rory McCrossan Apr 30 '18 at 15:50