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

View file

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

View file

@ -7,12 +7,14 @@
<input type="button" id="hider" value="Нажмите, чтобы спрятать текст"/>
<div id="hide">Текст</div>
<div id="text">Текст</div>
<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>
</body>
</html>

View file

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

View file

@ -2,9 +2,9 @@
[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="Нажми, чтобы меня спрятать"/>
[/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");
}
button.addEventListener("click", handler, false);
button.removeEventListener("click", handler, false);
button.addEventListener("click", handler);
button.removeEventListener("click", handler);
```
Обработчик `button.onclick` сработает независимо и в дополнение к назначенному в `addEventListener`.

View file

@ -9,9 +9,9 @@
Что будет выведено при клике после выполнения кода?
```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); };
```

View file

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

View file

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

View file

@ -6,5 +6,5 @@
[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>
<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 *после текста*, а не до него.
# Обработчики
Для того, чтобы получить кнопку из контейнера, можно найти все `IMG` в нём и выбрать из них кнопку по `className`. На каждую кнопку можно повесить обработчик.
# Решение
[edit src="solution"]Решение в песочнице[/edit]
Для поиска элементов `span` с нужным классом в нём используется `getElementsByTagName` с фильтрацией. К сожалению, это единственный способ, доступный в IE 6,7. Если же эти браузеры вам не нужны, то гораздо лучше -- искать элементы при помощи `querySelector` или `getElementsByClassName`.
С другой стороны, потенциальным преимуществом способа с `position` по сравнению с `float` в данном случае является возможность поместить элемент кнопки в HTML *после текста*, а не до него.</li>
<li>Для того, чтобы получить кнопку из контейнера, используем `querySelectorAll`. На каждую кнопку повесим обработчик, который будет убирать родителя. Найти родителя можно через `parentNode`.</li>
</ol>

View file

@ -3,26 +3,6 @@
<head>
<link rel="stylesheet" href="messages.css">
<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>
<body>
@ -31,17 +11,17 @@
<div class="pane">
<h3>Лошадь</h3>
<p>Домашняя лошадь — животное семейства непарнокопытных, одомашненный и единственный сохранившийся подвид дикой лошади, вымершей в дикой природе, за исключением небольшой популяции лошади Пржевальского.</p>
<span class="remove-button"></span>
<button class="remove-button">[x]</button>
</div>
<div class="pane">
<h3>Осёл</h3>
<p>Домашний осёл или ишак — одомашненный подвид дикого осла, сыгравший важную историческую роль в развитии хозяйства и культуры человека. Все одомашненные ослы относятся к африканским ослам.</p>
<span class="remove-button"></span>
<button class="remove-button">[x]</button>
</div>
<div class="pane">
<h3>Корова, а также пара слов о диком быке, о волах и о тёлках. </h3>
<p>Коро́ва — самка домашнего быка, одомашненного подвида дикого быка, парнокопытного жвачного животного семейства полорогих. Самцы вида называются быками, молодняк — телятами, кастрированные самцы — волами. Молодых (до первой стельности) самок называют тёлками.</p>
<span class="remove-button"></span>
<button class="remove-button">[x]</button>
</div>
</div>
@ -49,13 +29,12 @@
<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++) {
var span = spans[i];
if (span.className != 'remove-button') continue;
for(var i=0; i<buttons.length; i++) {
var button = buttons[i];
span.onclick = function() {
button.onclick = function() {
var el = this.parentNode;
el.parentNode.removeChild(el);
};

View file

@ -2,31 +2,35 @@ 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;
position: relative;
}
.remove-button {
position: absolute;
top: 10px;
right: 10px;
cursor: pointer;
display: block;
background: url(delete.gif) no-repeat;
width: 16px;
height: 16px;
position: absolute;
font-size: 110%;
top: 0;
color: red;
right: 10px;
display: block;
width: 24px;
height: 24px;
border: none;
background: transparent;
}

View file

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

View file

@ -2,17 +2,30 @@ body {
margin: 10px auto;
width: 470px;
}
h3 {
margin: 0;
padding-bottom: .3em;
font-size: 1.1em;
}
p {
margin: 0;
padding: 0 0 .5em;
}
.pane {
background: #edf5e1;
padding: 10px 20px 10px;
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">
Чтобы список был длинный и элементы не переходили вниз, ему ставится `width: 9999px`, а элементам, соответственно, `float:left`.
Чтобы список был длинный и элементы не переходили вниз, ему ставится `width: 9999px`, а элементам `<li>`, соответственно, `float:left`, либо для элементов используется `display: inline-block`, как в этом решении.
[warn header="Не используйте display:inline"]
Элементы с `display:inline` имеют дополнительные отступы для возможных "хвостов букв".
Главное -- не использовать `display:inline`, так как такие элементы имеют дополнительные отступы снизу для возможных "хвостов букв".
В частности, для `img` нужно поставить в стилях явно `display:block`, чтобы пространства под ними не оставалось.
[/warn]
В частности, для `<img>` нужно поставить в стилях явно `display:block`, чтобы пространства под ними не оставалось.
При прокрутке UL сдвигается назначением `margin-left`:
Для "прокрутки" будем сдвигать `<ul>`. Это можно делать по-разному, например, назначением `margin-left`:
<img src="carousel2.png">
У внешнего `DIV` фиксированная ширина, поэтому "лишние" изображения обрезаются.
У внешнего `<div>` фиксированная ширина, поэтому "лишние" изображения обрезаются.
Снаружи окошка находятся стрелки и внешний контейнер.
Реализуйте эту структуру, и к ней прикручивайте обработчики, которые меняют `ul.style.marginLeft`.

View file

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

View file

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

View file

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

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