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?

Michael Crenshaw
  • 4,412
  • 2
  • 33
  • 59
Josh
  • 7,099
  • 11
  • 65
  • 109

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
  • 306,214
  • 37
  • 269
  • 303
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.

Michael Crenshaw
  • 4,412
  • 2
  • 33
  • 59
  • 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