renovations

This commit is contained in:
Ilya Kantor 2015-01-29 00:09:23 +03:00
parent 6cfa0a2738
commit a2dd0148ab
25 changed files with 279 additions and 284 deletions

View file

@ -58,6 +58,7 @@
Если консоль уже была включена ранее, то этот шаг не нужен, но если она серая -- выберите в меню `Консоль` и включите её: Если консоль уже была включена ранее, то этот шаг не нужен, но если она серая -- выберите в меню `Консоль` и включите её:
<img src="firefox_console_enable.png"> <img src="firefox_console_enable.png">
</li> </li>
<li>Для того, чтобы Firebug работал без глюков, желательно сначала открыть Firebug, а уже потом -- зайти на страницу. <li>Для того, чтобы Firebug работал без глюков, желательно сначала открыть Firebug, а уже потом -- зайти на страницу.
@ -96,6 +97,7 @@
Откройте меню, нажав на колесико справа-сверху и выберите `Настройки`. Откройте меню, нажав на колесико справа-сверху и выберите `Настройки`.
Затем вкладка `Дополнительно`: Затем вкладка `Дополнительно`:
<img src="safari.png"> <img src="safari.png">
Отметьте `Показывать меню "Разработка" в строке меню`. Закройте настройки. Отметьте `Показывать меню "Разработка" в строке меню`. Закройте настройки.

View file

@ -1,8 +1,8 @@
# Итого: DOM-шпаргалка # Итого
В этой статье перечислены основные свойства и методы DOM, которые мы изучили. В этой главе кратко перечислены основные свойства и методы DOM, которые мы изучили.
Используйте её, чтобы быстро подглядеть то, что изучали ранее. Используйте её, чтобы получить быстрый итоговый обзор того, что изучали ранее.
[cut] [cut]

View file

@ -7,12 +7,14 @@
<input type="button" id="hider" value="Нажмите, чтобы спрятать текст"/> <input type="button" id="hider" value="Нажмите, чтобы спрятать текст"/>
<div id="hide">Текст</div> <div id="text">Текст</div>
<script> <script>
document.getElementById('hider').onclick = function() { // в этой задаче неважно, как именно прятать элемент
document.getElementById('hide').style.display = 'none'; // например через style.display:
} document.getElementById('hider').onclick = function() {
document.getElementById('text').style.display = 'none';
}
</script> </script>
</body> </body>
</html> </html>

View file

@ -7,7 +7,7 @@
<input type="button" id="hider" value="Нажмите, чтобы спрятать текст"/> <input type="button" id="hider" value="Нажмите, чтобы спрятать текст"/>
<div id="hide">Текст</div> <div id="text">Текст</div>
<script> <script>
/* ваш код */ /* ваш код */

View file

@ -2,9 +2,9 @@
[importance 5] [importance 5]
Используя JavaScript, сделайте так, чтобы при клике на кнопку исчезал элемент с `id="hide"`. Используя JavaScript, сделайте так, чтобы при клике на кнопку исчезал элемент с `id="text"`.
Демо: Демо:
[iframe border=1 src="solution"] [iframe border=1 src="solution" height=80]

View file

@ -4,5 +4,7 @@
Создайте кнопку, при клике на которую, она будет скрывать сама себя. Создайте кнопку, при клике на которую, она будет скрывать сама себя.
[online]
Как эта: Как эта:
<input type="button" onclick="this.style.display='none'" value="Нажми, чтобы меня спрятать"/> <input type="button" onclick="this.style.display='none'" value="Нажми, чтобы меня спрятать"/>
[/online]

View file

@ -1,17 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<script>
function countRabbits() {
for(var i=1; i<=3; i++) {
alert("Кролик номер " + i);
}
}
</script>
</head>
<body>
<input type="button" onclick="countRabbits()" value="Считать кроликов!"/>
</body>
</html>

View file

@ -9,8 +9,8 @@ function handler() {
alert("1"); alert("1");
} }
button.addEventListener("click", handler, false); button.addEventListener("click", handler);
button.removeEventListener("click", handler, false); button.removeEventListener("click", handler);
``` ```
Обработчик `button.onclick` сработает независимо и в дополнение к назначенному в `addEventListener`. Обработчик `button.onclick` сработает независимо и в дополнение к назначенному в `addEventListener`.

View file

@ -9,9 +9,9 @@
Что будет выведено при клике после выполнения кода? Что будет выведено при клике после выполнения кода?
```js ```js
button.addEventListener("click", function() { alert("1"); }, false); button.addEventListener("click", function() { alert("1"); });
button.removeEventListener("click", function() { alert("1"); }, false); button.removeEventListener("click", function() { alert("1"); });
button.onclick = function() { alert(2); }; button.onclick = function() { alert(2); };
``` ```

View file

