I'm trying to create a theme app extension for shopify to run some javascript when the add to cart button is clicked. As an example:
<head>
<script type="text/javascript" src={{ 'jquery-3.6.0.js' | asset_url }} defer></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js" rel="preload" defer="defer"></script>
</head>
...
<button id="add-btn">click me!</button>
<script>
jQuery( document ).ready(function() {
$('#add-btn').on('click', function(event){
alert("clicked")
})
});
</script>
This is within a sheet app extension in the blocks folder.
It seems like jquery isn't loading, and I get an error
Uncaught ReferenceError: jQuery is not defined
Any ideas why?
Summary: You are loading the jQuery library using the defer keyword so that it doesn't block the page load, but are referencing jQuery before the page finishes loading.
By loading jQuery using the defer attribute on the script tag, the browser will start loading the script immediately but won't execute the script until the DOM content has loaded:
<script type="text/javascript" src={{ 'jquery-3.6.0.js' | asset_url }} defer></script>
However, the inline script tag further down the page will be run immediately when the browser gets to it:
<script>
jQuery( document ).ready(function() { // ERROR! jQuery library hasn't been run yet, so we can't use jQuery to select the document!
$('#add-btn').on('click', function(event){
alert("clicked")
})
});
</script>
Since deferred scripts are executed in the order that they are defined, your instinct might be to try putting the defer keyword on the inline script tag - but unfortunately, defer and async only apply to external script files.
There are several ways that we could resolve this timing issue. Two possibilities are:
If you create an external JS file, you can load the script using the defer keyword as all deferred scripts execute in the order that they were called once the DOM is ready. As an added bonus, you wouldn't need to wrap your function with the jQuery(document).ready as you would already know that the document is ready when the script executes.
If the inline script tag makes sense, then using vanilla Javascript to set up your event will solve your issue. The equivalent code would be to use the addEventListener function to listen for the DOMContentLoaded event:
<script>
// This event is created before the DOM is loaded, so jQuery doesn't exist yet
document.addEventListener('DOMContentLoaded', function() {
// This event will be run after the deferred scripts above, so jQuery exists now
$('#add-btn').on('click', function(event){
alert("clicked")
})
});
</script>