Создать таблицу с кликабельными ячейками, которые реагируют на ивент click

У меня задача: создать HTML таблицу при помощи JS, где ячейками выступают кнопки

Логика работы сайта: На вход подается .csv файл. Логика заточена под определенный файл, где по началу выводится таблица дат.

Задача: нужно сделать так, чтобы при нажатии на ячейку, вместо двух дат выводилась полная строчка с данными. То есть, нажав на ячейку с датой, чтобы выводилась вся строка. (Пример внизу под кодом)

Я хотел сделать это в виде таблицы из кнопок, но проблема в том что кнопки создаются внутри цикла, и в таком случае нет доступа к addEventListener("click")

Внизу на картинке представлен первый вывод таблицы. Входной файл под который заточен сайт содержит в себе 28 колонок. На картинке изображены две из них

введите сюда описание изображения

.html:

/**
 * @param {*} inputForm - form element
 * @param {*} file - input with type="file"
 * @param {*} filterInput - input filter word
 */
const inputForm = document.getElementById("input-form")
const file = document.getElementById("file-choose")
const filterInput = document.getElementById("test-input")
const dataTable = document.getElementById("data-table")
const emptyMessage = document.getElementById('empty-message')
const dateInput = document.getElementById("date-input")
const rowLimiter = document.getElementById("row-limiter")
const timeInput = document.getElementById("time-input")

/**
 * @param {*} table - creating element table 
 * @param {*} thead - creating element table header
 * @param {*} tbody - creating element table body
 */

/**
 * @function csvToArray - function that converts csv file data into an array
 * @param {string} str 
 * @param {char} delimiter 
 * @return {Array} Array with all information from file
 */

function csvToArray(str, delimiter=',') {
   /**
    * @param {Array} headers - array, is created from the first row elements. String slices from index 0, to index of first '\n'
    * @param {Array} rows - array of rows. Each row - object with key:value
    * */
   const headers = str.slice(0, str.indexOf("\n")).split(delimiter)
   const rows = str.slice(str.indexOf("\n") + 1).split("\n")

   // As last element of headers is '\r', it erases
   headers.pop()

   /**
    * @param {Array} arr - Data array. Includes objects with data as array elements
    * @param {Array} values - Values from the row splitted by separator '-'
    * @param {Object} element - Object that appears from headers Array as setting key:value 
    * @return {Array} arr - Result array
    */
   const arr = rows.map((row) => {
      const values = row.split(delimiter)
      const element = headers.reduce((object, header, index) => {
         object[header] = values[index]
         return object
      }, {})
      return element
   })

   return arr
}

/**
 * 
 * @param {Array} data - Array of objects 
 * @returns {Array} initialArray - Array of arrays, each element of whom - is array with two specific values
 * 
 * As by the assignment table has to print only two dates first, we fill array with arrays of those two dates from each object
 */
function setInitialTable(data) {

   let initialArray = [['tLogIn', 'tLogOut']]

   for (let i = 0; i < data.length; i++) {
      initialArray.push([data[i].tLogIn, data[i].tLogOut])
   }

   return initialArray
}

