dynamic tables

by Giesenhausener

HTML

<!DOCTYPE html>

<html lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>tableTest</title>
    <link rel="stylesheet" href="stylesheet.css">
    <script src="javascript.js" defer></script>
</head>
<body>
    <header>
    </header>
    <div class="mainWrapper">
        <main>
            <h1>TableTest</h1>
            <h2>table ingredients</h2>
            <details id ="ingredients"> 
                <summary>Ingredients (click to open)</summary>
                <table id="ingredientsTable">
                    <tbody id="ingredientsTableBody">
                        <tr id="ingredientRow_0" class="inputRow">   
                            <td><div class="input text"><input type="text" name="ingredients[0][_joinData][amount_name]" list="amounts-datalist_0" autocomplete="off" placeholder="Menge..." oninput="fetchCookingamounts(this.value)" id="ingredients-0-joindata-amount-name" aria-label="Menge..."></div>
                                <datalist id="amounts-datalist_0"></datalist>
                            </td>
                            <td><div class="input text"><input type="text" name="ingredients[0][_joinData][measurement_name]" list="measurements-datalist_0" autocomplete="off" placeholder="Maßeinheit..." oninput="fetchCookingmeasurements(this.value)" id="ingredients-0-joindata-measurement-name" aria-label="Maßeinheit..."></div>
                                <datalist id="measurements-datalist_0"></datalist>
                            </td>
                            <td><div class="input text"><input type="text" name="ingredients[0][name]" list="ingredients-datalist_0" autocomplete="off" placeholder="Zutat..." oninput="fetchIngredients(this.value)" id="ingredients-0-name" aria-label="Zutat..." maxlength="255"></div>
                                <datalist id="ingredients-datalist_0"></datalist>
                     ...

CSS

body {
    display: flex; 
    flex-direction: column; 
    min-height: 100vh; 
    background-color: rgb(248 250 252 / var(--tw-bg-opacity, 1));
    font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
    margin:20 100;
    
}
header {
    background-color: #FEF2F2; 
}
div.mainWrapper{
    display: flex; 
    flex-direction: column; 
    flex: 1 1 0%; 

    @media (min-width: 640px) { 
    flex-direction: row; 
    }
}
main {
    flex: 1 1 0%; 
    background-color: #E0E7FF; 
}
nav {
    order: -9999; 
    background-color: #DDD6FE; 
    @media (min-width: 640px) { 
        width: 8rem; 
    }
}
aside {
    background-color: #FEF3C7; 
    @media (min-width: 640px) { 
        width: 8rem; 
    }
}
footer {
    background-color: #F3F4F6; 
}

details {
    width:100%;
    summary {
        font-size: 1.25rem;
        line-height: 1.75rem;
        font-weight: 600;
    }
}
table {
    background-color:red; 
    width:100%;
}

JavaScript

//tabledefinition(s) for each table
// the suffix .ingredientsTable has to be changed to the ID of the tablebody where the data will be inserted
tableData = {};
tableData.ingredientsTable = {
    'minRows':1,
    'maxRows':10,
    'td':               //the 'td' data is only in the object to show how it could be expanded
    {
        0: {'class':null,
            'innerHTML': 'Table 1 td0 *'
        },
        1: {'class':null,
            'innerHTML': 'Table 1 {placeholder1} td1 *'
        },
        2: {'class':null,
            'innerHTML': 'Table 1 td2 *'
        },
        3: {'class':null,
            'innerHTML': 'Table 1 td3 *'
        },
        4: { 'class':'actions',
            'innerHTML':'<button class=\"insert\" onClick=\"handleTable(this,\'addRowsBefore(1)\')\">+1 &UpArrowBar;</button>'+
            '<button class=\"delete\" onClick=\"handleTable(this,\'deleteRow()\')\">delete</button>'+
            '<button class=\"insert\" onClick=\"handleTable(this,\'addRowsAfter(1)\')\">+1 &DownArrowBar;</button>'
            }
    }
}
tableData.directionsTable = {
    'minRows':1,
    'maxRows':8,
}

////********* */


function checkMinRows(tableBody,rowsToDelete) {
    let tableId = String(tableBody.closest("table").id);
    let minRows = tableData[tableId].minRows;
    let currentRows = tableBody.rows.length;
    if ((currentRows-rowsToDelete)<minRows) {
        return false;
    }
    else {
        return true;
    }
}

function checkMaxRows (tableBody,rowsToInsert) {
    let tableId = String(tableBody.closest("table").id);
    let maxRows = tableData[tableId].maxRows;
    let currentRows = tableBody.rows.length;
    if ((currentRows+rowsToInsert)>maxRows) {
        return false;
    }
    else {
        return true;
    }
}

function addRowBeforeCurrentRow (trData) {
    let tableBody = trData.closest("tbody");
    if (checkMaxRows(tableBody,1) == true) {
        let rowId = trData.parentNode.parentNode.id;
        let currentRow =...