I have some Javascript code inside a Twig template that uses jQuery. The script seems to be loading before jQuery, so it throws a $ is not defined
error. I can't figure out why it's loading before the main bundle that includes jQuery (compiled with webpack-encore
).
JQuery does load because I can reference it from the console or wrap the script inside a setTimeout
to force it to be loaded later.
I have this base template:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>
{% block title %}Welcome!
{% endblock %}
</title>
{% block stylesheets %}
{{ encore_entry_link_tags('app') }}
{% endblock %}
{% block javascripts %}
{{ encore_entry_script_tags('app') }}
{% endblock %}
</head>
<body>
{% block body %}{% endblock %}
</body>
</html>
The page template:
{% extends 'base.html.twig' %}
{% block body %}
/// ...
<script>
$.change(/*...*/); // $ is not defined
</script>
{% endblock %}
webpack.config.js
:
const path = require('path');
const Encore = require('@symfony/webpack-encore');
if (!Encore.isRuntimeEnvironmentConfigured()) {
Encore.configureRuntimeEnvironment(process.env.NODE_ENV || 'dev');
}
Encore
.setOutputPath('public/build/')
.setPublicPath('/build')
.enableSassLoader()
.addEntry('app', './assets/app.js')
.enableStimulusBridge('./assets/controllers.json')
.splitEntryChunks()
.enableSingleRuntimeChunk()
.cleanupOutputBeforeBuild()
.enableBuildNotifications()
.enableSourceMaps(!Encore.isProduction())
.enableVersioning(Encore.isProduction())
.configureBabel((config) => {
config.plugins.push('@babel/plugin-proposal-class-properties');
})
.configureBabelPresetEnv((config) => {
config.useBuiltIns = 'usage';
config.corejs = 3;
})
.autoProvidejQuery()
;
module.exports = Encore.getWebpackConfig();
app.js
:
require('./styles/app.scss');
import $ from 'jquery';
global.$ = global.jQuery = $;
This was the generated HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="/build/app.css">
<script src="/build/runtime.js" defer></script>
<script src="/build/app.js" defer></script>
</head>
<body>
<main class="col p-4 content flex-grow-1">
<!-- page content -->
<script>
// $ is not defined here
</script>
</main>
</body>
</html>
The problem was found in these lines from the generated HTML:
The
defer
attribute forces the Javascript files where jQuery is defined, to be loaded after the page is rendered. Since I'm adding inline Javascript in the page, it's loaded beforeapp.js
.Since there is no way to add deferred inline Javascript in HTML, there are two alternatives:
config/packages/webpack_encore.yaml
:This disables the behavior described. This solution is not ideal because using
defer
is desirable in my case.encore_entry_script_tags
).Save your Javascript file in
assets/
and, if you have a similar base template as the one in the question, import it in your page template like this:In
webpack.config.js
: