Конспект JS-course

Задачи

Задача 1

Дан список сообщений. Добавьте каждому сообщению кнопку для его удаления.

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

В результате, при нажатии на крестик, сообщение удаляется(скрывается).

![](animal_list.png)

Изображение кнопки:

Исходный код http://learn.javascript.ru/play/tutorial/browser/events/messages-delegate-src/index.html или:

<!DOCTYPE HTML>
<html>
<head>
    <meta charset="utf-8">
    <style>
        body {
            margin: 10px auto;
            width: 470px;
        }
        h3 {
            margin: 0;
            padding-bottom: .3em;
            padding-right: 20px;
            font-size: 1.1em;
        }
        p {
            margin: 0;
            padding: 0 0 .5em;
        }
        .pane {
            background: #edf5e1;
            padding: 10px 20px 10px;
            border-top: solid 2px #c4df9b;
            position: relative;
        }

        .remove-button {
            position: absolute;
            top: 10px;
            right: 10px;
            cursor: pointer;
            display: block;
            background: url(delete.gif) no-repeat;
            width: 16px;
            height: 16px;
        }
    </style>
</head>

<body>

<div id="messages-container">
  <div class="pane">
    <h3>Лошадь</h3>
    <p>Домашняя лошадь — животное семейства непарнокопытных, одомашненный и единственный сохранившийся подвид дикой лошади, вымершей в дикой природе, за исключением небольшой популяции лошади Пржевальского.</p>
    <span class="remove-button"></span>
  </div>
  <div class="pane">
    <h3>Осёл</h3>
    <p>Домашний осёл или ишак — одомашненный подвид дикого осла, сыгравший важную историческую роль в развитии хозяйства и культуры человека. Все одомашненные ослы относятся к африканским ослам.</p>
    <span class="remove-button"></span>
  </div>
  <div class="pane">
    <h3>Корова, а также пара слов о диком быке, о волах и о тёлках. </h3>
    <p>Коро́ва — самка домашнего быка, одомашненного подвида дикого быка, парнокопытного жвачного животного семейства полорогих. Самцы вида называются быками, молодняк — телятами, кастрированные самцы — волами. Молодых (до первой стельности) самок называют тёлками.</p>
    <span class="remove-button"></span>
  </div>
</div>

<script>

  /* ваш код */

</script>
</body>
</html>

Решение:

Шаг 1:

Поставьте обработчик click на контейнере. Он должен проверять, произошел ли клик на кнопке удаления (target), и если да, то удалять соответствующий ей DIV.

Шаг 2:

document.getElementById('messages-container').onclick = function(e) {

    e = e || window.event;
    var target = e.target || e.srcElement;

    // без цикла, т.к. мы точно знаем, что внутри нет тегов
    if (target.className != 'remove-button') return;

    target.parentNode.style.display = 'none';
}

Свой вариант:

document.querySelector('#messages-container').addEventListener('click', function(e){
  var event = e || window.event;
  var target = event.target || event.srcElement;
  if (target.className != 'remove-button') return;
    target.parentNode.style.display = 'none';
}, false);

Задача 2:

Создайте дерево, которое по клику на заголовок скрывает-показывает детей:

![](animal-tree.png)

Исходный документ: http://learn.javascript.ru/play/tutorial/browser/events/tree-src/index.html или

<!DOCTYPE HTML>
<html>
<head><meta charset="utf-8"></head>
<body>

<ul class="tree">
    <li>Животные
        <ul>
            <li>Млекопитающие
                <ul>
                    <li>Коровы</li>
                    <li>Ослы</li>
                    <li>Собаки</li>
                    <li>Тигры</li>
                </ul>
            </li>
            <li>Другие
                <ul>
                    <li>Змеи</li>
                    <li>Птицы</li>
                    <li>Ящерицы</li>
                </ul>
            </li>
        </ul>
    </li>
    <li>Рыбы
        <ul>
            <li>Аквариумные
                <ul>
                    <li>Гуппи</li>
                    <li>Скалярии</li>
                </ul>

            </li>
            <li>Морские
                <ul>
                    <li>Морская форель</li>
                </ul>
            </li>
        </ul>
    </li>
