Задача №4

Дано 3 таблиці розмірності 3*3 з випадковими числами. Якщо відбувається клік на якійсь із клітинок, то до відповідної таблиці додається червона рамка (спробуйте додати можливість відображення кількості кліків біля назви таблиці з використанням відповідно доданого для цього атрибута).


Рішення:

'use strict'

/**
 * @param {number} min
 * @param {number} max
 */
function getRandomInteger(min = 0, max = 99) {
  if (typeof min !== 'number' || typeof max !== 'number')
    throw new TypeError('Min and max must be numbers.')

  return min + Math.floor(Math.random() * (max - min + 1))
}

class Table {
  #rowsAmount
  #colsAmount

  /**
   * @param {number} rowsAmount
   * @param {number} colsAmount
   * @param {string} [label]
   * @param {boolean} [hasCounter]
   */
  constructor(rowsAmount, colsAmount, label, hasCounter) {
    this.rowsAmount = rowsAmount
    this.colsAmount = colsAmount
    this.label = label
    this.hasCounter = hasCounter
  }

  get rowsAmount() {
    return this.#rowsAmount
  }

  set rowsAmount(newRowsAmount) {
    if (typeof newRowsAmount !== 'number')
      throw new TypeError('Rows amount must be number.')
    if (newRowsAmount <= 0)
      throw new RangeError('Rows amount must be positive.')

    this.#rowsAmount = newRowsAmount
  }

  get colsAmount() {
    return this.#colsAmount
  }

  set colsAmount(newColsAmount) {
    if (typeof newColsAmount !== 'number')
      throw new TypeError('Columns amount must be number.')
    if (newColsAmount <= 0)
      throw new RangeError('Columns amount must be positive.')

    this.#colsAmount = newColsAmount
  }

  createCell() {
    const cell = document.createElement('TD')

    cell.textContent = String(getRandomInteger())

    return cell
  }

  createRow() {
    const row = document.createElement('TR')

    for (let i = 0; i < this.colsAmount; i++) {
      row.append(this.createCell())
    }

    return row
  }

  createCaption() {
    const caption = document.createElement('CAPTION')

    caption.textContent = this.label

    if (!this.hasCounter) return caption

    const counter = document.createElement('SPAN')
    counter.dataset.clickCounter = '0'
    counter.textContent = counter.dataset.clickCounter
    caption.append('. Натискань: ', counter)

    return caption
  }

  createTable() {
    const table = document.createElement('TABLE')
    const tBody = document.createElement('TBODY')

    if (this.label) table.append(this.createCaption())

    table.append(tBody)

    for (let i = 0; i < this.rowsAmount; i++) {
      tBody.append(this.createRow())
    }

    return table
  }

  render(selector) {
    const table = this.createTable()

    if (selector) document.querySelector(selector).append(table)

    return table
  }
}

// =============================================================================
// Event handlers

/**
 * @param {Event} e
 */
function highlightTable({target, currentTarget}) {
  const td = target.closest('td')

  if (!td) return

  // reset borders
  const tables = currentTarget.querySelectorAll('table')

  for (const table of tables) {
    table.style.borderColor = ''
  }

  const table = td.closest('table')
  table.style.borderColor = 'red'
}

/**
 * @param {Event} e
 */
function increaseTableCounter({target}) {
  const td = target.closest('td')

  if (!td) return

  const table = td.closest('table')
  const counter = table.querySelector('[data-click-counter]')

  if (!counter) return

  counter.dataset.clickCounter = String(
    Number(counter.dataset.clickCounter) + 1,
  )
  counter.textContent = counter.dataset.clickCounter
}

// =============================================================================

try {
  const container = document.querySelector('.js-app')

  for (let i = 0; i < 3; i++) {
    const table = new Table(3, 3, `Таблиця №${i + 1}`, true)
    const tableEl = table.render()

    tableEl.style.border = '2px solid'
    container.append(tableEl)
  }

  container.addEventListener('click', highlightTable)
  container.addEventListener('click', increaseTableCounter)
} catch (error) {
  console.error(error)
}