Thymeleaf 学习笔记

1. Thymeleaf介绍

​ Thymeleaf是一个流行的模板引擎,是基于HTML的,语法应用在HTML标签中。该模板引擎采用java语言开发。

​ 模板引擎是做视图层工作的,在服务器端将Controller传过来的数据替换掉模板上的静态数据。(模板+数据=展示页面) Java生态下的模板引擎有:Thymeleaf、Freemaker、Velocity、Beetl等。

​ Spring Boot框架集成了Thymeleaf,并且Spring Boot官方也推荐使用Thymeleaf来替代jsp技术。Thymeleaf 是另外的一种模板技术,它本身并不属于 Spring Boot,Spring Boot只是很好地集成这种模板技术,作为前端页面的数据展示,在过去的 Java Web 开发中,我们往往会选择使用 Jsp 去完成页面的动态渲染,但是 jsp 需要翻译编译运行,效率低。

Thymeleaf 的官方网站: http://www.thymeleaf.org

Thymeleaf 官方手册: https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html

2. 入门案例

1、创建Spring Boot项目,添加web和Thymeleaf起步依赖

<!--thymeleaf起步依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

2、在resources/templates目录下创建index.html

​ 1)在html根元素添加命名空间声明:xmlns:th="http://www.thymeleaf.org"

​ 2)在html标签内引用thymeleaf的语法:th:text="${data}",接收key为data的数据,替换所修饰标签内的文本内容。

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"><!--1.添加命名空间声明-->
    <meta charset="UTF-8">
    <title>Title</title>
</head>
    <!--2.引用thymeleaf语法-->
    <h2 th:text="${data}">测试数据</h2>
</body>
</html>

3、创建Controller