</ul>

</body>
</html>

Требования:

  • Использовать делегирование.
  • Клик вне текста заголовка (на пустом месте) ничего делать не должен.
  • При наведении на заголовок — он становится жирным, реализовать через CSS.
  • При двойном клике на заголовке, его текст не должен выделяться. P.S. При необходимости HTML/CSS дерева можно изменить.

Решение

Дерево устроено как вложенный список.

Клики на все элементы можно поймать, повесив единый обработчик onclick на внешний UL.

Как поймать клик на заголовке? Элемент LI является блочным, поэтому нельзя понять, был ли клик на тексте, или справа от него.

Проблема в верстке, в том что LI занимает всю ширину. Можно кликнуть справа от текста, это все еще LI.

Один из способов это поправить — обернуть заголовки в дополнительный элемент SPAN, и обрабатывать только клики внутри SPAN'ов.

Мы могли бы это сделать в HTML, но давайте для примера используем JavaScript. Следующий код ищет все LI и оборачивает текстовые узлы в SPAN.

var treeUl = document.getElementsByTagName('ul')[0];

var treeLis = treeUl.getElementsByTagName('li');

for(var i=0; i<treeLis.length; i++) {
  var li = treeLis[i];

  var span = document.createElement('span');
  li.insertBefore(span, li.firstChild); // добавить пустой SPAN
  span.appendChild(span.nextSibling); // переместить в него заголовок
}

Теперь можно отслеживать клики на заголовках.

При проверке так выглядит дерево с обёрнутыми в SPAN заголовками и делегированием:

<style>
span { border: 1px solid red; }
</style>

<ul onclick="alert((event.target||event.srcElement).tagName)">
<li><span>Млекопетающие</span>
  <ul>
    <li><span>Коровы</span></li>
    <li><span>Ослы</span></li>
    <li><span>Собаки</span></li>
    <li><span>Тигры</span></li>
  </ul>
</li>
</ul>

Так как SPAN инлайновый элемент, он всегда такого же размера как текст.

В реальной жизни дерево должно быть сразу со SPAN, чтобы не нужно было исправлять структуру. Если HTML-код дерева генерируется на сервере, то это несложно. Если дерево генерируется в JavaScript – тем более просто.

Получение узла по SPAN

Для делегирования нужно по клику понять, на каком узле он произошел.

В нашем случае у SPAN нет детей-элементов, поэтому не нужно подниматься вверх по цепочке родителей. Достаточно просто проверить event.target.tagName == &#39;SPAN&#39;, чтобы понять, где был клик, и спрятать потомков.

var tree = document.getElementsByTagName('ul')[0];

tree.onclick = function(e) {
  e = e || event;
  var target = e.target || e.srcElement;

  if (target.tagName != 'SPAN') {   
    return; // клик был не на заголовке
  }

  var li = target.parentNode; // получить родительский LI

  // получить UL с потомками -- это первый UL внутри LI
  var node = li.getElementsByTagName('ul')[0];

  if (!node) return; // потомков нет -- ничего не надо делать

  // спрятать/показать (можно и через CSS-класс)
  node.style.display = node.style.display ? '' : 'none';
}

Жирные узлы при наведении

Узел выделяется при наведении при помощи CSS-селектора :hover.

Невыделяемость при клике

На всё дерево можно поставить обработчик, отменяющий выделение при клике

tree.onselectstart = tree.onmousedown = function() {
  return false; // делаем узлы невыделяемыми
}

Полное решение вы можете увидеть здесь http://learn.javascript.ru/play/tutorial/browser/events/tree/index.html или:

<!DOCTYPE HTML>
<html>
<head>
<style>
* html body {
  behavior:url("csshover.htc"); /* IE6: http://www.xs4all.nl/~peterned/csshover.html */
}

