dk21
dk21

Reputation: 21

How to create a html/javascript table that automatically selects radiobuttons?

I have only very basic html/javascript understanding.

I am trying to make what is probably only a slight modification to an existing code. Below is, first the original code, and then how I'd like to change it.

Original code: A table gives the user a choice between left and right for several rows. The table enforces at most a single switch point from left to right going down the table, meaning that if the user chooses right at some row, the table automatically selects right for all rows below, and if the user chooses left at a given row, the table automatically selects left for all rows above. No switch point at all is allowed (meaning left is selected for all rows, or right is selected for all rows). A selection is required for each row (the app checks for this by default already).

{{ block content }}

    <table class="table table-striped">
        <colgroup>
            <col width="45%">
            <col width="10%">
            <col width="45%">
        </colgroup>
        <tr>
            <td align="right"><b>Option A</b></td>
            <td></td>
            <td align="left"><b>Option B</b></td>
        </tr>
        {{ for amount in player.right_side_amounts }}
            <tr>
                <td align="right">
                    <b>{{ player.left_side_amount }}</b> now
                <td align="middle">
                    <input type="radio"
                           value="left"
                           name="{{ amount|json }}"
                           required>&nbsp;&nbsp;
                    <input type="radio"
                           name="{{ amount|json }}"
                           value="right" data-amount="{{ amount|json }}"
                           required>
                </td>
                <td align="left">
                    <b>{{ amount }} </b> next month
            </tr>
        {{ endfor }}
    </table>

    {{ formfield_errors 'switching_point' }}
    <input type="hidden" name="switching_point" id="id_switching_point"
           value="9999">

    {{ next_button }}

{{ endblock }}


{{ block scripts }}
    <script>
        $(document).ready(function () {
            $('input[type=radio]').change(
                function () {
                    var clickedRadio = this;
                    var afterClickedRadio = false;


                    var radios = document.querySelectorAll('input[type=radio]');
                    for (i = 0; i < radios.length; ++i) {
                        var radio = radios[i];
                        if (radio === clickedRadio) {
                            afterClickedRadio = true;
                            continue;
                        }
                        if (!afterClickedRadio && clickedRadio.value === 'left' && radio.value === 'left') {
                            radio.checked = true;
                        }
                        if (afterClickedRadio && clickedRadio.value === 'right' && radio.value === 'right') {
                            radio.checked = true;
                        }
                    }
                }
            );

            $('.otree-btn-next').click(function () {
                var radios = document.querySelectorAll('input[type=radio]');


                for (i = 0; i < radios.length; ++i) {
                    var radio = radios[i];
                    if (radio.value === 'right' && radio.checked) {
                        $('#id_switching_point').val(radio.dataset.amount);
                        break;
                    } else {
                        $('#id_switching_point').val(9999);
                    }
                }
            });
        });
    </script>

{{ endblock }}

This is how the original table looks: original table

Modification: Just as in the original, I'd like to have the table with a left/right choice, and enforce at most a single switch point. But, I'd like user to have the possibility to express indifference between the left and right choice at the switch point.

In other words, at the row the user chooses to switch from option A to option B, he could choose either "I prefer B" or "I am exactly indifferent between A and B".

I envision a table with three radio buttons per row, where the center button means “I am indifferent”. At whichever row the user chooses to switch ($17 in this image: what I envision) the user could select either the center button (which would express exact indifference) or the right button (which would express preference for option B). Regardless of whether the user chooses center or right button, all rows above would get checked for A, and all rows below would get checked for B. The center button can be selected at most once, since all rows below would be automatically selected right, and all rows above would be automatically selected left.

I think a second variable would need to be created relative to the original code, to record not just the switching point, but also whether the switching point occurred with indifference or strict preference.

Thank you so much for any help.

Upvotes: 2

Views: 63

Answers (1)

user6702203
user6702203

Reputation:

How's this? If I understood you correctly, then the rules are:

  1. Selecting A checks all A inputs
  2. Selecting B checks all B inputs
  3. Selecting X will make all inputs below the X selection to be the opposite of the first checked input and everything above the X selection defaults to the first checked input selection. (If A, then B after the X. If B, then A after the X)
  4. Prevents selecting more than one X. If more than X, the last checked X will prevail, the previous X changes according to rule 3.

In addition to those rules, I disabled the first and last middle inputs because you didn't say what happens if the first or last middle inputs are checked. Also, I added logic that prevents the user from checking any middle input before anything else is selected since you didn't say what happens in that situation either.

