Search code examples
htmlsuper-linter

How to conditionally add "checked" using a django template tag without causing htmlhint to throw an error?


I have 8 lines in my template where I have checkbox inputs. I save their checked state in a cookie and use a template tag to insert checked if it should be checked when the page initially loads. The code works just fine. It looks like this:

<td><input type="checkbox" id="showaverage" onclick="set_template_cookie('showaverage', this.checked ? 'checked' : '')" {% get_template_cookie selfmt 'showaverage' 'checked' %} /> Average</td>

However, when I run superlinter, I end up with the following errors regarding the above line:

<td><input type="checkbox" id="showaverage" onclick="set_template_cookie('showave...
    ^ Special characters must be escaped : [ < ]. (spec-char-escape)
...rage' 'checked' %} /> Average</td>
                       ^ Special characters must be escaped : [ > ]. (spec-char-escape)

Oddly, if I run the exact same version of htmlhint manually on the command line using the same .htmlhintrc config file, I get no error:

>npx htmlhint -c .htmlhintrc DataRepo/search/results/fcirc.html
npx: installed 32 in 3.793s

   Config loaded: .htmlhintrc

Scanned 0 files, no errors found (6 ms).

That config file contains:

{
  "tagname-lowercase": true,
  "attr-lowercase": true,
  "attr-value-double-quotes": true,
  "attr-value-not-empty": false,
  "attr-no-duplication": true,
  "doctype-first": false,
  "tag-pair": true,
  "tag-self-close": false,
  "spec-char-escape": true,
  "id-unique": true,
  "src-not-empty": true,
  "title-require": true,
  "alt-require": true,
  "doctype-html5": true,
  "id-class-value": false,
  "style-disabled": false,
  "inline-style-disabled": false,
  "inline-script-disabled": false,
  "space-tab-mixed-disabled": "space",
  "id-class-ad-disabled": false,
  "href-abs-or-rel": false,
  "attr-unsafe-chars": true,
  "head-script-disabled": true
}

...where you see "spec-char-escape": true,, so if it's going to error, it should error in both places...

I don't understand why superlinter's htmlhint complains and mine doesn't, but I tried to silence superlinter's version by adding:

<!-- htmlhint spec-char-escape:false -->

But I still get the error. Can anyone advise me on how to avoid the error?


ADDENDUM: I just tried working around this issue by using a conditional, inside which I either have checked or not, but when I do that, it complains about the ID needing to be unique. Observe:

Work-around for the issue above:

                    <td>
                        {% get_template_cookie selfmt 'showaverage' 'checked' as avgchkd %}
                        {% if avgchkd == "checked" %}
                            <input type="checkbox" id="showaverage" onclick="set_template_cookie('showaverage', this.checked ? 'checked' : '')" checked />
                        {% else %}
                            <input type="checkbox" id="showaverage" onclick="set_template_cookie('showaverage', this.checked ? 'checked' : '')" />
                        {% endif %}
                        Average
                    </td>

New problem:

<input type="checkbox" id="showaverage" onclick="set_template_cookie('showaverage',...
                      ^ The id value [ showaverage ] must be unique. (id-unique)

Solution

  • htmlhint is not written to be used with templating frameworks. It will largely ignore template tags wrapped in quotes, which generally isn't a problem until you conditionally need an attribute inside an html element/tag. And what's worse, it will apply rules to the template tag, such as uniqueness of an ID, so to get around these issues without turning off otherwise useful htmlhint rules, you have to figure out a work-around.

    Here's a work-around I got to solve this particular problem. It's a lot more code and complexity to solve what should be a simple problem, so if anyone has a more elegant solution, please share.

    To get around the conditionally added checked attribute spec-char-escape error from htmlhint, this works (as I noted in my addendum in the question):

    <td>
        {% get_template_cookie selfmt 'showaverage' 'checked' as avgchkd %}
        {% if avgchkd == "checked" %}
            <input type="checkbox" id="showaverage" onclick="set_template_cookie('showaverage', this.checked ? 'checked' : '')" checked />
        {% else %}
            <input type="checkbox" id="showaverage" onclick="set_template_cookie('showaverage', this.checked ? 'checked' : '')" />
        {% endif %}
        Average
    </td>
    

    but that causes another problem: the id-unique error. And to get around that, I added an otherwise pointless template tag:

    @register.simple_tag
    def uniquify(retval, unused):
        return retval
    

    Then you can make the ID's seem unique to htmlhint with:

    <td>
        {% get_template_cookie selfmt 'showaverage' 'checked' as avgchkd %}
        {% if avgchkd == "checked" %}
            <input type="checkbox" id="{% uniquify 'showaverage' 1 %}" onclick="set_template_cookie('showaverage', this.checked ? 'checked' : '')" checked />
        {% else %}
            <input type="checkbox" id="{% uniquify 'showaverage' 2 %}" onclick="set_template_cookie('showaverage', this.checked ? 'checked' : '')" />
        {% endif %}
        Average
    </td>