.tree span:hover {
  font-weight: bold;
}
.tree span {
  cursor: pointer;
}
</style>
<meta charset="utf-8">
</head>
<body>

<ul class="tree">
    <li>Животные
        <ul>
            <li>Млекопитающие
                <ul>
                    <li>Коровы</li>
                    <li>Ослы</li>
                    <li>Собаки</li>
                    <li>Тигры</li>
                </ul>
            </li>
            <li>Другие
                <ul>
                    <li>Змеи</li>
                    <li>Птицы</li>
                    <li>Ящерицы</li>
                </ul>
            </li>
        </ul>
    </li>
    <li>Рыбы
        <ul>
            <li>Аквариумные
                <ul>
                    <li>Гуппи</li>
                    <li>Скалярии</li>
                </ul>

            </li>
            <li>Морские
                <ul>
                    <li>Морская форель</li>
                </ul>
            </li>
        </ul>
    </li>
</ul>

<script>
var tree = document.getElementsByTagName('ul')[0];

var treeLis = tree.getElementsByTagName('li');

/* wrap all textNodes into spans */
for(var i=0; i<treeLis.length; i++) {
  var li = treeLis[i];

  var span = document.createElement('span');
  li.insertBefore(span, li.firstChild);
  span.appendChild(span.nextSibling);
}

/* catch clicks on whole tree */
tree.onclick = function(evt) {
  evt = evt || window.event;
  var target = evt.target || evt.srcElement;

  if (target.tagName != 'SPAN') {
    return;
  }

  /* now we know SPAN is clicked */
  var node = target.parentNode.getElementsByTagName('ul')[0];
  if (!node) return; // no children

  node.style.display = node.style.display ? '' : 'none';
}

tree.onselectstart = tree.onmousedown = function() {
  return false; // делаем узлы невыделяемыми
}
</script>

</body>
</html>

Задача 3

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

Результат должен выглядеть так:

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

Исходный документ: http://learn.javascript.ru/play/tutorial/browser/events/gallery-src/index.html или:

<!DOCTYPE HTML>
<html>
<head>
  <title>Галерея</title>
  <meta charset="utf-8">
      body {
        margin: 0;
        padding: 0;
        font: 75%/120% Arial, Helvetica, sans-serif;
        }
        h2 {
            font: bold 190%/100% Arial, Helvetica, sans-serif;
            margin: 0 0 .2em;
        }
        h2 em {
            font: normal 80%/100% Arial, Helvetica, sans-serif;
            color: #999999;
        }
        #largeImg {
            border: solid 1px #ccc;
            width: 550px;
            height: 400px;
            padding: 5px;
        }
        #thumbs a {
            border: solid 1px #ccc;
            width: 100px;
            height: 100px;
            padding: 3px;
            margin: 2px;
            float: left;
        }
        #thumbs a:hover {
            border-color: #FF9900;
        }
</head>
<body>

<p><img id="largeImg" src="img1-lg.jpg" alt="Large image"></p>

<div id="thumbs">
  <!-- При наведении на изображение показывается встроенная подсказка браузера (title) -->
  <a href="img2-lg.jpg" title="Image 2"><img src="img2-thumb.jpg"></a>
  <a href="img3-lg.jpg" title="Image 3"><img src="img3-thumb.jpg"></a>
  <a href="img4-lg.jpg" title="Image 4"><img src="img4-thumb.jpg"></a>
  <a href="img5-lg.jpg" title="Image 5"><img src="img5-thumb.jpg"></a>
  <a href="img6-lg.jpg" title="Image 6"><img src="img6-thumb.jpg"></a>
</div>

</body>
</html>

P.S. Обратите внимание — клик может быть как на маленьком изображении IMG, так и на A вне него. При этом event.target будет, соответственно, либо IMG, либо A.

Дополнительно:

  1. Если получится — сделайте предзагрузку больших изображений, чтобы при клике они появлялись сразу.
  2. Всё ли в порядке с семантической вёрсткой в HTML исходного документа? Если нет — поправьте, чтобы было, как нужно.

