alpine: [Bug] x-for should generate valid/compliant html when used on elements such as lists, tables, selects etc

Currently the x-for directive requires you to wrap the element in a <template> tag, however, this produces invalid HTML when used with <ul>, <ol>,<table>, <select> etc etc.

As the HTML spec say only <li> elements can live within a ol, or ul - they cannot accept <template> (which also messes up css like first-child)

E.g. you’re required to do…

<ul>
    <template x-for="item in items">
        <li x-text="item"></li>
    </template>
</ul>

This obviously isn’t valid, although Vue/React/etc can get around this by their virtual dom as they remove the <template> tags during rendering I guess?

So it actually produces

<ul>
    <template x-for="item in items">
         <li x-text="item">d</li>
    </template>
    <li x-text="item">1</li>
    <li x-text="item">2</li>
    <li x-text="item">3</li>
    <li x-text="item">4</li>
</ul>

Although I understand this is probably quite a tricky problem, lists are the fundamental reason for loops such as x-for and you’d expect to be able to do this without side effects, maybe even accessibility issues?

The same problem is present when building HTML tables, another common use case for x-for

<table>
   <template x-for="item in items">
        <tr>
            <td x-text="item"></td>
        <tr>
    </template>
</table>

which outputs

<table>
    <template x-for="item in items">
        <tr>
            <td x-text="item"></td>
        </tr>
    </template>
    <tr>
        <td x-text="item">1</td>
    </tr>
    <tr>
        <td x-text="item">2</td>
    </tr>
    <tr>
        <td x-text="item">3</td>
    </tr>
    <tr>
        <td x-text="item">4</td>
    </tr>
</table>

Same for selects etc…

<select>
	<template x-for="item in items">
		<option x-text="item"></option>
	</template>
	<option x-text="item">1</option>
	<option x-text="item">2</option>
	<option x-text="item">3</option>
	<option x-text="item">4</option>
</select>

Mozilla actually provide an <template>, with tables where the template is defined outside of the markup to make sure this doesn’t happen: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template - maybe something like this could be investigated to solve this issue?


If this is going to get marked as a wont-fix would it at a minimum to be acceptable within the documentation under the x-for to have an obvious warning explaining that lists are not supported in a “compliant” fashion to save developers brains from trying to figure something out that doesn’t work.

Thanks for all the hard work, is much appreciated 😃

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 3
  • Comments: 18 (2 by maintainers)

Most upvoted comments

I can imagine the x-template syntax will be a massive source of user error.

In a perfect world, Alpine could go through the DOM and remove all templates while keeping track of their contents somehow… sounds like a bunch of work though.

It’s an interesting point but still a bit unclear to me.

Where do you immagine the loop declaration to be (the point where you tell alpine which variable to use for the list and which one for the element)?

Still an x-for on the template tag or somewhere else? If template i think it would be complicate to ignore it later in the dom when alpine meet the template tag again?

Theoretically the template should also leave inside the tag having x-data since components shouldn’t know about other part of the pages (this is more a design point than a real constraint).

Sorry I missed that in the reduced example(scroll up, I’ve edited it). The x-for is still within the x-data component.

So it would still be i. The x-data just not inside the element that doesn’t support the template tag. Effectively it references it instead.

I’m gonna try knock up a basic proof of concept and see what happens 🤷🏻‍♂️