Search code examples
javascripthtmljquerynested-table

Form not submitting data from elements nested in a table


I have the following code run to get the values from dynamically created table elements. I can't get the input data when I submit the display (see console). I am unsure what to do, please advise!

console.clear();
var dimensions = [4, 4];
var controlType = [];

// empty and repopulate display with specified dimensions
function populate() {
    var table = $("#display > table > tbody");
    var tr = document.createElement("tr");
    var td = document.createElement("td");
    table.empty();

    let row = 0;
    for (let i = 0; i < dimensions[1]; i++) {
        row++;
        let column = 0;
        var newRow = $(tr).clone().appendTo(table);
        for (let i = 0; i < dimensions[0]; i++) {
            column++
            let position = row + "-" + column
            let newCell = $(td).clone().appendTo(newRow);
            $(newCell).append("<input type='button' name='" + position + "' class='on' form='display'>")
            $(newCell.children()).click(function () {
                let inputType = $("input:checked").val();
                if (inputType == "lights" && !$(this).hasClass("wall")) {
                    $(this).toggleClass("on off");
                } else if (inputType == "walls") {
                    $(this).toggleClass("wall");
                };
            });
        };
    };

    console.log("Populated display\nDimensions:", dimensions[0] + "x" + dimensions[1]);
};

// when document is ready
$(document).ready(function () {
    populate();

    // solve the puzzle input
    $("#display").submit(function (e) {
        e.preventDefault();

        let formData = $(this).serializeArray();
        console.log(formData);
    });

    // generate a dimensions list containing user input height and width on submit
    $("#options").submit(function (e) {
        e.preventDefault();

        let formData = $(this).serializeArray();
        formData.forEach(function (i) {
            let val = Math.round(i.value)
            if (i.name == "width") dimensions[0] = val;
            if (i.name == "height") dimensions[1] = val;
        });
        
        populate();
    });
});
:root {
    --font: Arial;
    --fs: 30px;
    --inputBg: whitesmoke;
    --border: 2px solid lightgrey;
}

/* always show arrow buttons on number input */
input[type=number]::-webkit-inner-spin-button,
input[type=number]::-webkit-outer-spin-button {
    opacity: 1;
}

/* body formatting */
body {
    width: auto;
    height: auto;
    font-family: var(--font);
    font-size: var(--fs)
}

/* lights out puzzle formatting */
#lightsOut {
    width: fit-content;
    height: auto;
    margin: 0px;
    border: var(--border);
    display: flex;
    gap: 0.2em;
}

/* display formatting */
#display {
    & table {
        height: fit-content;
        margin-top: 0.1em;
        margin-right: 0.1em;
        border-collapse: collapse;
        & tr {
            & td {
                padding: 0px;

                & input {
                    width: 1.5em;
                    height: 1.5em;
                    font-size: inherit;
                    margin-bottom: 0.1em;
                    margin-left: 0.1em;
                    padding: 0px;
                }
                & .on {
                    background-color: var(--inputBg);
                    border: var(--border);
                }
                & .off {
                    background-color: darkgrey;
                    border: 2px solid grey;
                }
                & .wall {
                    background-color: transparent;
                    border: 2px solid transparent;
                }
            }
        }
    }
    & span {
        width: 100%-var(--margin);
        height: 1.5em;
        display: flex;
        font-size: inherit;
        text-align: center;
        margin-bottom: 0.1em;
        margin-left: 0.1em;

        & input[type="submit"] {
            width: 100%;
            height: 1.5em;
            font-size: inherit;
            background-color: var(--inputBg);
            margin-right: 0.1em;
            border: var(--border);
        }
    }
}

/* user interface */
#ui {
    display: flex;
    flex-direction: column;
    margin-top: 0.1em;
    margin-right: 0.1em;

    /* options formatting */
    & #options,
    #controls {
        display: flex;
        flex-direction: column;

        & span {
            width: 100%;
            height: 1.5em;
            display: flex;
            justify-content: left;
            font-size: inherit;
            text-align: center;
            margin-bottom: 0.1em;
            margin-left: 0.1em;

            & label {
                width: 3.25em;
                height: 1.5em;
                font-size: inherit;
                margin-right: 0.5em;
                display: flex;
                align-items: center;
            }

            & input[type="number"] {
                width: 1.5em;
                height: 1em;
                font-size: inherit;
                padding: 0px;
                align-self: center;
            }

            & input[type="radio"] {
                width: 1em;
                height: 1em;
                font-size: inherit;
                margin-left: 0px;
                align-self: center;
            }

            & input[type="submit"] {
                width: 100%;
                height: 1.5em;
                font-size: inherit;
                background-color: var(--inputBg);
                margin-right: 0.1em;
                border: var(--border);
            }
        }
    }
}
<!DOCTYPE html>
<html>

<head>
    <link rel="stylesheet" href="lightsOut.css">
    <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
    <script src="lightsOut.js"></script>
</head>

