htmlthymeleafnoscript

Thymeleaf breaks the HTML before ´noscript´ tag


Considering this markup:

<!DOCTYPE HTML>
<html>
<head>
    <meta charset="UTF-8">
    <title>Page title</title>
    <link rel="stylesheet" href="css/layouts.css" type="text/css" media="all">
        <noscript>
        <link rel="stylesheet" href="css/layouts_nojs.css" type="text/css" media="all">
    </noscript>
    <script src="js/jquery.min.js" charset="utf-8"></script>
</head>
<body>

I'm using Thymeleaf as the engine, but the output code results as this:

<!DOCTYPE HTML>
<html>
<head>
    <meta charset="UTF-8">
    <title>Page title</title>
    <link rel="stylesheet" href="css/layouts.css" type="text/css" media="all">
 </head>
 <body>
    <noscript>
         <link rel="stylesheet" href="css/layouts_nojs.css" type="text/css" media="all">
    </noscript>
    <script src="js/jquery.min.js" charset="utf-8"></script>

How can I prevent this behaviour?


Solution

  • Thymeleaf last version, 2.1.3, use nekohtml library, this library for some versions previous to 1.9.14 has a bug that avoid the use of noscript in head, so probably you have using an older version, review your dependencies.

    But, this is not all, all superior versions don't allow within head, the use of elements with children, so noscript in head can not support link tag inside it. Using your code the result must be this:

    <!DOCTYPE HTML>
    <html>
    <head>
        <meta charset="UTF-8">
        <title>Page title</title>
        <link rel="stylesheet" href="css/layouts.css" type="text/css" media="all">          
        <noscript>             
        </noscript><link rel="stylesheet" href="css/layouts_nojs.css" type="text/css" media="all">
        <script src="js/jquery.min.js" charset="utf-8"></script>
    </head>
    <body>
    

    This issue is more complicated to be resolved, it requires deeper changes.

    Workarounds:

    1 - Use a custom LegacyHtml5TemplateParser where you can set "http://cyberneko.org/html/features/parse-noscript-content" feature to false in the HTMLConfiguration of nekohtml library, this make not to parse the content of noscript elements, so is treated as text, to make use of dynamic code of thymeleaf, set th:inline attribute to text in the noscript start tag and use the markup inside like in
    Tutorial: Using Thymeleaf, 12.1 Text inlining

    An example:

    <!DOCTYPE HTML>
    <html>
    <head>
        <meta charset="UTF-8">
        <title>Page title</title>
        <link rel="stylesheet" href="css/layouts.css" type="text/css" media="all">     
        <noscript th:inline="text">
           <link rel="stylesheet" href="[[@{css/layouts_nojs.css}]]" type="text/css" media="all">  
        </noscript>
        <script src="js/jquery.min.js" charset="utf-8"></script>
     </head>
     <body>
    

    2 - Another possibility, more complex, needs to use a custom LegacyHtml5TemplateParser for thymeleaf, and a custom HTMLTagBalancer for nekohtml, so you can control if head can contained elements with children. This involves creating a serial of classes to override the nekohtml behaviour, these classes are the following:

    HTMLAugmentations, HTMLConfiguration, HTMLScanner, HTMLTagBalancer, LostText, ObjectFactory, SecuritySupport, SecuritySupport12.

    You must copy the original sources from nekohtml library in your package an modify them accordingly to this package changes, and in LegacyHtml5TemplateParser use your new HTMLConfiguration class.

    For this workaround, in HTMLTagBalancer, change these lines:

    if ((fElementStack.top > 1 && fElementStack.peek().element.code == HTMLElements.SCRIPT) 
    || (fElementStack.top > 2 && fElementStack.data[fElementStack.top-2].element.code == HTMLElements.HEAD)) {
    

    with these others:

    if (fElementStack.top > 1 && fElementStack.peek().element.code == HTMLElements.SCRIPT) {
    //|| (fElementStack.top > 2 && fElementStack.data[fElementStack.top-2].element.code == HTMLElements.HEAD)) {
    

    Pros: lets you define th:block tags within head for thymeleaf.

    Cons: if you want to change nekohtml library version to a newer one, you must ensure that the changes do not affect your code.