How to add javascript script to thymeleaf fragment

I am currently learning to use thymeleaf and javascript. I want to create a header, and when click on the user’s avatar a dropdown menu will show.

The problem I am countering is when I add <script> tag to my header fragment, when I click on the button, the console give me ReferenceError, function not found.

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<head>
    <meta charset="UTF-8">
</head>
<body>
<header th:fragment="header" class="global-header">
<!--    Logged in show the user's avatar-->
    <div class="profile bulge-icon"  sec:authorize="isAuthenticated()">
        <button class="profile-btn" id="profile-btn" th:onclick="dropdown()">
            <img class="img" alt="user profile picture"  th:src="@{images/profile-placeholder.png}"/>
        </button>

        <div id = "dropdown-menu">
            <form th:action="@{/logout}" method="post">
                <button type = "submit" class="sign out bulge-icon" aria-label="sign out">Sign Out</button>
            </form>
        </div>
    </div>
</header>

<script type="text/javascript" th:src="@{/js/jquery/header.js}"></script>
</body>
</html>

To make this js work, I need to add .js file to my page that uses the header fragment. For example, my test.html.

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Test</title>
  <link rel="stylesheet" href="https://pro.fontawesome.com/releases/v5.15.3/css/all.css" integrity="sha384-iKbFRxucmOHIcpWdX9NTZ5WETOPm0Goy0WmfyNcl52qSYtc2Buk0NCe6jU1sWWNB" crossorigin="anonymous">
  <link rel="stylesheet" type="text/css" media="all" th:href="@{/css/header.css}"/>
</head>
<body>
    <header th:replace="global/header :: header"></header>
    <script type="text/javascript" th:src="@{/js/jquery/header.js}"></script>
</body>
</html>

Is there a method to let .js works when <script> tag is put in my header.html. So in this way, I don’t need to add header.js to every page that use the header fragment.

Answer

When you use th:replace, Thymeleaf will literally take the contents of that <header> element and place it in your main page. That JavaScript line in your header fragment will never be used.

The way I usually handle this is using Thymeleaf Layout Dialect so that all needed script tags are included on every page. See https://ultraq.github.io/thymeleaf-layout-dialect/ for info on how to use it.

For example, create a layout.html file like this:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      lang="en">
<head>
    <meta charset="UTF-8">
<link rel="stylesheet" href="https://pro.fontawesome.com/releases/v5.15.3/css/all.css" integrity="sha384-iKbFRxucmOHIcpWdX9NTZ5WETOPm0Goy0WmfyNcl52qSYtc2Buk0NCe6jU1sWWNB" crossorigin="anonymous">
  <link rel="stylesheet" type="text/css" media="all" th:href="@{/css/header.css}"/>

<div layout:fragment="page-content">
</div>

<script type="text/javascript" th:src="@{/js/jquery/header.js}"></script>
</head>

</html>

Now update test.html to use it:

<!DOCTYPE html>
<html
        xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
        layout:decorate="~{layout}">
<head>
    <title>Test</title>
</head>
<body>
<div layout:fragment="page-content">
    <header th:replace="global/header :: header"></header>
    <!-- Add more content for the test page here -->
</div>
</body>
</html>

If the header should be on all pages, you can also move it into layout.html.