Javascript / подсказка к 3 задачке

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

Начнем со стандартной вёрстки:

<!doctype html>
<html lang="en">
  <head>
    <title>Title</title>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
  </head>
  <body>

    <div class="container">

    </div>

    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
  </body>
</html>

теперь определюсь с данными. У меня буду такой список:

Название Год выпуска Рейтинг кинопоиска URL с обложкой, с кинопоиска Режиссер
Унесённые призраками 2001 8.4 https://avatars.mds.yandex.net/get-kinopoisk-image/1629390/64becb5d-e7b9-4a56-8c71-5008294a854a/300x450 Хаяо Миядзаки
Малхолланд Драйв 2002 7.6 https://avatars.mds.yandex.net/get-kinopoisk-image/1777765/477bd555-16ee-47aa-be90-6a252c189414/300x450 Дэвид Линч
Мертвец 1995 7.8 https://avatars.mds.yandex.net/get-kinopoisk-image/1773646/707e78dc-43a0-4309-88f0-dee4ce7a4e57/300x450 Джим Джармуш

в табличку я буду выводить только название и рейтинг. Остальное буду выводить, когда буду тыкать на строки. Собственно, как ее добавить?

Для этого надо минимально прописать следующие теги:

<div class="container">
  <table>
    <thead>
      <tr>
        <th>Название</th>
        <th>Рейтинг</th>
      </tr>
    </thead>
    <tbody>
    </tbody>
  </table>
</div>

на картинке красным я выделил область заголовка таблицы (это как правило первая строка), внутри этой области оранжевым выделено собственно само объявление строки (тег tr), ну и синим выделены собственно теги ячеек th, так как у меня в строке две ячейки то и тега две штуки.

если посмотреть, то будет выглядеть вот так:

выглядит уродливо, но это потому что мы не добавили классы bootstrap.

Добавим табличке класс table

<div class="container">
  <table class="table">
    <thead>
      <tr>
        <th>Название</th>
        <th>Рейтинг</th>
      </tr>
    </thead>
    <tbody>
    </tbody>
  </table>
</div>

от теперь другое дело, таблица сразу респонсивная:

теперь добавим данных. Данные как ты, наверное, уже догадалась пишутся внутрь тега tbody.

На каждую строку данных надо добавлять сначала тег tr, а потом внутри него прописывать теги td для каждой ячейки. Тут небольшой тонкий момент, для ячеек заголовка используем тег th, а для ячеек, данных используем td, так принято, но на самом деле можно и там и сям использовать и тот и этот тег. Просто th выделяет текст жирным.

Добавлю строчку:

<div class="container">
  <table class="table">
    <thead>
      <!-- ... -->
    </thead>
    <tbody>
      <tr>
        <td>Унесённые призраками</td>
        <td>8.4</td>
      </tr>
    </tbody>
  </table>
</div>

и еще парочку:

  <table class="table">
    <thead>
      <!-- ... -->
    </thead>
    <tbody>
      <tr>
         <!-- ... -->
      </tr>
      <tr>
        <td>Малхолланд Драйв</td>
        <td>7.6</td>
      </tr>
      <tr>
        <td>Мертвец</td>
        <td>7.8</td>
      </tr>
    </tbody>
  </table>

посмотрим что нам предлагает для таблиц bootstrap: https://getbootstrap.com/docs/4.6/content/tables/ . Посмотри какие классы ты хочешь себе добавить. Главное обязательно добавь класс table-hover чтобы при наведении на строку она выделялась, я добавлю себе еще темный заголовок:

<div class="container">
  <table class="table table-hover">
    <thead class="thead-dark"> <!-- темный заголовок сделал -->
       <!-- ... -->
    </thead>
    <tbody>
       <!-- ... -->
    </tbody>
  </table>
</div>

получится так

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

<style>
  .table tr td:hover { /* :hover -- это псевдо класс, добавляется когда наводишь на элемент мышку */
    cursor: pointer; /*курсор -- указатель*/
  }
</style>

вообще наверное и td:hover хватило, но так точнее.

Теперь подумаем, что делать с остальными данными? Надо их как-то привязать к строкам, но так чтобы юзер их не видел. Для этого есть так называемые дата атрибуты, их можно добавлять к любому элементу, и они начинаются со слова data-, а дальше любое свое слово.