There are some edge cases I didn't code for, but this is more than enough to cover your rules.

https://jsfiddle.net/j1vas2x8/

<table>
  <tr>
    <th>A</th>
    <th>X</th>
    <th>B</th>
  </tr>
  <tr>
    <td><input type="radio" name="r1[]" value="A"></td>
    <!-- if the first middle selection is not disabled, then which column is the opposite of it? -->
    <td><input type="radio" name="r1[]" value="X" disabled="disabled"></td>
    <td><input type="radio" name="r1[]" value="B"></td>
  </tr>
  <tr>
    <td><input type="radio" name="r2[]" value="A"></td>
    <td><input type="radio" name="r2[]" value="X"></td>
    <td><input type="radio" name="r2[]" value="B"></td>
  </tr>
  <tr>
    <td><input type="radio" name="r3[]" value="A"></td>
    <td><input type="radio" name="r3[]" value="X"></td>
    <td><input type="radio" name="r3[]" value="B"></td>
  </tr>
  <tr>
    <td><input type="radio" name="r4[]" value="A"></td>
    <td><input type="radio" name="r4[]" value="X"></td>
    <td><input type="radio" name="r4[]" value="B"></td>
  </tr>
  <tr>
    <td><input type="radio" name="r5[]" value="A"></td>
    <!-- if the last middle selection is not disabled, then what should should happen if it's selected last? -->
    <td><input type="radio" name="r5[]" value="X" disabled="disabled"></td>
    <td><input type="radio" name="r5[]" value="B"></td>
  </tr>
</table>
var header_row = document.querySelectorAll('table tr th');
var column = {
  'left': header_row[0].innerText,
  'middle': header_row[1].innerText,
  'right': header_row[2].innerText
};
var radios = document.querySelectorAll('input[type="radio"]');
var rows = document.querySelectorAll('table tr');
Array.from(radios).forEach(function(radio) {
  radio.addEventListener('click', function(event) {
    var is_more_than_one_middle_column_selected = document.querySelectorAll('input[type="radio"][value="' + column.middle + '"]:checked').length > 1;
    if (is_more_than_one_middle_column_selected === true) {
      // loops through all radio inputs with the middle column checked
      Array.from(document.querySelectorAll('input[type="radio"][value="' + column.middle + '"]:checked')).forEach(function(input) {
        if (input !== event.target) {input.checked = false;}
      });
    }
    var current_row_index = Array.from(rows).findIndex(function(row) {
      var current_input = Array.from(row.querySelectorAll('td input[type="radio"]')).find(function(input) {
        return input === event.target;
      });
      return !!current_input;
    });
    var middle_selected_input_row_index = Array.from(rows).findIndex(function(row) {
      return row.querySelector('input[type="radio"][value="' + column.middle + '"]')?.checked === true;
    });
    var is_middle_input_selected = middle_selected_input_row_index > -1;
    let first_input_column = rows[1].querySelector('input[type="radio"]:checked')?.value || '';
    // if the first input has not been checked but a middle input somewhere else has
    if (!first_input_column && is_middle_input_selected === true) {
      // uncheck the current input, and stop the script here; if script keeps going it will run into null errors
      return event.target.checked = false;
    }
    for (var row_index = 1; row_index < rows.length; row_index++) {
      // if the middle input is not checked yet
      if (is_middle_input_selected === false) {
        // whatever selection the current value, change all inputs to that
        rows[row_index].querySelector('input[type="radio"][value="' + event.target.value + '"]').checked = true;
      }
      else if (is_middle_input_selected === true && row_index < middle_selected_input_row_index) {
        // check the previous input to whatever the first checked input value was
        rows[row_index].querySelector('input[type="radio"][value="' + first_input_column + '"]').checked = true;
      }
      else if (is_middle_input_selected === true && row_index > middle_selected_input_row_index) {
        // check the previous input to whatever the first checked input value was
        rows[row_index].querySelector('input[type="radio"][value="' + (first_input_column === column.left ? column.right : column.left) + '"]').checked = true;
      }
      // if the current input checked was the input that triggered this logic, and a there was more than one middle input that was checked
      else if (row_index === current_row_index && is_more_than_one_middle_column_selected === true) {
        // get the first checked input value
        let first_input_column = rows[1].querySelector('input[type="radio"]:checked').value;
        // check the previous input to whatever the first checked input value was
        rows[row_index - 1].querySelector('input[type="radio"][value="' + first_input_column + '"]').checked = true;
      }
    }
  });
});

Upvotes: 1

Related Questions