Radio Buttons in a Repeating Section

A question that comes up from time to time on the roll20 forums, is how can I have a radio button that spreads across every row of a repeating section? At the time of writing, here’s the most recent example.

You’d think this was easy, given the way that radio buttons work, but the way repeating sections work creates a problem.

How Do Radio Buttons Work?

Radio buttons are HTML that allow you to make a single choice from several options. Let’s say you have four buffs, and you want to choose which one is active:

<input type="radio" name="attr_buff" value="-">Boost Nothing</input>
<input type="radio" name="attr_buff" value="strength">Boost Strength</input>
<input type="radio" name="attr_buff" value="dexterity">Boost Dexterity</input>
<input type="radio" name="attr_buff" value="wisdom">Boost Wisdom</input>
<input type="radio" name="attr_buff" value="charisma">Boost Charisma</input>Code language: HTML, XML (xml)

Notice these are inputs of type radio, and all share the same name. The character sheet will show them as connected, and if you click any one, it gains the checked property and the listed value, and any other radio inputs of the same name are unchecked.

It’s handy to have a nothing state or null state, because as soon as you check one, you might want to uncheck it, and without a separate button to do that, it’s hard.

Repeating Section Name sand the Problem

Radio buttons will work even spread across the same sheet. Any input of type radio with the same name is considered part of the same radio group, and clicking an of them will unclick all others.

Now, lets look at repeating section names. Each row might have something like

        <fieldset class="repeating_aspects">
            <input name="attr_description" type="text"  class="desc" >
            <select name="attr_dice" class="select_dice">
                <option value="0" selected>-</option>
                <option value="4"         >D4</option>
                <option value="6"         >D6</option>
                <option value="8"         >D8</option>
                <option value="10"        >D10</option>
                <option value="12"        >D12</option>
            </select>
            <input type="radio" value="1" name="attr_selected" >
        </fieldset>Code language: HTML, XML (xml)

Notice that last input – it’s a radio button. You might thing you can click on that, and all other selected inputs will be unselected. But that won’t work.

Let’s remember what a repeating section is, in roll20. It is a template, telling roll20 character sheets how to build that section. the part you write will never actually be part of the character sheet. Instead, for each row, it will create an attribute name made of three parts:

  1. The repeating section name, for instance, repeating_aspects.
  2. A unique id for the row, which looks something like -sfgd68G
  3. And finally, the name you list above, for example dice or selected.

So that final radio input will have a name that looks something like repeating_aspects_-sfgd68G_selected.

The problem is that row id, because roll20 deliberately tries to make it unique (and has good reasons for this).

So the actual name of that last item might be repeating_aspects_-sfgd68G_selected, and when roll20 looks for a matching unique name it will find none, so this is always a radio group with just one item.

Solving The Problem With Checkboxes

The way I solve this problem is by using a checkbox and a ssheet worker. The HTML will look like this:

        <fieldset class="repeating_aspects">
            <input name="attr_description" type="text"  class="desc" >
            <select name="attr_dice" class="select_dice">
                <option value="0" selected>-</option>
                <option value="4"         >D4</option>
                <option value="6"         >D6</option>
                <option value="8"         >D8</option>
                <option value="10"        >D10</option>
                <option value="12"        >D12</option>
            </select>
            <input type="checkbox" value="1" name="attr_selected" >
        </fieldset>Code language: HTML, XML (xml)

See how it is almost the same. Now in the sheet wortker, whenever that checkbox is clicked, we need to identify which row was clicked, and uncheck all other matching checkboxes.

on('change:repeating_aspects:selected', event_info => {
   const row_selected = event_info.sourceAttribute.split('_')[2];
   getSectionIDs('repeating_aspects', rows => {
      const output = {};
      rows.forEach(id =< {
         if(id === row_selected) {
            \\ do nothing
         } else { 
            output[`repeating_aspects_${id}_selected`] = 0;
         }
      });
      setAttrs(output);
   })
});Code language: JavaScript (javascript)

And there we have it. Whenever an item named selected in the repeating section changes, this worker is triggered. Thanks to usign the event_info object, some information about the attribute that just changed is saved. Part of that information is the sourceAttribute, which is the attributes full name, which looks like repeating_aspects_-sfgd68G_selected.

From that we went to get the row id so we can identify the id of row which was just clicked. Luckily, these names always follow the same format, and the row id is always after the second underscore and before the 3rd, so we can split on underscores and get the id that way.

Then we loop throw every row and make sre all other checkboxes are not checked. And that’s it. A bit more laborious than a standard radio button would be, but pretty simple once you know what to do.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.