inputForm.addEventListener("submit", (e) => {
   /**
    * e.preventDefault() - prevents reload when click Submit button
    */
   e.preventDefault()

   // A files property returnes files list. So [0] will return us first file
   const input = file.files[0]
   
   /**
    * @param table - table element. Will be used to append thead and tbody
    * @param thead - table header element. Will be used to append tr and th inside
    * @param tbody - table body element. Will be used to append tr and td inside
    * @param reader - FileReader variable. Will be used to work with file
    */
   let table = document.createElement("table")
   let thead = document.createElement("thead")
   let tbody = document.createElement("tbody")
   const reader = new FileReader()

   // Check if file chosen or not. If not - prints following message
   if (file.value == '') {
      emptyMessage.innerHTML = "File not chosen"

      dataTable.innerHTML = ''
   }
   else {
      reader.onload = function(e) {
         // Recieving table info in text form separated with coma
         const text = e.target.result

         // If recieved file is empty, message with be revealed with file name
         if (text.length === 0) {
            if (file.DOCUMENT_NODE > 0) {
               dataTable.innerHTML = ''
               table.innerHTML = ''
               thead.innerHTML = ''
               tbody.innerHTML = ''
            }

            /** 
             * file.value will be recieved as C:\\....
             * So to recieve file name, we need to split it by '\\', and it will be ["C:", ..., "file.name"] 
            */
            const arrFromFileName = file.value.replaceAll('\\', ',').split(',')

            // To print file name, we need to take the last element of array
            emptyMessage.innerHTML = `File <span>${arrFromFileName[arrFromFileName.length - 1]}</span> is empty`
         }

         // If file is not empty:
         else {
            // Cleaning emptyMessage field if it was printed before
            if (emptyMessage.value != 0)
               emptyMessage.innerHTML = ''

            /**
             * @param data - Data array with Array of Objects 
             * @param initialArray - Array with only two columns 
             */
            const data = csvToArray(text)
            const initialArray = setInitialTable(data)
         
            // Cleaning table if it was printed before
            dataTable.innerHTML = ''
            table.innerHTML = ''
            thead.innerHTML = ''
            tbody.innerHTML = ''

            // Adding thead and tbody to the table
            table.appendChild(thead)
            table.appendChild(tbody)

            // Adding table to our parent table declared in HTML file
            document.getElementById('data-table').appendChild(table)

            /**
             * @param hrow - Header row
             * 
             * First row in InitialArray are headers names as it's written in csvToArray() function when adding headers
             * So we create a loop with 2 iterations as we have only 2 columns, and take first row as headers names
             */
            let hrow = document.createElement('tr')
            for (let i = 0; i < initialArray[i].length; i++) {
               let theader = document.createElement('th')

               // Writing header name into theader slot
               theader.innerHTML = initialArray[0][i]
               // Appending theader slot to header row
               hrow.appendChild(theader)
            }
            // Appending header row into Header section
            thead.appendChild(hrow)

            /**
             * @param {Number} rowLimiter - rowLimiter from HTML input with type="number". 
             *                              Unary plus converts recieved string into a number
             * @param table_data - table data slot
             * @param table_data_button - a button inserting into a table slot
             * @param body_row - row for table body (tbody)
             * @param tbody - table body
             * 
             * Iterations going from 1 to rowLimiter. From 1, because index 0 in initialArray is headers array, and we need only data
             */
            for (let i = 1; i < +rowLimiter.value; i++) {
               let body_row = document.createElement('tr')
               for (let j = 0; j < 2; j++) {
                  let table_data = document.createElement('td')
                  var table_data_button = document.createElement('button')

                  if (initialArray[i][j].length !== 0) {
                     table_data_button.innerHTML = initialArray[i][j]
                     table_data.appendChild(table_data_button)
                  }
                  body_row.appendChild(table_data)
               }
               tbody.appendChild(body_row)
            }

            table_data_button.addEventListener("click", (e) => {
               e.preventDefault()

               console.log("Click!")
            })
      
            table.appendChild(thead)
            table.appendChild(tbody)
            dataTable.appendChild(table)
         }
      }
      // Using to read input file
      reader.readAsText(input)
   }
})
* {
   font-family: 'Courier New', Courier, monospace;
}

body {
   margin: 0;
}

#file-choose {
   max-width: fit-content;
}

#data-table table {
   margin: none;
}

#data-table {
   margin: auto;
   
   border-collapse: collapse;
   border-radius: 5px;
   font-size: 0.9em;
   font-family: sans-serif;

   min-width: fit-content;

   box-shadow: 0 0 20px rgba(0, 0, 0, 0.15);
}

#data-table {
   max-width: 1400px;
}

#data-table thead tr {
   background-color: #009897;
   color: #ffffff;
   
   text-align: left;
}

#data-table th, #data-table td {
   padding: 12px 15px;
}

#data-table tbody tr {
   border-bottom: 1px solid #dddddd;
}

#data-table tbody tr:nth-child(even) {
   background-color: #f3f3f3;
}

#data-table tbody tr:last-of-type {
   border-bottom: 2px solid #009897;
   border-radius: 50px;
}

#data-table tbody td button {
   border: none;
   background-color: transparent;
   cursor: pointer;
}

#empty-message {
   text-align: center;
   font-size: 40px;
}

#file-choose {
   margin-right: -0px;
   max-width: 90px;
}

#input-section {
   display: flex;
   flex-direction: row;
   justify-content: space-evenly;
   margin: 0 250px;
   width: 70%;
}

#input-form {
   position: static;
   
   min-height: 100px;

   display: flex;
   flex-direction: column;
   justify-content: center;
   align-items: center; 
   border-bottom-left-radius: 15px;
   border-bottom-right-radius: 15px;
}

#row-limiter {
   max-width: 85px;
}