package com.tsccg.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class ThymeleafController {
    @GetMapping("/find")
    public String find(Model model) {
        //向请求作用域中写入数据
        model.addAttribute("data","Hello Thymeleaf");
        //返回视图
        return "index";

4、启动应用,在浏览器中发送请求: http://localhost:8080/find

3. 模板引擎的常用设置

我们在前面的入门案例中,Controller方法返回的是逻辑视图名称,但我们并没有设置视图解析器,也能访问到页面。这是因为Thymeleaf在核心配置文件中有一些默认的设置,如下:

##Thymeleaf默认设置
#缓存 默认为true,但在开发阶段为了修改立即生效,需设置为false
spring.thymeleaf.cache=false
#模板使用的字符编码格式
spring.thymeleaf.encoding=UTF-8
#模板类型  默认为HTML,模板是html文件
spring.thymeleaf.mode=HTML
#模板视图前缀 默认设置为 classpath:/templates/
spring.thymeleaf.prefix=classpath:/templates/
#模板视图后缀 默认为 .html
spring.thymeleaf.suffix=.html

4. 表达式

表达式就是在页面中获取数据的一种thymeleaf语法,类似于el表达式的${key}

4.1 标准变量表达式

语法:${key}

作用:获取请求作用域中key对应的文本数据

实例演示:

以前面的入门案例为基础进行修改。

①创建Student类

package com.tsccg.pojo;
public class Student {
    private String name;
    private Integer age;
    private String email;
	//无、有参构造
    //get set
    //toString

②修改index.html页面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
    <meta charset="UTF-8">
    <title>Title</title>
</head>
    <h1>标准变量表达式</h1>
    <h2>获取简单类型数据</h2>
    <p th:text="${data}"></p>
    <h2>获取对象属性值</h2>
    <p th:text="${student.name}"></p>
    <p th:text="${student.age}"></p>
    <p th:text="${student.email}"></p>
    <p th:text="${student.getEmail()}"></p>
</body>
</html>

③修改Controller

@Controller
public class ThymeleafController {
    @RequestMapping("/findStudent")
    public String findStudent(Model model) {
        Student student = new Student("张三",20,"zhangsan@qq.com");
        model.addAttribute("student",student);
        model.addAttribute("data","Hello Thymeleaf");
        return "index";

④开启应用,在浏览器访问 http://localhost:8080/findStudent

4.2 选择变量表达式

语法:*{key}

说明:需要配合th:object一起使用。使用th:object属性来绑定对象,然后使用*来代表这个对象,后面{} 中的值是此对象中的属性。

作用:获取key对应的对象属性值

实例演示:

①在resources/templates目录下新建select.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
    <meta charset="UTF-8">
    <title>Title</title>
</head>
    <h1>选择变量表达式</h1>
    <div th:object="${student}">
        <p th:text="*{name}"></p>
        <p th:text="*{age}"></p>
        <p th:text="*{email}"></p>
    <p th:text="*{student.email}"></p>
</body>
</html>

②在Controller中新添对应方法

@RequestMapping("/select")
public String select(Model model) {
    Student student = new Student("李四",22,"lisi@qq.com");
    model.addAttribute("student",student);
    return "select";

③开启应用,在浏览器访问 http://localhost:8080/select

4.3 链接表达式

语法:@{url}

作用:主要用于链接、地址的展示,可用于<script src="..."><link href="..."><a href="..."><form action="..."><img src="">等,可以在 URL 路径中动态获取数据

实例演示:

以入门案例为基础进行修改。

①在resources/templates目录下创建 link.html

使用Thymeleaf的链接表达式发送各种请求

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
    <meta charset="UTF-8">
    <title>Title</title>
</head>
    <h1>链接表达式</h1>
    <a th:href="@{http://www.baidu.com}">链接绝对地址</a>
    <a th:href="@{/get}">链接相对地址,无参</a>
    <a th:href="@{'/get?id=' + ${id}}">链接相对地址,有参</a>
    <a th:href="@{/getStudent(id=${id},name=${name})}">传参数方式 2</a>
    <form th:action="@{'/student/' + ${id}}" method="post">
        <input type="hidden" name="_method" value="DELETE">
        <input type="submit" value="RESTful风格DELETE请求">
    </form>
    <form th:action="@{/student/{id}(id=${id})}" method="post">
        <input type="hidden" name="_method" value="DELETE">
        <input type="submit" value="RESTful风格DELETE请求 方式2">
    </form>
</body>
</html>

②在Controller中添加方法

@RequestMapping("/link")
public String link(Model model) {
    model.addAttribute("id",1001);
    model.addAttribute("name","Tom");
    return "link";
@RequestMapping("/get")
@ResponseBody
public String get(Integer id) {
    return "查询学生,id=" + id;
@RequestMapping("/getStudent")
@ResponseBody
public String getStudent(Integer id,String name) {
    return "查询学生,id=" + id + ",name=" + name;
@DeleteMapping("/student/{id}")
@ResponseBody
public String restFul(@PathVariable Integer id) {
    return "执行删除操作,id=" + id;

③在核心配置文件中开启HiddenHttpMethodFilter过滤器

#开启HiddenHttpMethodFilter过滤器
spring.mvc.hiddenmethod.filter.enabled=true

④开启应用,在浏览器发送请求 http://localhost:8080/link

5. Thymeleaf属性

Thymeleaf的大部分属性和html元素的属性一样。在html元素属性前加上th:前缀之后,属性的作用不变,属性的值由模板引擎处理。

在Thymeleaf属性中可以使用变量表达式。

5.1 常用基本属性

1、th:action

作用同form表单标签的action属性,主要结合链接表达式,获取动态变量。

<form th:action="@{/tmp/doSome(id=${id})}" th:method="post" >...</form>

2、th:method

同form表单的method属性。

<form th:action="@{/tmp/doSome(id=${id})}" th:method="post" >...</form>

3、th:href

定义超链接,主要结合链接表达式,获取动态变量。

<a th:href="@{/getStudent(id=${id},name=${name})}">传参数方式 2</a>

4、th:src

同html的src属性,用于引入外部资源,常与链接表达式结合使用。

在Spring Boot项目中,所有静态资源都放到resources的static目录下,引如此目录下的资源时,路径上不需要写上static。

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

5、th:text

用于文本的显示,该属性显示的文本在标签体中,如果是文本框,数据会在文本框外显示,要想显示在文本框内显示,需要使用 th:value。

<h3 th:text="${title}"></h3>
<input type="text" th:text="${preText}" name="username" th:value="${username}"  >

6、 th:style

<div th:style="'color: red'"></div>

实例演示使用:

新建一个Spring Boot项目,添加web和thymeleaf起步依赖

①在templates目录下创建模板 test.html

在html元素属性前添加th:前缀

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" src="../static/js/jquery.min.js"></script>
    <script type="text/javascript">
        function fun() {
            alert("hello");
    </script>
</head>
<center>
    <h3 th:text="${title}"></h3>
    <div th:style="'color: red'">
        <form th:action="@{/tmp/doSome(id=${id})}" th:method="post" >
            <input type="text" th:name="username" th:value="${username}"><br>
            <input type="submit" th:value="提交"><br>
        </form>
        <input type="button" th:attr="value='按钮'" th:onclick="fun()"><br>
</center>
</body>
</html>

②创建Controller方法

@RequestMapping("/test")
public String test(Model model) {
    model.addAttribute("title","Thymeleaf属性");
    model.addAttribute("id","1001");
    model.addAttribute("username","Tom");
    return "test";

③启动应用,访问 http://localhost:8080/test

5.2 th:each 遍历集合

th:each用于遍历后台传过来的集合。

①创建Controller方法

向请求作用域中存入一个List集合,集合中的元素是Student对象。

@RequestMapping("/each")
public String each(Model model) {
    List<Student> studentList = new ArrayList<>();
    studentList.add(new Student("张三",19,"zhangsan@qq.com"));
    studentList.add(new Student("李四",20,"lisi@qq.com"));
    studentList.add(new Student("王五",21,"wangwu@qq.com"));
    studentList.add(new Student("赵六",22,"zhaoliu@qq.com"));
    model.addAttribute("studentList",studentList);
    return "each";

②创建页面

在templates目录下创建each.html,使用Thymeleaf的th:each属性将集合中的数据遍历展示在表格中。

<center>
    <table cellpadding="0" cellspacing="0" border="1" width="300px">
        <thead>
                <th>序号</th>
                <th>下标</th>
                <th>姓名</th>
                <th>年龄</th>
                <th>邮箱地址</th>
                <th>是否为第一行</th>
                <th>姓名2</th>
        </thead>
        <tbody th:each="student,itemStat:${studentList}">
                <td th:text="${itemStat.count + '/' + itemStat.size}"></td>
                <td th:text="${itemStat.index}"></td>
                <td th:text="${student.name}"></td>
                <td th:text="${student.age}"></td>
                <td th:text="${student.email}"></td>
                <td th:text="${itemStat.first}"></td>
                <td th:text="${itemStat.current.name}"></td>
        </tbody>
    </table>
</center>

语法说明:th:each="student,itemStat:${studentList}"

  • ${studentList}:后台传过来的待遍历集合。
  • student:自定义变量,代表当前迭代元素。
  • itemStat:自定义变量,代表当前循环体的信息,通过该变量可以获取如下信息
  • index:当前迭代元素的下标(从0开始算)
  • count:当前迭代元素的个数(从1开始算)
  • size:集合中元素的数量
  • even/odd:布尔值,当前循环是否是偶数/奇数(从0开始算)
  • first:布尔值,当前迭代元素是否是集合中第一个元素
  • last:布尔值,当前迭代元素是否是集合中最后一个元素
  • current:获取当前迭代元素,即student
  • 注意:上面代表循环体信息的itemStat也可以不自己命名,默认采用自定义迭代元素名加上Stat后缀,即studentStat

    ③开启应用,在浏览器访问 http://localhost:8080/each

    ④遍历map

    当遍历value为对象的map集合时,需要根据迭代元素的value属性获取对象的数据

    <div th:each="m,mSt:${users}">
        <p th:text="${mSt.count}"></p>
        <p th:text="${m.key}"> </p>
        <p th:text="${m.value.id}"></p>
        <p th:text="${m.value.name}"></p>
    

    5.3 if 条件判断

  • th:if="boolean条件" ,条件为true时显示所修饰标签里的内容
  • th:unless="boolean条件",条件为false时显示所修饰标签里的内容
  • ①写Controller方法

    @RequestMapping("/if")
    public String iF(Model model) {
        model.addAttribute("age",20);
        model.addAttribute("sex",0);
        model.addAttribute("name","");
        model.addAttribute("result",null);
        return "ifUnless";
    

    ②在templates目录下创建ifUnless.html

    <p th:if="${age} >= 18"> 1、当年龄大于等于18 <p th:unless="${age} >= 18"> 2、当年龄小于18 <p th:if="${sex} == 0"> 3、当性别为女 <p th:if="${sex} == 1"> 4、当性别为男 <p th:if="${result}"> 5、当值为null <p th:if="${name}"> 6、当值为空字符串“” </body>

    ③开启应用,在浏览器访问 http://localhost:8080/if

    可见,当值为""时,也表示true。

    5.4 switch case 条件判断

    ①模板页面

    <div th:switch="${sex}">
        <p th:case="0">女</p>
        <p th:case="1">男</p>
        <p th:case="*">人中龙凤</p>
    

    一旦某个case匹配为true,其他case都会被当作false。

    *表示默认的case,当其他所有case都匹配失败时,执行*

    ②处理器方法

    @RequestMapping("/switch")
    public String switchCase(Model model) {
        model.addAttribute("sex",null);
        return "switch";
    

    ③浏览器访问

    5.5 inline 内联

    th:inline有三个取值类型:text、javascript、none。

    5.5.1 内联text

    内联text可以让Thymeleaf表达式不依赖于html标签,单独使用。类似于vue的{{data}}

    <div th:inline="text"><!--要求在父级标签上加 th:inline="text" 属性,但不加也能用-->
        <p>你好,[[${name}]]</p>
    

    实例演示:

    ①模板页面

    <h3>内联text</h3>
    <div th:inline="text">
        <p>你好,[[${name}]]</p>
    <h3>不加 th:inline="text"也能用</h3>
        <p>你好,[[${name}]]</p>
    

    ②处理器方法

    @RequestMapping("/inlineText")
    public String inlineText(Model model) {
        model.addAttribute("name","阿伟");
        return "inlineText";
    

    ③页面显示

    5.5.2 内联javascript

    内联javascript可以在js语句中,通过模板语法获取请求作用域中的数据。

    ①模板页面

    <button onclick="fun1()">按钮</button>
    <script type="text/javascript" th:inline="javascript">
        var name = [[${name}]];
        function fun1() {
            alert(name);
    </script>
    

    ②处理器方法

    @RequestMapping("/inlineJs")
    public String inlineJs(Model model) {
        model.addAttribute("name","杰哥");
        return "inlineJs";
    

    ③页面显示

    5.6 字符串连接

    在Thymeleaf中,有两种方式连接字符串:

    1)使用单引号把字符串括起来,用+连接其他字符串或表达式

    <p th:text="'a' + ${data} + 'b'"></p>
    

    2)使用双竖线将所有字符串和表达式括起来,字符串无需加单引号

    <p th:text="|a${data}b|"></p>
    

    实例演示:

    ①模板页面

    <h3>字符串连接方式1:'a' + ${data} + 'b'</h3>
    <p th:text="'我的名字是' + ${name} + ',年龄' + ${age}"></p>
    <h3>字符串连接方式2:|a${data}b|</h3>
    <p th:text="|我的名字是${name},年龄${age}|"></p>
    

    ②处理器方法

    @RequestMapping("/connStr")
    public String connStr(Model model) {
        model.addAttribute("name","汤姆");
        model.addAttribute("age","20");
        return "connStr";
    

    ③页面显示

    5.7 内置对象

    Thymeleaf模板引擎内置了一组内置的对象,常用的如下:

  • #request表示HttpServletRequest 请求作用域对象
  • #session表示HttpSession 会话作用域对象
  • session表示Map对象的, 是#session的简单表示方式, 用来获取session中指定的key的值
  • 实例演示:

    ①处理器方法

    @RequestMapping("/innerObj")
    public String innerObj(HttpServletRequest request, HttpSession session) {
        request.setAttribute("reqData","请求作用域中的数据");
        session.setAttribute("sessionData","会话作用域中的数据");
        return "innerObj";
    

    ②模板页面

    <h3>获取作用域中的数据</h3>
    <p th:text="${#request.getAttribute('reqData')}"></p>
    <p th:text="${#session.getAttribute('sessionData')}"></p>
    <p th:text="${session.sessionData}"></p>
    <h3>使用内置对象的方法</h3>
    getRequestURL=<span th:text="${#request.getRequestURL()}"></span><br/>
    getRequestURI=<span th:text="${#request.getRequestURI()}"></span><br/>
    getQueryString=<span th:text="${#request.getQueryString()}"></span><br/>
    getContextPath=<span th:text="${#request.getContextPath()}"></span><br/>
    getServerName=<span th:text="${#request.getServerName()}"></span><br/>
    getServerPort=<span th:text="${#request.getServerPort()}"></span><br/>
    

    ③页面展示

    5.8 内置工具类对象

    模板引擎提供了一组功能性内置对象,可以在模板中直接使用这些对象提供的功能方法。工作中常用的数据类型,如集合、时间、数值等都可以使用这些工具类对象来处理。

    官方手册:http://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html

    工具类对象前都需要加#,一般都以s结尾。

    常用的工具类对象:

  • #dates: 处理日器的工具类
  • #numbers:处理数字
  • #lists: 处理list集合
  • 实例演示:

    ①处理器方法

    @RequestMapping("/innerUtil")
    public String innerUtil(Model model) {
        model.addAttribute("myDate",new Date());//日期
        model.addAttribute("myNum",26.695);//数字
        model.addAttribute("myStr","tsccg");//字符串
        //list集合
        List<String> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        list.add("c");
        model.addAttribute("myList",list);
        //对象,带有null属性
        Student student = new Student();
        student.setName("张三");
        model.addAttribute("student",student);
        return "innerUtil";
    

    ②模板页面

    <h3>日期类对象 #dates</h3>
    #dates.format(myDate) == <span th:text="${#dates.format(myDate)}"></span><br>
    #dates.format(myDate,'yyyy-MM-dd') == <span th:text="${#dates.format(myDate,'yyyy-MM-dd')}"></span><br>
    #dates.format(myDate,'yyyy-MM-dd HH:mm:ss') == <span th:text="${#dates.format(myDate,'yyyy-MM-dd HH:mm:ss')}"></span><br>
    #dates.year(myDate) == <span th:text="${#dates.year(myDate)}"></span><br>
    #dates.month(myDate) == <span th:text="${#dates.month(myDate)}"></span><br>
    #dates.monthName(myDate) == <span th:text="${#dates.monthName(myDate)}"></span><br>
    #dates.createNow() == <span th:text="${#dates.createNow()}"></span>
    <h3>内置工具类#numbers,操作数字的</h3>
    #numbers.formatCurrency(myNum) == <span th:text="${#numbers.formatCurrency(myNum)}"></span><br>
    #numbers.formatDecimal(myNum,5,2) == <span th:text="${#numbers.formatDecimal(myNum,5,2)}"></span>
    <h3>内置工具类#strings,操作字符串</h3>
    #strings.toUpperCase(myStr) == <span th:text="${#strings.toUpperCase(myStr)}"></span><br>
    #strings.indexOf(myStr,'b') == <span th:text="${#strings.indexOf(myStr,'s')}"></span><br>
    #strings.substring(myStr,2,5) == <span th:text="${#strings.substring(myStr,2,5)}"></span><br>
    #strings.substring(myStr,2) == <span th:text="${#strings.substring(myStr,2)}"></span><br>
    #strings.concat(myStr,'---Hello String---') == <span th:text="${#strings.concat(myStr,'---Hello String---')}"></span><br>
    #strings.length(myStr) == <span th:text="${#strings.length(myStr)}"></span><br>
    #strings.length('hello') == <span th:text="${#strings.length('hello')}"></span><br>
    #strings.isEmpty(myStr) == <span th:unless="${#strings.isEmpty(myStr)}"> myStr不是空字符串  </span>
    <h3>内置工具类#lists,操作list集合</h3>
    #lists.size(myList) == <span th:text="${#lists.size(myList)}"></span><br>
    #lists.contains(myList,'a') == <span th:if="${#lists.contains(myList,'a')}">有成员a</span><br>
    !${#lists.isEmpty(myList)} == <span th:if="!${#lists.isEmpty(myList)}"> list 集合有多个成员</span>
    <h3>处理null</h3>
    student?.name == <span th:text="${student?.name}"></span><br>
    student?.email == <span th:text="${student?.email}"></span><br>
    

    ③页面展示

    5.9 内容复用

    为了将页面中重复的内容提取出来,我们可以创建自定义模板,用Thymeleaf的语法在其它页面中引用模板内容。

    1.定义模板

    语法:th:fragment="top",自定义模板名称为 top

    如:head.html

    <div th:fragment="top">
            www.upanddown.com
    

    2.引用模板

    引用语法:

  • ~{templateName :: selector},引用selector所包含的部分
  • templateName :: selector,引用selector所包含的部分
  • templateName :: html,引用整个模板页面
  • templdate,引用整个模板页面
  • 其中,templdateName指的是模板文件名称,selector指的是自定义模板名称

    有两种引用方式:

  • 插入模板:<div th:insert="~{head :: top}"></div>
  • 包含模板:<div th:include="head :: top"></div>
  • 3.实例演示

    ①定义模板

    在templates目录下分别创建如下页面:

    1)head.html

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <div th:fragment="top" style="color: red">
        <center>
                www.upanddown.com
        </center>
    </body>
    </html>
    

    2)footer.html

    <!DOCTYPE html>
    <html lang="en">
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <div align="center">
            www.upanddown.com
            @copy; tsccg 2021
    </body>
    </html>
    

    3)创建目录common,在其中创建left.html

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
        <div align="left" th:fragment="menu">
            <p>会员管理</p>
            <p>商品管理</p>
            <p>销售管理</p>
    </body>
    </html>
    

    ②在另一个页面中引用自定义模板

    main.html

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <div align="center">
        <h2>插入模板 th:insert</h2>
        <div th:insert="~{head :: top}">
            第一种方式
        <div th:insert="head :: top">
            第二种方式
        <h2>包含模板 th:include</h2>
        <div th:include="~{head :: top}">
        <div th:include="head :: top">
        <h2>引用多级目录的模板文件</h2>
        <div th:insert="common/left :: menu"></div>
        <h2>引用整个模板文件</h2>
        <div th:include="footer :: html"></div>
        <div th:include="footer"></div>
    </body>
    </html>
    

    ③处理器方法

    @RequestMapping("/useModel")
    public String useModel() {
        return "main";
    

    ④查看页面

    查看页面源代码:

    <!DOCTYPE html> <html lang="en"> <meta charset="UTF-8"> <title>Title</title> </head> <div align="center"> <h2>插入模板 th:insert</h2> <div style="color: red"> <center> www.upanddown.com </center> <div style="color: red"> <center> www.upanddown.com </center> <h2>包含模板 th:include</h2> <center> www.upanddown.com </center> <center> www.upanddown.com </center> <h2>引用多级目录的模板文件</h2> <div align="left"> <p>会员管理</p> <p>商品管理</p> <p>销售管理</p> <h2>引用整个模板文件</h2> <meta charset="UTF-8"> <title>Title</title> </head> <div align="center"> www.upanddown.com @copy; tsccg 2021 </body> <div><!DOCTYPE html> <html lang="en"> <meta charset="UTF-8"> <title>Title</title> </head> <div align="center"> www.upanddown.com @copy; tsccg 2021 </body> </html> </body> </html>