<body>
    <div id="lightsOut">
        <form id="display">
            <table>
                <tbody>
                </tbody>
            </table>
            <span class="submit">
                <input value="Solve" type="submit">
            </span>
        </form>
        <span id="ui">
            <form id="options">
                <span class="number">
                    <label for="width">Width:</label>
                    <input name="width" id="width" type="number" value="4" min="3" max="15">
                </span>
                <span class="number">
                    <label for="height">Height:</label>
                    <input name="height" id="height" type="number" value="4" min="3" max="15">
                </span>
                <span class="submit">
                    <input value="Apply" type="submit">
                </span>
            </form>
            <form id="controls">
                <span class="radio">
                    <input name="type" id="lights" type="radio" value="lights" checked>
                    <label for="lights">Lights</label>
                </span>
                <span class="radio">
                    <input name="type" id="walls" type="radio" value="walls">
                    <label for="walls">Walls</label>
                </span>
            </form>
        </span>
    </div>
</body>

</html>

When you hit the Solve button, the console has an empty list instead of all the inputs. I tried adding a value to each input but that didn't help.


Solution

  • Here is a solution....

    It consists of parsing all the cells of the table to get the different values ​​of className and put them in an array.

    Sorry, I lost a little time to restore your interface...

    const
      formOptionsControls = document.querySelector('#options-controls')
    , lightsOut_Table     = document.querySelector('#lightsOut > table > tbody')
    , lightsOut_Solve     = document.querySelector('#lightsOut > table > caption')
      ;
    setTableDims();
    formOptionsControls.addEventListener('submit',setTableDims);
    
    lightsOut_Table.addEventListener('click',({target: TDx}) => 
      {
      if (!TDx.matches('td')) return;
    
      let xClassName = formOptionsControls.type.value === 'wall' ? 'wall' : 'off';
    
      TDx.classList.toggle(xClassName);
      // console.log( TDx.closest('tr').rowIndex, TDx.cellIndex );
      })
    
    lightsOut_Solve.addEventListener('click', e => 
      {
      // if (!formOptionsControls.checkValidity() ) return;
      let 
        xRows = lightsOut_Table.rows.length
      , xCols = lightsOut_Table.rows[0].cells.length
      , resp  = []
        ; 
      for( let r = 0; r < xRows; r++)
        {
        resp.push([]);
        for( let c = 0; c < xCols; c++)
          resp[r].push( lightsOut_Table.rows[r].cells[c].className || 'on');
        }
      console.clear();
      console.log( JSON.stringify({xRows,xCols}) );
      console.log( JSON.stringify(resp,0,2) );
      })
    
    function setTableDims(e)
      {
      if (!!e) e.preventDefault();
    
      let dimensions = [ formOptionsControls.width.valueAsNumber, formOptionsControls.height.valueAsNumber ];
      console.clear();
      console.log("Populated display\nDimensions:", dimensions[0] + "x" + dimensions[1]);
    
      while (lightsOut_Table.rows.length ) lightsOut_Table.deleteRow(-1);
    
      for (let r=0;r<formOptionsControls.height.valueAsNumber; r++)
        {
        let newRow = lightsOut_Table.insertRow();
        for(let c = formOptionsControls.width.valueAsNumber; c--;) newRow.insertCell();
        }
      }
    body {
      font-family : Arial, Helvetica, sans-serif;
      font-size   : 30px;
      }
    #lightsOut {
      border      : 0.1em solid lightgrey;
      display     : flex;
      gap         : 0.2em;
      width       : fit-content;
      line-height : 1.5em;
      }
    table {
      border-collapse : separate;
      border-spacing  : 0.1em;
      background      : white;
      height          : fit-content;
      }
    td, caption {
      cursor      : pointer;
      height      : 1.5em;
      background  : whitesmoke;
      box-sizing  : border-box;
      border      : 0.1em solid lightgrey;
      }
    td { 
      width     : 1.5em;
      }
    td.off {
      background   : darkgrey;
      border-color : grey;
      }
    td.wall {
      background   : white !important;
      border-color : white !important;
      }
    caption {
      caption-side : bottom;
      margin       : 0 .1em .1em .1em;
      }
    form#options-controls {
      width   : 6em;
      padding : .2em .2em 0 0;
      }
    label { 
      display : block;
      }
    label > input[type="number"] {
      font-size  : .9em;
      text-align : center;
      float      : inline-end;
      padding    : 0;
      width      : 2.3em;
      }
    button { 
      display    : block;
      width      : 6em;
      font-size  : 1em;
      padding    : 0;
      height     : 1.5em;
      background : whitesmoke;
      box-sizing : border-box;
      border     : 0.1em solid lightgrey;
      margin     : .1em 0 .3em 0;
      cursor     : pointer;
      }
    input[type="radio"] {
      width       : 1em;
      height      : 1em;
      font-size   : inherit;
      margin-left : .2em;
      align-self  : center;
      }
    label:has(input[type="radio"]) {
      cursor      : pointer;
      }  
    td:hover, caption:hover, button:hover {
      background  : #b5d11566;
      }
    <div id="lightsOut">
      <table>
        <caption> Solve </caption>
        <tbody></tbody>
      </table>
      <form id="options-controls">
        <label>
          Width:
          <input name="width" type="number" value="4" min="3" max="15" required>
        </label>
        <label>
          Height:
          <input name="height" type="number" value="4" min="3" max="15" required>
        </label>
        <button> Apply </button>
        <label>
          <input name="type" type="radio" value="light" checked >
          Lights 
        </label>
        <label>
          <input name="type" type="radio" value="wall" >
          walls 
        </label>
      </form>
    </div>