Решение

Решение состоит в том, чтобы добавить обработчик на контейнер #thumbs и отслеживать клики на ссылках.

Когда происходит событие, обработчик должен изменять src #largeImg на href ссылки и заменять alt на ее title.

Код решения:

var largeImg = document.getElementById('largeImg');

document.getElementById('thumbs').onclick = function(e) { e = e || window.event; var target = e.target || e.srcElement;

while(target != this) {

if (target.nodeName == 'A') {
  showThumbnail(target.href, target.title);
  return false;
}

target = target.parentNode;

}

}

function showThumbnail(href, title) { largeImg.src = href; largeImg.alt = title; }

Рабочий пример http://learn.javascript.ru/play/tutorial/browser/events/gallery/index.html или:

<!DOCTYPE HTML>
<html>
<head>
  <title>Галерея</title>
  <meta charset="utf-8">
  <style>
    body {
        margin: 0;
        padding: 0;
        font: 75%/120% Arial, Helvetica, sans-serif;
    }
    h2 {
        font: bold 190%/100% Arial, Helvetica, sans-serif;
        margin: 0 0 .2em;
    }
    h2 em {
        font: normal 80%/100% Arial, Helvetica, sans-serif;
        color: #999999;
    }
    #largeImg {
        border: solid 1px #ccc;
        width: 550px;
        height: 400px;
        padding: 5px;
    }
    #thumbs a {
        border: solid 1px #ccc;
        width: 100px;
        height: 100px;
        padding: 3px;
        margin: 2px;
        float: left;
    }
    #thumbs a:hover {
        border-color: #FF9900;
    }
    #thumbs li {
        list-style: none;
    }
    #thumbs {
        margin: 0;
        padding: 0;
    }
  </style>
</head>

<body>

<p><img id="largeImg" src="img1-lg.jpg" alt="Large image"></p>

<ul id="thumbs">
  <!-- При наведении на изображение показывается встроенная подсказка браузера (title) -->
  <li><a href="img2-lg.jpg" title="Image 2"><img src="img2-thumb.jpg"></a></li>
  <li><a href="img3-lg.jpg" title="Image 3"><img src="img3-thumb.jpg"></a></li>
  <li><a href="img4-lg.jpg" title="Image 4"><img src="img4-thumb.jpg"></a></li>
  <li><a href="img5-lg.jpg" title="Image 5"><img src="img5-thumb.jpg"></a></li>
  <li><a href="img6-lg.jpg" title="Image 6"><img src="img6-thumb.jpg"></a></li>
</ul>

<script>
var largeImg = document.getElementById('largeImg');

var thumbs = document.getElementById('thumbs');

thumbs.onclick = function(e) {
  e = e || window.event;
  var target = e.target || e.srcElement;

  while(target != this) {

    if (target.nodeName == 'A') {
      showThumbnail(target.href, target.title);
      return false;
    }

    target = target.parentNode;
  }

}

function showThumbnail(href, title) {
  largeImg.src = href;
  largeImg.alt = title;
}


/* предзагрузка */
var imgs = thumbs.getElementsByTagName('img');
for(var i=0; i<imgs.length; i++) {
  var url = imgs[i].parentNode.href;
  var img = document.createElement('img');
  img.src = url;
}
</script>

</body>
</html>

Предзагрузка картинок

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

var imgs = thumbs.getElementsByTagName('img');
for(var i = 0; i < imgs.length; i++) {
  var url = imgs[i].parentNode.href;
  var img = document.createElement('img'); 
  img.src = url;
}

Как только элемент создан и ему назначен src, браузер сам начинает скачивать файл картинки.

При правильных настройках сервера как-то использовать этот элемент не обязательно — картинка уже закеширована.

Семантичная верстка

Для списка картинок используется DIV. С точки зрения семантики более верный вариант — список UL/LI.