Например, добавлю я год выпуска, делается это так:

<div class="container">
  <table class="table table-hover">
    <thead class="thead-dark">
      <!-- ... -->
    </thead>
    <tbody>
      <tr data-year="2001"> <!-- добавил data-year -->
        <td>Унесённые призраками</td>
        <td>8.4</td>
      </tr>
      <tr  data-year="2002">  <!-- и тут-->
        <td>Малхолланд Драйв</td>
        <td>7.6</td>
      </tr>
      <tr  data-year="1995">  <!-- и здесь -->
        <td>Мертвец</td>
        <td>7.8</td>
      </tr>
    </tbody>
  </table>
</div>

так как их не видно, то сделаны они специально чтобы js их использовал, добавим функцию:

<script>
  function onRowClick(row) {
    console.log(row);
  }
</script>

подключим ее к строкам:

<div class="container">
  <table class="table table-hover">
    <thead class="thead-dark">
      <!-- ... -->
    </thead>
    <tbody>
      <tr data-year="2001" onclick="onRowClick(this)">
        <!-- ... -->
      </tr>
      <tr  data-year="2002" onclick="onRowClick(this)">
        <!-- ... -->
      </tr>
      <tr  data-year="1995" onclick="onRowClick(this)">
        <!-- ... -->
      </tr>
    </tbody>
  </table>
</div>

проверим что все работает:

теперь собственно, как получить доступ к этим атрибутам? А очень просто, у всякого элемента при работе с ним в js есть свойство dataset и вот в него эти атрибуты и попадают, подправим функцию:

<script>
  function onRowClick(row) {
    console.log(row.dataset);
  }
</script>

проверяем:

то есть, чтобы вывести год привязанный к строке, надо написать так:

<script>
  function onRowClick(row) {
    console.log(row.dataset.year);
  }
</script>

тыкаем:

добавим теперь оставшиеся наши данные в атрибуты, режиссера я буду пихать в data-director, а картинку в data-image, вот так:

<div class="container">
  <table class="table table-hover">
    <thead class="thead-dark">
      <!-- ... -->
    </thead>
    <tbody>
      <tr data-year="2001" data-director="Хаяо Миядзаки"  data-image="https://avatars.mds.yandex.net/get-kinopoisk-image/1629390/64becb5d-e7b9-4a56-8c71-5008294a854a/300x450" onclick="onRowClick(this)">
        <!-- ... -->
      </tr>
      <tr  data-year="2002" data-director="Дэвид Линч" data-image="https://avatars.mds.yandex.net/get-kinopoisk-image/1777765/477bd555-16ee-47aa-be90-6a252c189414/300x450" onclick="onRowClick(this)">
        <!-- ... -->
      </tr>
      <tr  data-year="1995" data-director="Джим Джармуш" data-image="	https://avatars.mds.yandex.net/get-kinopoisk-image/1773646/707e78dc-43a0-4309-88f0-dee4ce7a4e57/300x450" onclick="onRowClick(this)">
        <!-- ... -->
      </tr>
    </tbody>
  </table>
</div>

вообще если так подумать есть смысл туда запихать и название, и рейтинг:

<!-- ... -->
<tr data-title="Унесённые призраками" data-rating="8.4" data-year="2001" data-director="Хаяо Миядзаки"  data-image="..." onclick="onRowClick(this)">
    <td>Унесённые призраками</td>
    <td>8.4</td>
</tr>
<tr data-title="Малхолланд Драйв" data-rating="7.6" data-year="2002" data-director="Дэвид Линч" data-image="..." onclick="onRowClick(this)">
    <td>Малхолланд Драйв</td>
    <td>7.6</td>
</tr>
<tr  data-title="Мертвец" data-rating="7.8" data-year="1995" data-director="Джим Джармуш" data-image="..." onclick="onRowClick(this)">
    <td>Мертвец</td>
    <td>7.8</td>
</tr>
<!-- ... -->

Теперь добавим блок справа куда я буду выводить подробную информацию. Для этого запихаем таблицу в строку,

<div class="container">
<div class="row">
  <div class="col-8">
    <table class="table table-hover">
      <!-- ... -->
    </table>
  </div>
  <div class="col-4">
     <!-- ... -->
  </div>