@ -13,18 +13,21 @@
} }
.menu .title { .menu .title {
padding-left: 16px;
font-size: 18px; font-size: 18px;
cursor: pointer; cursor: pointer;
background: url(http://js.cx/clipart/arrow-right.png) left center no-repeat;
} }
.menu-open .title { .menu .title::before {
background: url(http://js.cx/clipart/arrow-down.png) left center no-repeat; content: '▶ ';
font-size: 80%;
color: green;
} }
.menu-open ul { .menu.open .title::before {
content: '▼ ';
}
.menu.open ul {
display: block; display: block;
} }
@ -33,7 +36,7 @@
<body> <body>
<div id="sweeties" class="menu"> <div id="sweeties" class="menu">
<span id="sweeties-title" class="title">Сладости (нажми меня)!</span> <span class="title">Сладости (нажми меня)!</span>
<ul> <ul>
<li>Торт</li> <li>Торт</li>
<li>Пончик</li> <li>Пончик</li>
@ -43,14 +46,12 @@
</div> </div>
<script> <script>
var titleElem = document.getElementById('sweeties-title'); var menuElem = document.getElementById('sweeties');
var titleElem = menuElem.querySelector('.title');
titleElem.onclick = function() {
var menu = this.parentNode;
menu.classList.toggle('menu-open');
};
titleElem.onclick = function() {
menuElem.classList.toggle('open');
};
</script> </script>
</body> </body>

View file

@ -5,10 +5,7 @@
</head> </head>
<body> <body>
<img src="http://js.cx/clipart/arrow-down.png"> ▶ ▼ Сладости (нажми меня)!
<img src="http://js.cx/clipart/arrow-right.png">
Сладости (нажми меня)!
<ul> <ul>
<li>Торт</li> <li>Торт</li>
<li>Пончик</li> <li>Пончик</li>
@ -16,6 +13,5 @@
</ul> </ul>
</body> </body>
</html> </html>

View file

@ -6,5 +6,5 @@
[iframe border=1 height=100 src="solution"] [iframe border=1 height=100 src="solution"]
HTML/CSS исходного документа, возможно, понадобится изменить. P.S. HTML/CSS исходного документа понадобится изменить.

View file

@ -1,26 +1,10 @@
# Алгоритм решения
<ol>
<li>Разработать структуру HTML/CSS. Позиционировать кнопку внутри сообщения.</li>
<li>Найти все кнопки</li>
<li>Присвоить им обработчики</li>
<li>Обработчик будет ловить событие на кнопке и удалять соответствующий элемент.</li>
<ol> <ol>
<li>Изменим HTML/CSS, чтобы кнопка была в нужном месте сообщения. Кнопка -- это тег `<button>`, поэтому понадобится несколько стилей.
# Вёрстка Расположить кнопку справа можно при помощи `position:relative` для `pane`, а для кнопки `position:absolute + right/top`. Так как `position:absolute` вынимает элемент из потока, то кнопка может частично оказаться "сверху" текста заголовка, перекрыв его конец. Чтобы этого не произошло, можно добавить `padding-right` к заголовку.
Исправьте HTML/CSS, чтобы кнопка была в нужном месте сообщения. Кнопку лучше сделать как `div`, а картинка --- будет его `background`. Это более правильно, чем `img`, т.к. в данном случае картинка является *оформлением кнопки*, а оформление должно быть в CSS. Если использовать `float:right`, то кнопка никогда не перекроет текст. Это, пожалуй хорошо.
Расположить кнопку справа можно при помощи `position: relative` для `pane`, а для кнопки `position: absolute + right/top`. Так как `position: absolute` вынимает элемент из потока, то кнопка может перекрыть текст заголовка. Чтобы этого не произошло, можно добавить `padding-right` к заголовку. С другой стороны, потенциальным преимуществом способа с `position` по сравнению с `float` в данном случае является возможность поместить элемент кнопки в HTML *после текста*, а не до него.</li>
<li>Для того, чтобы получить кнопку из контейнера, используем `querySelectorAll`. На каждую кнопку повесим обработчик, который будет убирать родителя. Найти родителя можно через `parentNode`.</li>
Потенциальным преимуществом способа с `position` по сравнению с `float` в данном случае является возможность поместить элемент кнопки в HTML *после текста*, а не до него. </ol>
# Обработчики
Для того, чтобы получить кнопку из контейнера, можно найти все `IMG` в нём и выбрать из них кнопку по `className`. На каждую кнопку можно повесить обработчик.
# Решение
[edit src="solution"]Решение в песочнице[/edit]
Для поиска элементов `span` с нужным классом в нём используется `getElementsByTagName` с фильтрацией. К сожалению, это единственный способ, доступный в IE 6,7. Если же эти браузеры вам не нужны, то гораздо лучше -- искать элементы при помощи `querySelector` или `getElementsByClassName`.

View file

@ -3,26 +3,6 @@
<head> <head>
<link rel="stylesheet" href="messages.css"> <link rel="stylesheet" href="messages.css">
<meta charset="utf-8"> <meta charset="utf-8">
<style>
.pane {
position: relative;
}
.remove-button {
position: absolute;
top: 10px;
right: 10px;
cursor: pointer;
display: block;
background: url(http://js.cx/clipart/delete.gif) no-repeat;
width: 16px;
height: 16px;
}
h3 {
padding-right: 20px;
}
</style>
</head> </head>
<body> <body>
@ -31,17 +11,17 @@
<div class="pane"> <div class="pane">
<h3>Лошадь</h3> <h3>Лошадь</h3>
<p>Домашняя лошадь — животное семейства непарнокопытных, одомашненный и единственный сохранившийся подвид дикой лошади, вымершей в дикой природе, за исключением небольшой популяции лошади Пржевальского.</p> <p>Домашняя лошадь — животное семейства непарнокопытных, одомашненный и единственный сохранившийся подвид дикой лошади, вымершей в дикой природе, за исключением небольшой популяции лошади Пржевальского.</p>
<span class="remove-button"></span> <button class="remove-button">[x]</button>
</div> </div>
<div class="pane"> <div class="pane">
<h3>Осёл</h3> <h3>Осёл</h3>
<p>Домашний осёл или ишак — одомашненный подвид дикого осла, сыгравший важную историческую роль в развитии хозяйства и культуры человека. Все одомашненные ослы относятся к африканским ослам.</p> <p>Домашний осёл или ишак — одомашненный подвид дикого осла, сыгравший важную историческую роль в развитии хозяйства и культуры человека. Все одомашненные ослы относятся к африканским ослам.</p>
<span class="remove-button"></span> <button class="remove-button">[x]</button>
</div> </div>
<div class="pane"> <div class="pane">
<h3>Корова, а также пара слов о диком быке, о волах и о тёлках. </h3> <h3>Корова, а также пара слов о диком быке, о волах и о тёлках. </h3>
<p>Коро́ва — самка домашнего быка, одомашненного подвида дикого быка, парнокопытного жвачного животного семейства полорогих. Самцы вида называются быками, молодняк — телятами, кастрированные самцы — волами. Молодых (до первой стельности) самок называют тёлками.</p> <p>Коро́ва — самка домашнего быка, одомашненного подвида дикого быка, парнокопытного жвачного животного семейства полорогих. Самцы вида называются быками, молодняк — телятами, кастрированные самцы — волами. Молодых (до первой стельности) самок называют тёлками.</p>
<span class="remove-button"></span> <button class="remove-button">[x]</button>
</div> </div>
</div> </div>
@ -49,13 +29,12 @@
<script> <script>
var spans = document.getElementById('messages-container').getElementsByTagName('span'); var buttons = document.querySelectorAll('#messages-container .remove-button');
for(var i=0; i<spans.length; i++) { for(var i=0; i<buttons.length; i++) {
var span = spans[i]; var button = buttons[i];
if (span.className != 'remove-button') continue;
span.onclick = function() { button.onclick = function() {
var el = this.parentNode; var el = this.parentNode;
el.parentNode.removeChild(el); el.parentNode.removeChild(el);
}; };

View file

@ -2,31 +2,35 @@ body {
margin: 10px auto; margin: 10px auto;
width: 470px; width: 470px;
} }
h3 { h3 {
margin: 0; margin: 0;
padding-bottom: .3em; padding-bottom: .3em;
padding-right: 20px; padding-right: 20px;
font-size: 1.1em; font-size: 1.1em;
} }
p { p {
margin: 0; margin: 0;
padding: 0 0 .5em; padding: 0 0 .5em;
} }
.pane { .pane {
background: #edf5e1; background: #edf5e1;
padding: 10px 20px 10px; padding: 10px 20px 10px;
border-top: solid 2px #c4df9b; border-top: solid 2px #c4df9b;
position: relative; position: relative;
} }
.remove-button { .remove-button {
position: absolute; position: absolute;
top: 10px; font-size: 110%;
right: 10px; top: 0;
cursor: pointer; color: red;
display: block; right: 10px;
background: url(delete.gif) no-repeat; display: block;
width: 16px; width: 24px;
height: 16px; height: 24px;
border: none;
background: transparent;
} }

View file

@ -7,7 +7,7 @@
<body> <body>
Картинка для кнопки удаления: <img src="http://js.cx/clipart/delete.gif"> Кнопка для удаления: <button class="remove-button">[x]</button>
<div> <div>
<div class="pane"> <div class="pane">

View file

@ -2,17 +2,30 @@ body {
margin: 10px auto; margin: 10px auto;
width: 470px; width: 470px;
} }
h3 { h3 {
margin: 0; margin: 0;
padding-bottom: .3em; padding-bottom: .3em;
font-size: 1.1em; font-size: 1.1em;
} }
p { p {
margin: 0; margin: 0;
padding: 0 0 .5em; padding: 0 0 .5em;
} }
.pane { .pane {
background: #edf5e1; background: #edf5e1;
padding: 10px 20px 10px; padding: 10px 20px 10px;
border-top: solid 2px #c4df9b; border-top: solid 2px #c4df9b;
} }
.remove-button {
font-size: 110%;
color: red;
right: 10px;
width: 24px;
height: 24px;
border: none;
background: transparent;
}

View file

@ -5,8 +5,8 @@
Есть список сообщений. Добавьте каждому сообщению по кнопке для его скрытия. Есть список сообщений. Добавьте каждому сообщению по кнопке для его скрытия.
Результат: Результат:
[iframe src="solution"] [iframe src="solution" height=450]
Как лучше отобразить кнопку справа-сверху: через `position:absolute` или `float`? P.S. Как лучше отобразить кнопку справа-сверху: через `position:absolute` или `float:right`? Почему?

View file

@ -1,25 +1,19 @@
Лента изображений должна быть оформлена как список, согласно принципам семантической вёрстки. Лента изображений в разметке должна быть представлена как список `<ul>` тегов `<img>`.
Нужно стилизовать его так, чтобы он был длинной лентой, из которой внешний `DIV` вырезает нужную часть для просмотра: Нужно расположить его внутри `<div>` фиксированного размера, так чтобы в один момент была видна только нужная часть списка:
<img src="carousel1.png"> <img src="carousel1.png">
Чтобы список был длинный и элементы не переходили вниз, ему ставится `width: 9999px`, а элементам, соответственно, `float:left`. Чтобы список был длинный и элементы не переходили вниз, ему ставится `width: 9999px`, а элементам `<li>`, соответственно, `float:left`, либо для элементов используется `display: inline-block`, как в этом решении.
[warn header="Не используйте display:inline"] Главное -- не использовать `display:inline`, так как такие элементы имеют дополнительные отступы снизу для возможных "хвостов букв".
Элементы с `display:inline` имеют дополнительные отступы для возможных "хвостов букв".
В частности, для `img` нужно поставить в стилях явно `display:block`, чтобы пространства под ними не оставалось. В частности, для `<img>` нужно поставить в стилях явно `display:block`, чтобы пространства под ними не оставалось.
[/warn]
При прокрутке UL сдвигается назначением `margin-left`: Для "прокрутки" будем сдвигать `<ul>`. Это можно делать по-разному, например, назначением `margin-left`:
<img src="carousel2.png"> <img src="carousel2.png">
У внешнего `DIV` фиксированная ширина, поэтому "лишние" изображения обрезаются. У внешнего `<div>` фиксированная ширина, поэтому "лишние" изображения обрезаются.
Снаружи окошка находятся стрелки и внешний контейнер. Снаружи окошка находятся стрелки и внешний контейнер.
Реализуйте эту структуру, и к ней прикручивайте обработчики, которые меняют `ul.style.marginLeft`.

View file

@ -1,28 +1,28 @@
<!DOCTYPE html> <!DOCTYPE html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="style.css"> <link rel="stylesheet" href="style.css">
</head> </head>
<body> <body>
<div class="carousel"> <div id="carousel" class="carousel">
<a href="#" class="arrow left-arrow" id="prev">&#8249; </a> <button class="arrow prev"></button>
<div class="gallery"> <div class="gallery">
<ul id="images"> <ul class="images">
<li><img src="http://js.cx/carousel/1.png"></li> <li><img src="http://js.cx/carousel/1.png">
<li><img src="http://js.cx/carousel/2.png"></li> <li><img src="http://js.cx/carousel/2.png">
<li><img src="http://js.cx/carousel/3.png"></li> <li><img src="http://js.cx/carousel/3.png">
<li><img src="http://js.cx/carousel/4.png"></li> <li><img src="http://js.cx/carousel/4.png">
<li><img src="http://js.cx/carousel/5.png"></li> <li><img src="http://js.cx/carousel/5.png">
<li><img src="http://js.cx/carousel/6.png"></li> <li><img src="http://js.cx/carousel/6.png">
<li><img src="http://js.cx/carousel/7.png"></li> <li><img src="http://js.cx/carousel/7.png">
<li><img src="http://js.cx/carousel/8.png"></li> <li><img src="http://js.cx/carousel/8.png">
<li><img src="http://js.cx/carousel/9.png"></li> <li><img src="http://js.cx/carousel/9.png">
<li><img src="http://js.cx/carousel/10.png"></li> <li><img src="http://js.cx/carousel/10.png">
</ul> </ul>
</div> </div>
<a href="#" class="arrow right-arrow" id="next">&#8250; </a> <button class="arrow next"></button>
</div> </div>
<script> <script>
@ -43,27 +43,28 @@ for(var i=0; i<lis.length; i++) {
var width = 130; // ширина изображения var width = 130; // ширина изображения
var count = 3; // количество изображений var count = 3; // количество изображений
var ul = document.getElementById('images'); var carousel = document.getElementById('carousel');
var imgs = ul.getElementsByTagName('li'); var list = carousel.querySelector('ul');
var listElems = carousel.querySelectorAll('li');
var position = 0; // текущий сдвиг влево var position = 0; // текущий сдвиг влево
document.getElementById('prev').onclick = function() { carousel.querySelector('.prev').onclick = function() {
if (position >= 0) return false; // уже до упора if (position >= 0) return; // уже сдвинулись до упора
// сдвиг влево
// последнее передвижение влево может быть не на 3, а на 2 или 1 элемент // последнее передвижение влево может быть не на 3, а на 2 или 1 элемент
position = Math.min(position + width*count, 0) position = Math.min(position + width*count, 0)
ul.style.marginLeft = position + 'px'; list.style.marginLeft = position + 'px';
return false; };
}
document.getElementById('next').onclick = function() { carousel.querySelector('.next').onclick = function() {
if (position <= -width*(imgs.length-count)) return false; // уже до упора if (position <= -width*(listElems.length-count)) return; // уже до упора
// сдвиг вправо
// последнее передвижение вправо может быть не на 3, а на 2 или 1 элемент // последнее передвижение вправо может быть не на 3, а на 2 или 1 элемент
position = Math.max(position-width*count, -width*(imgs.length-count)); position = Math.max(position-width*count, -width*(listElems.length-count));
ul.style.marginLeft = position + 'px'; list.style.marginLeft = position + 'px';
return false;
}; };
</script> </script>

View file

@ -1,5 +1,5 @@
body { body {
padding: 10px padding: 10px;
} }
.carousel { .carousel {
@ -10,43 +10,57 @@ body {
border-radius: 15px; border-radius: 15px;
background: #eee; background: #eee;
} }
.carousel img { .carousel img {
width: 130px; width: 130px;
height: 130px; height: 130px;
display: block; /* если не поставить block, в ряде браузеров будет inline -> лишнее пространтсво вокруг элементов */ /* по умолчанию inline, в ряде браузеров это даст лишнее пространство вокруг элементов */
} display: block;
.carousel .arrow {
position: absolute;
top: 57px;
padding: 3px 8px 8px 9px;
background: #ddd;
border-radius: 15px;
font-size: 24px;
color: #444;
text-decoration: none;
} }
.carousel .arrow:hover { .arrow {
background: #ccc; position: absolute;
top: 60px;
padding: 0;
background: #ddd;
border-radius: 15px;
border: 1px solid gray;
font-size: 24px;
line-height: 24px;
color: #444;
display: block;
} }
.carousel .left-arrow {
.arrow:focus {
outline: none;
}
.arrow:hover {
background: #ccc;
cursor: pointer;
}
.prev {
left: 7px; left: 7px;
} }
.carousel .right-arrow { .next {
right: 7px; right: 7px;
} }
.gallery { .gallery {
width: 390px; width: 390px;
overflow: hidden; overflow: hidden;
} }
.gallery ul { .gallery ul {
height: 130px; height: 130px;
width: 9999px; width: 9999px;
margin: 0; margin: 0;
padding: 0; padding: 0;
list-style: none; list-style: none;
transition: margin-left 250ms;
} }
.gallery ul li { .gallery li {
float: left; display: inline-block;
} }

View file

@ -1,22 +1,28 @@
<!DOCTYPE html> <!DOCTYPE html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<link rel="stylesheet" href="style.css">
</head> </head>
<body> <body>
<!-- ваша верстка виджета, ваши стили --> <!-- ваша верстка виджета, ваши стили -->
<ul id="images">
<li><img src="http://js.cx/carousel/1.png"></li> <button class="arrow"></button>
<li><img src="http://js.cx/carousel/2.png"></li> <button class="arrow"></button>
<li><img src="http://js.cx/carousel/3.png"></li>
<li><img src="http://js.cx/carousel/4.png"></li>
<li><img src="http://js.cx/carousel/5.png"></li> <ul>
<li><img src="http://js.cx/carousel/6.png"></li> <li><img src="http://js.cx/carousel/1.png">
<li><img src="http://js.cx/carousel/7.png"></li> <li><img src="http://js.cx/carousel/2.png">
<li><img src="http://js.cx/carousel/8.png"></li> <li><img src="http://js.cx/carousel/3.png">
<li><img src="http://js.cx/carousel/9.png"></li> <li><img src="http://js.cx/carousel/4.png">
<li><img src="http://js.cx/carousel/10.png"></li> <li><img src="http://js.cx/carousel/5.png">
<li><img src="http://js.cx/carousel/6.png">
<li><img src="http://js.cx/carousel/7.png">
<li><img src="http://js.cx/carousel/8.png">
<li><img src="http://js.cx/carousel/9.png">
<li><img src="http://js.cx/carousel/10.png">
</ul> </ul>

View file

@ -0,0 +1,40 @@
.arrow {
padding: 0;
background: #ddd;
border-radius: 15px;
border: 1px solid gray;
font-size: 24px;
line-height: 24px;
color: #444;
display: block;
}
.arrow:focus {
outline: none;
}
.arrow:hover {
background: #ccc;
cursor: pointer;
}
ul {
height: 130px;
width: 9999px;
margin: 0;
padding: 0;
list-style: none;
}
ul img {
width: 130px;
height: 130px;
/* по умолчанию inline, но в ряде браузеров это даст лишнее пространство вокруг элементов */
display: block;
}
ul li {
/* если в HTML между элементами <li>..</li> есть пробелы, то они будут видны на странице,
чтобы их избежать, нужно либо писать <li>..</li><li>..</li> вплотную, либо не ставить закрывающие </li> */
display: inline-block;
}

View file

@ -1,6 +1,6 @@
# Карусель # Карусель
[importance 5] [importance 4]
Напишите "Карусель" -- ленту изображений, которую можно листать влево-вправо нажатием на стрелочки. Напишите "Карусель" -- ленту изображений, которую можно листать влево-вправо нажатием на стрелочки.
@ -8,5 +8,5 @@
В дальнейшем к ней можно легко добавить анимацию, динамическую подгрузку и другие возможности. В дальнейшем к ней можно легко добавить анимацию, динамическую подгрузку и другие возможности.
В этой задаче разработка HTML/CSS-структуры составляет 90% решения. P.S. В этой задаче разработка HTML/CSS-структуры составляет 90% решения.

View file

@ -2,11 +2,9 @@
Для реакции на действия посетителя и внутреннего взаимодействия скриптов существуют *события*. Для реакции на действия посетителя и внутреннего взаимодействия скриптов существуют *события*.
*Событие* - это сигнал от браузера о том, что что-то произошло.
[cut] [cut]
Существует много видов событий.
Посмотрим список самых часто используемых, пока просто для ознакомления: *Событие* -- это сигнал от браузера о том, что что-то произошло. Существует много видов событий. Посмотрим список самых часто используемых, пока просто для ознакомления:
<dl> <dl>
<dt>События мыши</dt> <dt>События мыши</dt>
@ -48,9 +46,9 @@
## Назначение обработчиков событий ## Назначение обработчиков событий
Событию можно назначить обработчик, то есть функцию, которая сработает, как только событие произошло. Событию можно назначить *обработчик*, то есть функцию, которая сработает, как только событие произошло.
Именно благодаря событиям JavaScript-код может реагировать на действия посетителя. Именно благодаря обработчикам JavaScript-код может реагировать на действия посетителя.
Есть несколько способов назначить событию обработчик. Сейчас мы их рассмотрим, начиная от самого простого. Есть несколько способов назначить событию обработчик. Сейчас мы их рассмотрим, начиная от самого простого.
@ -66,24 +64,40 @@
При клике мышкой на кнопке выполнится код, указанный в атрибуте `onclick`. При клике мышкой на кнопке выполнится код, указанный в атрибуте `onclick`.
В действии: [online]
В действии: <input value="Нажми меня" onclick="alert('Клик!');" type="button">
[/online]
<input value="Нажми меня" onclick="alert('Клик!');" type="button"> Обратите внимание, для содержимого атрибута `onclick` используются *одинарные кавычки*, так как сам атрибут находится в двойных.
Обратите внимание, для строки *внутри* `alert('Клик!')` используются *одиночные кавычки*, так как сам атрибут находится в двойных. Частая ошибка новичков в том, что они забывают, что код находится внутри атрибута. Запись вида `onclick="alert("Клик!")"`, с двойными кавычки внутри, не будет работать. Если вам действительно нужно использовать именно двойные кавычки, то это можно сделать, заменив их на `&quot;`, то есть так: <code>onclick="alert(&amp;quot;Клик!&amp;quot;)"</code>.
Частая ошибка новичков в том, что они забывают, что код находится внутри атрибута. Запись вида `onclick="alert("Клик!")"` не будет работать. Если вам действительно нужно использовать именно двойные кавычки, то это можно сделать, заменив их на `&quot;`: <code>onclick="alert(&amp;quot;Клик!&amp;quot;)"</code>. Однако, обычно этого не требуется, так как прямо в разметке пишутся только очень простые обработчики. Если нужно сделать что-то сложное, то имеет смысл описать это в функции, и в обработчике вызвать уже её.
Однако, обычно этого не требуется, так как в разметке пишутся только очень простые обработчики. Если нужно сделать что-то сложное, то имеет смысл описать это в функции, и в обработчике вызвать уже её.
Следующий пример по клику запускает функцию `countRabbits()`. Следующий пример по клику запускает функцию `countRabbits()`.
```html ```html
<!--+ src="2.html" run height=80 --> <!--+ run height=80 -->
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<script>
function countRabbits() {
for(var i=1; i<=3; i++) {
alert("Кролик номер " + i);
}
}
</script>
</head>
<body>
<input type="button" *!*onclick="countRabbits()"*/!* value="Считать кроликов!"/>
</body>
</html>
``` ```
Как мы помним, атрибут HTML-тега не чувствителен к регистру, поэтому `ONCLICK` будет работать так же, как `onClick` или `onclick`... Но, как правило, атрибуты пишут в нижнем регистре: `onclick`. Как мы помним, атрибут HTML-тега не чувствителен к регистру, поэтому `ONCLICK` будет работать так же, как `onClick` или `onCLICK`... Но, как правило, атрибуты пишут в нижнем регистре: `onclick`.
### Использование свойства DOM-объекта ### Использование свойства DOM-объекта
@ -102,17 +116,11 @@ elem.onclick = function() {
</script> </script>
``` ```
В действии:
<input id="elem" type="button" value="Нажми меня"/>
<script>
elem.onclick = function() {
alert('Спасибо');
};
</script>
Если обработчик задан через атрибут, то браузер читает HTML-разметку, создаёт новую функцию из содержимого атрибута и записывает в свойство `onclick`. Если обработчик задан через атрибут, то браузер читает HTML-разметку, создаёт новую функцию из содержимого атрибута и записывает в свойство `onclick`.
**Обработчик хранится именно в свойстве, а атрибут -- лишь один из способов его инициализации.** **Этот способ, по сути, аналогичен предыдущему.**
Обработчик хранится именно в DOM-свойстве, а атрибут -- лишь один из способов его инициализации.
Эти два примера кода работают одинаково: Эти два примера кода работают одинаково:
@ -142,7 +150,7 @@ button.onclick = function() {
</li> </li>
</ol> </ol>
**Так как свойство, в итоге, одно, то назначить более одного обработчика так нельзя.** **Так как DOM-свойство `onclick`, в итоге, одно, то назначить более одного обработчика так нельзя.**
В примере ниже назначение через JavaScript перезапишет обработчик из атрибута: В примере ниже назначение через JavaScript перезапишет обработчик из атрибута:
@ -152,7 +160,7 @@ button.onclick = function() {
<script> <script>
*!* *!*
elem.onclick = function() { // перезапишет существующий обработчик elem.onclick = function() { // перезапишет существующий обработчик
alert('После'); alert('После'); // выведется только это
}; };
*/!* */!*
</script> </script>
@ -170,7 +178,7 @@ elem.onclick = sayThanks;
Если обработчик надоел -- его всегда можно убрать назначением `elem.onclick = null`. Если обработчик надоел -- его всегда можно убрать назначением `elem.onclick = null`.
### Доступ к элементу через this ## Доступ к элементу через this
Внутри обработчика события `this` ссылается на текущий элемент, то есть на тот, на котором он сработал. Внутри обработчика события `this` ссылается на текущий элемент, то есть на тот, на котором он сработал.
@ -182,11 +190,11 @@ elem.onclick = sayThanks;
<button onclick="alert(this.innerHTML)">Нажми меня</button> <button onclick="alert(this.innerHTML)">Нажми меня</button>
``` ```
В действии: [online]
<button onclick="alert(this.innerHTML)">Нажми меня</button> В действии: <button onclick="alert(this.innerHTML)">Нажми меня</button>
[/online]
## Частые ошибки
### Частые ошибки
Если вы только начинаете работать с событиями -- обратите внимание на следующие особенности. Если вы только начинаете работать с событиями -- обратите внимание на следующие особенности.
@ -206,7 +214,7 @@ button.onclick = sayThanks;
<input type="button" id="button" onclick="sayThanks()"/> <input type="button" id="button" onclick="sayThanks()"/>
``` ```
Это различие просто объяснить. При создании обработчика браузером по разметке, он автоматически создает функцию из его содержимого. Поэтому последний пример -- фактически то же самое, что: Это различие просто объяснить. При создании обработчика браузером из атрибута, он автоматически создает функцию из его содержимого. Поэтому последний пример -- фактически то же самое, что:
```js ```js
button.onclick = function() { button.onclick = function() {
@ -219,9 +227,7 @@ button.onclick = function() {
</dd> </dd>
<dt>Используйте именно функции, а не строки.</dt> <dt>Используйте именно функции, а не строки.</dt>
<dd> <dd>
Назначение обработчика строкой `elem.onclick = 'alert(1)'` будет работать, но не рекомендуется, могут быть проблемы при сжатии JavaScript. Назначение обработчика строкой `elem.onclick = "alert(1)"` можно иногда увидеть в древнем коде. Это будет работать, но не рекомендуется, могут быть проблемы при сжатии JavaScript Да и вообще, передавать код в виде строки по меньшей мере странно в языке, который поддерживает Function Expressions. Это возможно лишь по соображениям совместимости, не делайте так.
Передавать код в виде строки по меньшей мере странно в языке, который поддерживает Function Expressions, оно здесь доступно только по соображениям совместимости с древними временами.
</dd> </dd>
<dt>Не используйте `setAttribute`.</dt> <dt>Не используйте `setAttribute`.</dt>
<dd> <dd>
@ -235,14 +241,12 @@ document.body.setAttribute('onclick', function() { alert(1) });
``` ```
</dd> </dd>
<dt>Регистр свойства имеет значение.</dt> <dt>Регистр DOM-свойства имеет значение.</dt>
<dd>Свойство называется `onclick`, а не `ONCLICK`.</dd> <dd>При назначении через DOM нужно использовать свойство `onclick`, а не `ONCLICK`.</dd>
</dl> </dl>
## Недостаток назначения через свойство
## Специальные методы
Фундаментальный недостаток описанных выше способов назначения обработчика -- невозможность повесить *несколько* обработчиков на одно событие. Фундаментальный недостаток описанных выше способов назначения обработчика -- невозможность повесить *несколько* обработчиков на одно событие.
@ -256,16 +260,16 @@ input.onclick = function() { alert(1); }
input.onclick = function() { alert(2); } // заменит предыдущий обработчик input.onclick = function() { alert(2); } // заменит предыдущий обработчик
``` ```
Разработчики стандартов достаточно давно это поняли и предложили альтернативный способ назначения обработчиков при помощи специальных методов, который свободен от указанного недостатка. Разработчики стандартов достаточно давно это поняли и предложили альтернативный способ назначения обработчиков при помощи специальных методов, которые свободны от указанного недостатка.
### addEventListener и removeEventListener ## addEventListener и removeEventListener
Методы `addEventListener` и `removeEventListener` являются современным способом назначить или удалить обработчик, и при этом позволяют использовать сколько угодно любых обработчиков. Методы `addEventListener` и `removeEventListener` являются современным способом назначить или удалить обработчик, и при этом позволяют использовать сколько угодно любых обработчиков.
Назначение обработчика осуществляется вызовом `addEventListener` с тремя аргументами: Назначение обработчика осуществляется вызовом `addEventListener` с тремя аргументами:
```js ```js
element.addEventListener( event, handler, phase ); element.addEventListener( event, handler[, phase]);
``` ```
<dl> <dl>
@ -274,27 +278,28 @@ element.addEventListener( event, handler, phase );
<dt>`handler`</dt> <dt>`handler`</dt>
<dd>Ссылка на функцию, которую надо поставить обработчиком.</dd> <dd>Ссылка на функцию, которую надо поставить обработчиком.</dd>
<dt>`phase`</dt> <dt>`phase`</dt>
<dd>Фаза, на которой обработчик должен сработать. Этот аргумент мы рассмотрим далее в учебнике. Пока что будем использовать значение `phase = false`, которое нужно в 99% случаев.</dd> <dd>Необязательный аргумент, "фаза", на которой обработчик должен сработать. Этот аргумент редко нужен, мы его рассмотрим позже.</dd>
</dl> </dl>
Удаление обработчика осуществляется вызовом `removeEventListener`: Удаление обработчика осуществляется вызовом `removeEventListener`:
```js ```js
element.removeEventListener( event, handler, phase ); // передать те же аргументы, что были у addEventListener
element.removeEventListener( event, handler[, phase]);
``` ```
[warn header="Удаление требует ту же функцию"] [warn header="Удаление требует именно ту же функцию"]
Для удаления нужно передать именно ту функцию-обработчик которая была назначена. Для удаления нужно передать именно ту функцию-обработчик которая была назначена.
Вот так `removeEventListener` не сработает: Вот так `removeEventListener` не сработает:
```js ```js
input.addEventListener( "click" , function() {alert('Спасибо!')}, false); elem.addEventListener( "click" , function() {alert('Спасибо!')});
// .... // ....
input.removeEventListener( "click", function() {alert('Спасибо!')}, false); elem.removeEventListener( "click", function() {alert('Спасибо!')});
``` ```
Это не одна и та же функция, а две независимо созданные (с одинаковым кодом, но это не важно). В `removeEventListener` передана не та же функция, а другая, с одинаковым кодом, но это не важно.
Вот так правильно: Вот так правильно:
@ -303,14 +308,15 @@ function handler() {
alert('Спасибо!'); alert('Спасибо!');
} }
input.addEventListener( "click" , handler, false); input.addEventListener( "click", handler);
// .... // ....
input.removeEventListener( "click", handler, false); input.removeEventListener( "click", handler);
``` ```
Обратим внимание -- если функцию не сохранить где-либо, а просто передать в `addEventListener`, как в предыдущем коде, то потом получить её обратно, чтобы снять обработчик, будет невозможно. Нет метода, который позволяет считать обработчики событий, назанченные через `addEventListener`.
[/warn] [/warn]
**Использование `addEventListener` позволяет добавлять несколько обработчиков на одно событие одного элемента:** Метод `addEventListener` позволяет добавлять несколько обработчиков на одно событие одного элемента, например:
```html ```html
<!--+ run --> <!--+ run -->
@ -327,28 +333,28 @@ input.removeEventListener( "click", handler, false);
*!* *!*
elem.onclick = function() { alert("Привет"); }; elem.onclick = function() { alert("Привет"); };
elem.addEventListener("click", handler1, false); // Спасибо! elem.addEventListener("click", handler1); // Спасибо!
elem.addEventListener("click", handler2, false); // Спасибо ещё раз! elem.addEventListener("click", handler2); // Спасибо ещё раз!
*/!* */!*
</script> </script>
``` ```
Как видно из примера выше, можно одновременно назначать обработчики и через `onсвойство` (только один) и через `addEventListener`. Однако, во избежание путаницы обычно рекомендуется выбрать один способ. Как видно из примера выше, можно одновременно назначать обработчики и через DOM-свойство и через `addEventListener`. Однако, во избежание путаницы, рекомендуется выбрать один способ.
[warn header="`addEventListener` работает всегда, а `onсвойство` -- нет"] [warn header="`addEventListener` работает всегда, а DOM-свойство -- нет"]
У специальных методов есть ещё одно перимущество перед "старой школой". У специальных методов есть ещё одно перимущество перед DOM-свойствами.
Есть некоторые события, которые нельзя назначить через `onсвойство`, но можно через `addEventListener`. Есть некоторые события, которые нельзя назначить через DOM-свойство, но можно через `addEventListener`.
Например, таково событие `transitionend`, то есть окончание CSS-анимации. В большинстве браузеров оно требует назначения через `addEventListener`. Например, таково событие `transitionend`, то есть окончание CSS-анимации. В большинстве браузеров оно требует назначения через `addEventListener`.
При нажатии на кнопку в примере ниже сработает второй обработчик, но не первый. Вы можете проверить это, запустив код в примере ниже. Как правило, сработает лишь второй обработчик, но не первый.
```html ```html
<!--+ autorun run --> <!--+ run -->
<style> <style>
button { button {
transition: width 3s; transition: width 1s;
width: 100px; width: 100px;
} }
.wide { .wide {
@ -375,10 +381,9 @@ input.removeEventListener( "click", handler, false);
[/warn] [/warn]
## Отличия IE8- ## Отличия IE8-
При работе с событиями в IE8- есть много отличий. Как правило, они формальны -- некое свойство или метод называются по-другому. Начиная с версии 9, также работают и стандартные свойства и методы, которые предпочтительны. При работе с событиями в IE8- есть много отличий. Как правило, они формальны -- некое свойство или метод называются по-другому. Начиная с версии 9, также работают и стандартные свойства и методы.
**В IE8- вместо `addEventListener/removeEventListener` используются свои методы.** **В IE8- вместо `addEventListener/removeEventListener` используются свои методы.**
@ -405,8 +410,7 @@ button.attachEvent( "onclick" , handler) // Назначение обработ
button.detachEvent( "onclick", handler) // Удаление обработчика button.detachEvent( "onclick", handler) // Удаление обработчика
``` ```
Как видите, почти то же самое, только событие должно включать префикс `on` и нет третьего аргумента, который нам пока не нужен. Как видите, почти то же самое, только событие должно включать префикс `on`.
[warn header="У обработчиков, назначенных с `attachEvent`, нет `this`"] [warn header="У обработчиков, назначенных с `attachEvent`, нет `this`"]
Обработчики, назначенные с `attachEvent` не получают `this`! Обработчики, назначенные с `attachEvent` не получают `this`!
@ -414,37 +418,7 @@ button.detachEvent( "onclick", handler) // Удаление обработчик
Это важная особенность и подводный камень старых IE. Это важная особенность и подводный камень старых IE.
[/warn] [/warn]
### Кроссбраузерный способ назначения обработчиков Чтобы ваш код работал в старом IE, нужно либо использовать DOM-свойства, то есть `onclick`, либо подключить полифилл для современных методов, например [такой](https://gist.github.com/jonathantneal/3748027) или с сервиса [polyfill.io](http://polyfill.webservices.ft.com/v1/docs/features/) или какой-то другой.
Можно объединить способы для IE<9 и современных браузеров, создав свои методы `addEvent(elem, type, handler)` и `removeEvent(elem, type, handler)`:
```js
var addEvent, removeEvent;
if (document.addEventListener) { // проверка существования метода
addEvent = function(elem, type, handler) {
elem.addEventListener(type, handler, false);
};
removeEvent = function(elem, type, handler) {
elem.removeEventListener(type, handler, false);
};
} else {
addEvent = function(elem, type, handler) {
elem.attachEvent("on" + type, handler);
};
removeEvent = function(elem, type, handler) {
elem.detachEvent("on" + type, handler);
};
}
...
// использование:
addEvent(elem, "click", function() { alert("Привет"); });
```
Это хорошо работает в большинстве случаев, но у обработчика не будет `this` в IE, потому что `attachEvent` не поддерживает `this`.
Кроме того, в IE7- есть проблемы с утечками памяти... Но если вам не нужно `this`, и вы не боитесь утечек (как вариант -- не поддерживаете IE7-), то это решение может подойти.
## Итого ## Итого
@ -456,8 +430,8 @@ addEvent(elem, "click", function() { alert("Привет"); });
<li>Свойство: <code>elem.onclick = function</code>.</li> <li>Свойство: <code>elem.onclick = function</code>.</li>
<li>Специальные методы: <li>Специальные методы:
<ul> <ul>
<li>Для IE8-: `elem.attachEvent( on+событие, handler )` (удаление через `detachEvent`).</li> <li>Современные: `elem.addEventListener( событие, handler[, phase])`, удаление через `removeEventListener`.</li>
<li>Для остальных: `elem.addEventListener( событие, handler, false )` (удаление через `removeEventListener`).</li> <li>Для старых IE8-: `elem.attachEvent( on+событие, handler )`, удаление через `detachEvent`.</li>
</ul> </ul>
</li> </li>
</ol> </ol>
@ -470,7 +444,7 @@ addEvent(elem, "click", function() { alert("Привет"); });
-Метод `onclick` кросс-браузерный. -Метод `onclick` кросс-браузерный.
[/compare] [/compare]
**Этим введением мы только начинаем работу с событиями, но вы уже можете решать разнообразные задачи с их использованием.** Этим введением мы только открывает работу с событиями, но вы уже можете решать разнообразные задачи с их использованием.
[head] [head]