#test-input {

}

.submit-button {
   padding: 3px 8px;

   font-size: 15px;

   border-radius: 3px;
   border: 0.1px solid #000
}
<!DOCTYPE html>
<html lang="en">
   <head>
      <title>Excel Transform Site</title>
      <link rel="stylesheet" href="./excel-transfrom.css">
   </head>
   <body>
      <form id="input-form">
         <section id="input-section">
            <input type="file" id="file-choose" apply=".csv"><br>
            <p id="chosen-file"></p>

            <div>
               <label class="test-input-label">Filter word:</label>
               <input type="text" id="test-input">
            </div>
            <div>
               <label class="date-input-label">Date:</label><br>
               <input type="date" id="date-input">
               <input type="time" id="time-input">
            </div>
            <div>
               <label>Rows limit:</label>
               <input type="number" id="row-limiter" placeholder="50000" value="3" min="1" max="50000">
            </div>
         </section><br>

         <input type="submit" value="Submit" class="submit-button">
      </form><br>

      <table id="data-table"></table>
      <p id="empty-message"></p>

      <script src="./excel-transform.js"></script>
   </body>
</html>

Пример: Нажав на ячейку 11/01/2022 5:00, таблица закрывается и вместо нее выводится строчка

header 1| header 2 | tLogIn | tLogOut | header 3 |

data 1 | data 2 | 11/01/2022 5:00 | 11/01/2022 8:00 | data 3 |

Данные для .csv файла на вход:

header 1,header 2,tLogIn,tLogOut,header 3
data 1,data 2,"11/01/2022 5:00", "11/01/2022 8:00",data 3,
data 11,data 22,"15/04/2019 12:00","15/04/2019 15:00",data 33

Ответы (2 шт):

Автор решения: Andrei

Если я правильно понял Вашу проблему тогда это обычно делается как-то так:

const bd = document.querySelector('body');

const table = document.createElement('TABLE');
table.setAttribute('id', 'tb');

for (let i = 0; i < 4; ++i) {
  let tr = document.createElement('TR');
  for (let j = 0; j < 4; ++j) {
    let td = document.createElement('TD');
    td.setAttribute('id', `cell ${i} ${j}`);
    td.append(`Cell ${i} ${j}`);
    tr.append(td);
  }
  table.append(tr);
}

bd.append(table);

const tb = document.querySelector('#tb');

tb.addEventListener('click', e => {
  e.target.innerHTML = `Я ячейка ${e.target.id}`;
});
td {
  border: solid 1px grey;
}

→ Ссылка
Автор решения: puffleeck

нажав на ячейку с датой, чтобы выводилась вся строка

Я хотел сделать это в виде таблицы из кнопок, но проблема в том что кнопки создаются внутри цикла, и в таком случае нет доступа к addEventListener("click")

в таком случае можно юзнуть "онклико-заменитель". ну то есть, использовать например селект или форму заполненную инпутами-радио повесив на неё(форму) onchange. в случае с формой можно спрятать инпуты, а щупать не их а label'ы, расфасовав их по любому удобному контейнеру.

и да, может пора бы уже отправить <table> с её кучей мусорных тегов на покой, всё таки css grid есть. вам же не в IE 6 каком нибудь всё это открывать... или таки в нём...? :3

пара наглядных примеров для "пощупать идею лапками":

var x =
[
  ['a1', 'a2', 'a3', 'a4', 'a5'],
  ['b1', 'b2', 'b3', 'b4', 'b5'],
  ['c1', 'c2', 'c3', 'c4', 'c5'],
  ['d1', 'd2', 'd3', 'd4', 'd5']
]
#y{
 display: grid;
 grid-template-columns: 1fr 1fr;
 grid-gap: 1em;
}
#y > *{background: pink;}
/* input[name=abc]{display: none} */
<select onchange='alert(x[this.value])' size='5'>
 <option>---</option>
 <option>0</option>
 <option>1</option>
 <option>2</option>
 <option>3</option>
</select>
 
<form onchange='console.log(x[abc.value])'>
 <input id='a' type='radio' name='abc' value='0'>
 <input id='b' type='radio' name='abc' value='1'>
 <input id='c' type='radio' name='abc' value='2'>
 <input id='d' type='radio' name='abc' value='3'>
</form>

<p id='y'>
  <label for='a'>a</label>
  <label for='b'>b</label>
  <label for='c'>c</label>
  <label for='d'>d</label>
</p>

→ Ссылка