</div>
</div>

получится так, вот справа видно колонку если ее из инспектора выделить

то есть таблица занимает 8/12 а табличка 4/12. Добавим туда что-нибудь с рамочкой

<div class="container">
    <div class="row">
      <div class="col-8">
        <table class="table table-hover">
            <!-- ... -->
        </table>
      </div>
      <div class="col-4">
        <div class="shadow p-2 text-center">
          Название
        </div>
      </div>
    </div>
  </div>

полчится так:

глянем как работают медиа классы, то есть которые меняют расположение элементов в зависимости от ширины экрана. Я хочу, чтобы, например, на большом экране от 768px в ширину у меня все было как сейчас, табличка 8/12, а название 4/12. Для этого я меняю класс col-8 на col-md-8, а col-4 на col-md-4,

<div class="container">
    <div class="row">
      <div class="col-md-8">
        <table class="table table-hover">
            <!-- ... -->
        </table>
      </div>
      <div class="col-md-4">
        <div class="shadow p-2 text-center">
          Название
        </div>
      </div>
    </div>
  </div>

а теперь глянем как себя поведет верстка если мы начнем менять размеры экрана:

то есть для размеров которые меньше 768 он делает так чтобы элемент занимал всю ширину. Я не буду сильно на этом останавливаться, но если научится оперировать этими модификаторами (типа md, там есть еще xl, lg, sm), то можно делать сайт который будет удобно смотреть и на десктопе, и на телефоне.

В бутстрапе у многих классов есть их версия с md, что позволяет очень гибко настраивать поведение. Например, у того же отступа можно для больших дисплеев использовать отступ p-md-2, а для телефонов, например, уменьшать p-sm-1, в общем, тьма возможностей =)

Сделаем теперь чтобы при клике на row у нас текст менялся на название фильма.

Обернем Название в тег span и добавим ему id:

<!-- ... -->
<div class="col-md-4">
    <div class="shadow p-2 text-center">
      <span id="title">Название</span>
    </div>
</div>

а теперь как обычно подправим функцию так чтобы она меняла текст title на дата атрибут нашей строки, вот так:

<script>
function onRowClick(row) {
  let title = document.getElementById("title")
  title.innerText = row.dataset.title;
}
</script>

теперь сделаем еще так чтобы при клике на строку она фиксировалась как выделенная. Чтобы выделить строку при клике на нее я буду добавлять класс table-info (больше классов тут https://getbootstrap.com/docs/4.6/content/tables/#contextual-classes)

<script>
function onRowClick(row) {
  let title = document.getElementById("title")
  title.innerText = row.dataset.title;

  row.classList.add("table-info");
}
</script>

смотрим:

строки выделяются, но не сбрасываются цвета у предыдущих. Вот и вопрос как сделать сброс?

Вариант 1

Можно завести переменную в которую будем фиксировать предыдущую строку, и при клике на очередную строку, у предыдущей будем удалять класс, вот так:

<script>
let previousRow = null; // завел переменную под строку
function onRowClick(row) {
  let title = document.getElementById("title")
  title.innerText = row.dataset.title;

  if (previousRow) { // если предыдущая строка не пустая
    previousRow.classList.remove("table-info") // то удаляем у нее класс
  }
  row.classList.add("table-info");
  previousRow = row; // меняем предыдущую строку на текущую
}
</script>

проверка:

Вариант 2

Более дубовый но более унивесальный, и чаще используется, просто удаляем класс у всех строк, и добавляем класс текущей

<script>
function onRowClick(row) {
  let title = document.getElementById("title")
  title.innerText = row.dataset.title;
    
  // находим все теги tr, которые внутри tbody, который внутри тега с классом table
  document.querySelectorAll(".table tbody tr").forEach(el => {
      el.classList.remove("table-info"); // удаляем у них класс table-info
  })
  row.classList.add("table-info");
}
</script>

работает так же:

Ну база есть, теперь иди пили задачку =)

3

Допилить задание, чтобы как-то так выглядело, когда тыкаешь на строчки

  • чтобы картинку заменять, надо добавить img с пустым атрибутом src и из скрипта устанавливать атрибут картинки на значение row.dataset.image