init
This commit is contained in:
parent
06f61d8ce8
commit
f301cb744d
2271 changed files with 103162 additions and 0 deletions
|
@ -0,0 +1 @@
|
|||
[edit src="solution"]Решение задачи[/edit]
|
|
@ -0,0 +1 @@
|
|||
{"name":"hideOther","plunk":"FlYF4rg31bMg3wxvsKUR"}
|
|
@ -0,0 +1 @@
|
|||
{"name":"hideOther","plunk":"FlYF4rg31bMg3wxvsKUR"}
|
|
@ -0,0 +1,18 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<input type="button" id="hider" value="Нажмите, чтобы спрятать текст"/>
|
||||
|
||||
<div id="hide">Текст</div>
|
||||
|
||||
<script>
|
||||
document.getElementById('hider').onclick = function() {
|
||||
document.getElementById('hide').style.display = 'none';
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1 @@
|
|||
{"name":"hideOtherSource","plunk":"JvpxZMeQJqNYSfdBucCY"}
|
|
@ -0,0 +1,16 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<input type="button" id="hider" value="Нажмите, чтобы спрятать текст"/>
|
||||
|
||||
<div id="hide">Текст</div>
|
||||
|
||||
<script>
|
||||
/* ваш код */
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,18 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<input type="button" id="hider" value="Нажмите, чтобы спрятать текст"/>
|
||||
|
||||
<div id="hide">Текст</div>
|
||||
|
||||
<script>
|
||||
document.getElementById('hider').onclick = function() {
|
||||
document.getElementById('hide').style.display = 'none';
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,10 @@
|
|||
# Спрятать при клике
|
||||
|
||||
[importance 5]
|
||||
|
||||
Используя JavaScript, сделайте так, чтобы при клике на кнопку исчезал элемент с `id="hide"`.
|
||||
|
||||
Демо:
|
||||
[iframe border=1 src="solution"]
|
||||
|
||||
[edit src="solution" task/]
|
|
@ -0,0 +1,7 @@
|
|||
Решение задачи заключается в использовании `this` в обработчике.
|
||||
|
||||
```html
|
||||
<!--+ run height=50 -->
|
||||
<input type="button" onclick="this.style.display='none'" value="Нажми, чтобы меня спрятать"/>
|
||||
```
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
# Спрятаться
|
||||
|
||||
[importance 5]
|
||||
|
||||
Создайте кнопку, при клике на которую, она будет скрывать сама себя.
|
||||
|
||||
Как эта:
|
||||
<input type="button" onclick="this.style.display='none'" value="Нажми, чтобы меня спрятать"/>
|
|
@ -0,0 +1,16 @@
|
|||
Ответ: будет выведено `1` и `2`.
|
||||
|
||||
Первый обработчик сработает, так как он не убран вызовом `removeEventListener`. Для удаления обработчика нужно передать в точности ту же функцию (ссылку на нее), что была назначена, а в коде передается такая же с виду функция, но, тем не менее, это другой объект.
|
||||
|
||||
Для того, чтобы удалить функцию-обработчик, нужно где-то сохранить ссылку на неё, например так:
|
||||
|
||||
```js
|
||||
function handler() {
|
||||
alert("1");
|
||||
}
|
||||
|
||||
button.addEventListener("click", handler, false);
|
||||
button.removeEventListener("click", handler, false);
|
||||
```
|
||||
|
||||
Обработчик `button.onclick` сработает независимо и в дополнение к назначенному в `addEventListener`.
|
|
@ -0,0 +1,18 @@
|
|||
# Какие обработчики сработают?
|
||||
|
||||
[importance 5]
|
||||
|
||||
В переменной `button` находится кнопка.
|
||||
|
||||
Изначально обработчиков на ней нет.
|
||||
|
||||
Что будет выведено при клике после выполнения кода?
|
||||
|
||||
```js
|
||||
button.addEventListener("click", function() { alert("1"); }, false);
|
||||
|
||||
button.removeEventListener("click", function() { alert("1"); }, false);
|
||||
|
||||
button.onclick = function() { alert(2); };
|
||||
```
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
# Структура HTML/CSS
|
||||
|
||||
Для начала, зададим структуру HTML/CSS.
|
||||
|
||||
Меню является отдельным графическим компонентом, его лучше поместить в единый DOM-элемент.
|
||||
|
||||
Элементы меню с точки зрения семантики являются списком `UL/LI`. Заголовок должен быть отдельным кликабельным элементом.
|
||||
|
||||
Получаем структуру:
|
||||
|
||||
```html
|
||||
<div class="menu">
|
||||
<span class="title">Сладости (нажми меня)!</span>
|
||||
<ul>
|
||||
<li>Пирог</li>
|
||||
<li>Пончик</li>
|
||||
<li>Мед</li>
|
||||
</ul>
|
||||
</div>
|
||||
```
|
||||
|
||||
Для заголовка лучше использовать именно `SPAN`, а не `DIV`, так как `DIV` постарается занять 100% ширины, и мы не сможем ловить `click` только на тексте:
|
||||
|
||||
```html
|
||||
<!--+ autorun height=50 -->
|
||||
<div style="border: solid red 1px">[Сладости (нажми меня)!]</div>
|
||||
```
|
||||
|
||||
...А `SPAN` -- это элемент с `display: inline`, поэтому он занимает ровно столько места, сколько занимает текст внутри него:
|
||||
|
||||
```html
|
||||
<!--+ autorun height=50 -->
|
||||
<span style="border: solid red 1px">[Сладости (нажми меня)!]</span>
|
||||
```
|
||||
|
||||
Раскрытие/закрытие делайте путём добавления/удаления класса `.menu-open` к меню, которые отвечает за стрелочку и отображение `UL`.
|
||||
|
||||
# CSS
|
||||
|
||||
CSS для меню:
|
||||
|
||||
```css
|
||||
.menu ul {
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
padding-left: 20px;
|
||||
|
||||
display: none;
|
||||
}
|
||||
|
||||
.menu .title {
|
||||
padding-left: 16px;
|
||||
font-size: 18px;
|
||||
cursor: pointer;
|
||||
|
||||
background: url(...arrow-right.png) left center no-repeat;
|
||||
}
|
||||
```
|
||||
|
||||
Если же меню раскрыто, то есть имеет класс `.menu-open`, то стрелочка слева заголовка меняется и список детей показывается:
|
||||
|
||||
```css
|
||||
.menu-open .title {
|
||||
background: url(...arrow-down.png) left center no-repeat;
|
||||
}
|
||||
|
||||
.menu-open ul {
|
||||
display: block;
|
||||
}
|
||||
```
|
||||
|
||||
Теперь сделайте JavaScript.
|
||||
|
||||
[edit src="solution"]Полное решение в песочнице[/edit]
|
|
@ -0,0 +1 @@
|
|||
{"name":"sliding","plunk":"j2lESMZ26M810rbWHKA0"}
|
|
@ -0,0 +1,57 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
|
||||
.menu ul {
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
padding-left: 20px;
|
||||
|
||||
display: none;
|
||||
}
|
||||
|
||||
.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-open ul {
|
||||
display: block;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="sweeties" class="menu">
|
||||
<span id="sweeties-title" class="title">Сладости (нажми меня)!</span>
|
||||
<ul>
|
||||
<li>Торт</li>
|
||||
<li>Пончик</li>
|
||||
<li>Пирожное</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var titleElem = document.getElementById('sweeties-title');
|
||||
|
||||
titleElem.onclick = function() {
|
||||
var menu = this.parentNode;
|
||||
menu.classList.toggle('menu-open');
|
||||
};
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1 @@
|
|||
{"name":"sliding","plunk":"j2lESMZ26M810rbWHKA0"}
|
|
@ -0,0 +1,57 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
|
||||
.menu ul {
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
padding-left: 20px;
|
||||
|
||||
display: none;
|
||||
}
|
||||
|
||||
.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-open ul {
|
||||
display: block;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="sweeties" class="menu">
|
||||
<span id="sweeties-title" class="title">Сладости (нажми меня)!</span>
|
||||
<ul>
|
||||
<li>Торт</li>
|
||||
<li>Пончик</li>
|
||||
<li>Пирожное</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var titleElem = document.getElementById('sweeties-title');
|
||||
|
||||
titleElem.onclick = function() {
|
||||
var menu = this.parentNode;
|
||||
menu.classList.toggle('menu-open');
|
||||
};
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,11 @@
|
|||
# Раскрывающееся меню
|
||||
|
||||
[importance 5]
|
||||
|
||||
Создайте меню, которое раскрывается/сворачивается при клике:
|
||||
|
||||
[iframe border=1 height=100 src="solution"]
|
||||
|
||||
HTML/CSS исходного документа, возможно, понадобится изменить.
|
||||
|
||||
[edit src="task" task/]
|
|
@ -0,0 +1 @@
|
|||
{"name":"sliding-src","plunk":"VIa8tNExKI5araaxM1H7"}
|
|
@ -0,0 +1,21 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</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>
|
||||
<li>Пирожное</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,26 @@
|
|||
# Алгоритм решения
|
||||
|
||||
<ol>
|
||||
<li>Разработать структуру HTML/CSS. Позиционировать кнопку внутри сообщения.</li>
|
||||
<li>Найти все кнопки</li>
|
||||
<li>Присвоить им обработчики</li>
|
||||
<li>Обработчик будет ловить событие на кнопке и удалять соответствующий элемент.</li>
|
||||
<ol>
|
||||
|
||||
# Вёрстка
|
||||
|
||||
Исправьте HTML/CSS, чтобы кнопка была в нужном месте сообщения. Кнопку лучше сделать как `div`, а картинка --- будет его `background`. Это более правильно, чем `img`, т.к. в данном случае картинка является *оформлением кнопки*, а оформление должно быть в CSS.
|
||||
|
||||
Расположить кнопку справа можно при помощи `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`.
|
|
@ -0,0 +1 @@
|
|||
{"name":"messages","plunk":"5ZNzdrGeixJuC9qYlpY6"}
|
|
@ -0,0 +1,67 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<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>
|
||||
|
||||
<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>
|
||||
|
||||
var spans = document.getElementById('messages-container').getElementsByTagName('span');
|
||||
|
||||
for(var i=0; i<spans.length; i++) {
|
||||
var span = spans[i];
|
||||
if (span.className != 'remove-button') continue;
|
||||
|
||||
span.onclick = function() {
|
||||
var el = this.parentNode;
|
||||
el.parentNode.removeChild(el);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,32 @@
|
|||
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;
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"name":"messages","plunk":"5ZNzdrGeixJuC9qYlpY6"}
|
|
@ -0,0 +1,67 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<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>
|
||||
|
||||
<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>
|
||||
|
||||
var spans = document.getElementById('messages-container').getElementsByTagName('span');
|
||||
|
||||
for(var i=0; i<spans.length; i++) {
|
||||
var span = spans[i];
|
||||
if (span.className != 'remove-button') continue;
|
||||
|
||||
span.onclick = function() {
|
||||
var el = this.parentNode;
|
||||
el.parentNode.removeChild(el);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,32 @@
|
|||
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;
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
# Спрятать сообщение
|
||||
|
||||
[importance 5]
|
||||
|
||||
Есть список сообщений. Добавьте каждому сообщению по кнопке для его скрытия.
|
||||
|
||||
Результат:
|
||||
[iframe src="solution"]
|
||||
|
||||
Как лучше отобразить кнопку справа-сверху: через `position:absolute` или `float`?
|
||||
|
||||
[edit src="task" task/]
|
|
@ -0,0 +1 @@
|
|||
{"name":"messages-src","plunk":"M4NIMfkzN3YQNGeayS1F"}
|
|
@ -0,0 +1,28 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="messages.css">
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
Картинка для кнопки удаления: <img src="http://js.cx/clipart/delete.gif">
|
||||
|
||||
<div>
|
||||
<div class="pane">
|
||||
<h3>Лошадь</h3>
|
||||
<p>Домашняя лошадь — животное семейства непарнокопытных, одомашненный и единственный сохранившийся подвид дикой лошади, вымершей в дикой природе, за исключением небольшой популяции лошади Пржевальского.</p>
|
||||
</div>
|
||||
<div class="pane">
|
||||
<h3>Осёл</h3>
|
||||
<p>Домашний осёл или ишак — одомашненный подвид дикого осла, сыгравший важную историческую роль в развитии хозяйства и культуры человека. Все одомашненные ослы относятся к африканским ослам.</p>
|
||||
</div>
|
||||
<div class="pane">
|
||||
<h3>Корова, а также пара слов о диком быке, о волах и о тёлках. </h3>
|
||||
<p>Коро́ва — самка домашнего быка, одомашненного подвида дикого быка, парнокопытного жвачного животного семейства полорогих. Самцы вида называются быками, молодняк — телятами, кастрированные самцы — волами. Молодых (до первой стельности) самок называют тёлками.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,18 @@
|
|||
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;
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 7.3 KiB |
Binary file not shown.
After Width: | Height: | Size: 8 KiB |
|
@ -0,0 +1,28 @@
|
|||
# HTML/CSS
|
||||
Лента изображений должна быть оформлена как список, согласно принципам семантической вёрстки.
|
||||
|
||||
Нужно стилизовать его так, чтобы он был длинной лентой, из которой внешний `DIV` вырезает нужную часть для просмотра:
|
||||
|
||||
<img src="carousel1.png">
|
||||
|
||||
Чтобы список был длинный и элементы не переходили вниз, ему ставится `width: 9999px`, а элементам, соответственно, `float:left`.
|
||||
|
||||
[warn header="Не используйте display:inline"]
|
||||
Элементы с `display:inline` имеют дополнительные отступы для возможных "хвостов букв".
|
||||
|
||||
В частности, для `img` нужно поставить в стилях явно `display:block`, чтобы пространства под ними не оставалось.
|
||||
[/warn]
|
||||
|
||||
При прокрутке UL сдвигается назначением `margin-left`:
|
||||
|
||||
<img src="carousel2.png">
|
||||
|
||||
У внешнего `DIV` фиксированная ширина, поэтому "лишние" изображения обрезаются.
|
||||
|
||||
Снаружи окошка находятся стрелки и внешний контейнер.
|
||||
|
||||
Реализуйте эту структуру, и к ней прикручивайте обработчики, которые меняют `ul.style.marginLeft`.
|
||||
|
||||
# Полное решение
|
||||
|
||||
[edit src="solution"]Открыть в песочнице[/edit]
|
|
@ -0,0 +1 @@
|
|||
{"name":"carousel","plunk":"0XReVAKUmkxra8Pb4Wld"}
|
|
@ -0,0 +1 @@
|
|||
{"name":"carousel","plunk":"0XReVAKUmkxra8Pb4Wld"}
|
|
@ -0,0 +1,71 @@
|
|||
<!DOCTYPE html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" type="text/css" href="style.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="carousel">
|
||||
<a href="#" class="arrow left-arrow" id="prev">‹ </a>
|
||||
<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>
|
||||
</div>
|
||||
<a href="#" class="arrow right-arrow" id="next">› </a>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
/* этот код помечает картинки, для удобства разработки */
|
||||
var lis = document.getElementsByTagName('li');
|
||||
for(var i=0; i<lis.length; i++) {
|
||||
lis[i].style.position='relative';
|
||||
var span = document.createElement('span');
|
||||
// обычно лучше использовать CSS-классы,
|
||||
// но этот код - для удобства разработки, так что не будем трогать стили
|
||||
span.style.cssText='position:absolute;left:0;top:0';
|
||||
span.innerHTML = i+1;
|
||||
lis[i].appendChild(span);
|
||||
}
|
||||
|
||||
/* конфигурация */
|
||||
var width = 130; // ширина изображения
|
||||
var count = 3; // количество изображений
|
||||
|
||||
var ul = document.getElementById('images');
|
||||
var imgs = ul.getElementsByTagName('li');
|
||||
|
||||
var position = 0; // текущий сдвиг влево
|
||||
|
||||
document.getElementById('prev').onclick = function() {
|
||||
if (position >= 0) return false; // уже до упора
|
||||
|
||||
// последнее передвижение влево может быть не на 3, а на 2 или 1 элемент
|
||||
position = Math.min(position + width*count, 0)
|
||||
ul.style.marginLeft = position + 'px';
|
||||
return false;
|
||||
}
|
||||
|
||||
document.getElementById('next').onclick = function() {
|
||||
if (position <= -width*(imgs.length-count)) return false; // уже до упора
|
||||
|
||||
// последнее передвижение вправо может быть не на 3, а на 2 или 1 элемент
|
||||
position = Math.max(position-width*count, -width*(imgs.length-count));
|
||||
ul.style.marginLeft = position + 'px';
|
||||
return false;
|
||||
};
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,52 @@
|
|||
body {
|
||||
padding: 10px
|
||||
}
|
||||
|
||||
.carousel {
|
||||
position: relative;
|
||||
width: 398px;
|
||||
padding: 10px 40px;
|
||||
border: 1px solid #CCC;
|
||||
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;
|
||||
}
|
||||
|
||||
.carousel .arrow:hover {
|
||||
background: #ccc;
|
||||
}
|
||||
.carousel .left-arrow {
|
||||
left: 7px;
|
||||
}
|
||||
.carousel .right-arrow {
|
||||
right: 7px;
|
||||
}
|
||||
.gallery {
|
||||
width: 390px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.gallery ul {
|
||||
height: 130px;
|
||||
width: 9999px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.gallery ul li {
|
||||
float: left;
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
<!DOCTYPE html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" type="text/css" href="style.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="carousel">
|
||||
<a href="#" class="arrow left-arrow" id="prev">‹ </a>
|
||||
<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>
|
||||
</div>
|
||||
<a href="#" class="arrow right-arrow" id="next">› </a>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
/* этот код помечает картинки, для удобства разработки */
|
||||
var lis = document.getElementsByTagName('li');
|
||||
for(var i=0; i<lis.length; i++) {
|
||||
lis[i].style.position='relative';
|
||||
var span = document.createElement('span');
|
||||
// обычно лучше использовать CSS-классы,
|
||||
// но этот код - для удобства разработки, так что не будем трогать стили
|
||||
span.style.cssText='position:absolute;left:0;top:0';
|
||||
span.innerHTML = i+1;
|
||||
lis[i].appendChild(span);
|
||||
}
|
||||
|
||||
/* конфигурация */
|
||||
var width = 130; // ширина изображения
|
||||
var count = 3; // количество изображений
|
||||
|
||||
var ul = document.getElementById('images');
|
||||
var imgs = ul.getElementsByTagName('li');
|
||||
|
||||
var position = 0; // текущий сдвиг влево
|
||||
|
||||
document.getElementById('prev').onclick = function() {
|
||||
if (position >= 0) return false; // уже до упора
|
||||
|
||||
// последнее передвижение влево может быть не на 3, а на 2 или 1 элемент
|
||||
position = Math.min(position + width*count, 0)
|
||||
ul.style.marginLeft = position + 'px';
|
||||
return false;
|
||||
}
|
||||
|
||||
document.getElementById('next').onclick = function() {
|
||||
if (position <= -width*(imgs.length-count)) return false; // уже до упора
|
||||
|
||||
// последнее передвижение вправо может быть не на 3, а на 2 или 1 элемент
|
||||
position = Math.max(position-width*count, -width*(imgs.length-count));
|
||||
ul.style.marginLeft = position + 'px';
|
||||
return false;
|
||||
};
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,52 @@
|
|||
body {
|
||||
padding: 10px
|
||||
}
|
||||
|
||||
.carousel {
|
||||
position: relative;
|
||||
width: 398px;
|
||||
padding: 10px 40px;
|
||||
border: 1px solid #CCC;
|
||||
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;
|
||||
}
|
||||
|
||||
.carousel .arrow:hover {
|
||||
background: #ccc;
|
||||
}
|
||||
.carousel .left-arrow {
|
||||
left: 7px;
|
||||
}
|
||||
.carousel .right-arrow {
|
||||
right: 7px;
|
||||
}
|
||||
.gallery {
|
||||
width: 390px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.gallery ul {
|
||||
height: 130px;
|
||||
width: 9999px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.gallery ul li {
|
||||
float: left;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
# Карусель
|
||||
|
||||
[importance 5]
|
||||
|
||||
Напишите "Карусель" -- ленту изображений, которую можно листать влево-вправо нажатием на стрелочки.
|
||||
|
||||
[iframe height=200 src="solution"]
|
||||
|
||||
В дальнейшем к ней можно легко добавить анимацию, динамическую подгрузку и другие возможности.
|
||||
|
||||
В этой задаче разработка HTML/CSS-структуры составляет 90% решения.
|
||||
|
||||
[edit src="task" task/]
|
|
@ -0,0 +1 @@
|
|||
{"name":"carousel-src","plunk":"upp5iHCJxVDJl5WMLiil"}
|
|
@ -0,0 +1,42 @@
|
|||
<!DOCTYPE html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</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>
|
||||
</ul>
|
||||
|
||||
|
||||
<script>
|
||||
/* этот код помечает картинки цифрами, для удобства разработки
|
||||
его можно убрать, если не нужен */
|
||||
var lis = document.getElementsByTagName('li');
|
||||
for(var i=0; i<lis.length; i++) {
|
||||
lis[i].style.position='relative';
|
||||
var span = document.createElement('span');
|
||||
span.style.cssText='position:absolute;left:0;top:0';
|
||||
span.innerHTML = i+1;
|
||||
lis[i].appendChild(span);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script>
|
||||
|
||||
// ваш код..
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
17
02-ui/02-events-and-interfaces/01-introduction-browser-events/2.html
Executable file
17
02-ui/02-events-and-interfaces/01-introduction-browser-events/2.html
Executable file
|
@ -0,0 +1,17 @@
|
|||
<!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>
|
|
@ -0,0 +1,526 @@
|
|||
# Введение в браузерные события
|
||||
|
||||
Для реакции на действия посетителя и внутреннего взаимодействия скриптов существуют *события*.
|
||||
|
||||
*Событие* - это сигнал от браузера о том, что что-то произошло.
|
||||
[cut]
|
||||
Существует много видов событий.
|
||||
|
||||
Посмотрим список самых часто используемых, пока просто для ознакомления:
|
||||
|
||||
<dl>
|
||||
<dt>События мыши</dt>
|
||||
<dd>
|
||||
<ul>
|
||||
<li>`click` -- происходит, когда кликнули на элемент левой кнопкой мыши</li>
|
||||
<li>`contextmenu` -- происходит, когда кликнули на элемент правой кнопкой мыши</li>
|
||||
<li>`mouseover` -- возникает, когда на элемент наводится мышь</li>
|
||||
<li>`mousedown` и `mouseup` -- когда кнопку мыши нажали или отжали</li>
|
||||
<li>`mousemove` -- при движении мыши</li>
|
||||
</ul>
|
||||
</dd>
|
||||
<dt>События на элементах управления</dt>
|
||||
<dd>
|
||||
<ul>
|
||||
<li>`submit` -- посетитель отправил форму `<form>`</li>
|
||||
<li>`focus` -- посетитель фокусируется на элементе, например нажимает на `<input>`</li>
|
||||
</ul>
|
||||
<dt>Клавиатурные события</dt>
|
||||
<dd>
|
||||
<ul>
|
||||
<li>`keydown` -- когда посетитель нажимает клавишу</li>
|
||||
<li>`keyup` -- когда посетитель отпускает клавишу</li>
|
||||
</ul>
|
||||
</dd>
|
||||
<dt>События документа</dt>
|
||||
<dd>
|
||||
<ul>
|
||||
<li>`DOMContentLoaded` -- когда HTML загружен и обработан, DOM документа полностью построен и доступен.</li>
|
||||
</ul></dd>
|
||||
<dt>События CSS</dt>
|
||||
<dd>
|
||||
<ul>
|
||||
<li>`transitionend` -- когда CSS-анимация завершена.</li>
|
||||
</ul></dd>
|
||||
</dl>
|
||||
|
||||
Также есть и много других событий.
|
||||
|
||||
## Назначение обработчиков событий
|
||||
|
||||
**Событию можно назначить обработчик, то есть функцию, которая сработает, как только событие произошло.**
|
||||
|
||||
Именно благодаря событиям JavaScript-код может реагировать на действия посетителя.
|
||||
|
||||
Есть несколько способов назначить событию обработчик. Сейчас мы их рассмотрим, начиная от самого простого.
|
||||
|
||||
### Использование атрибута HTML
|
||||
|
||||
Обработчик может быть назначен прямо в разметке, в атрибуте, который называется `on<событие>`.
|
||||
|
||||
Например, чтобы прикрепить `click`-событие к `input` кнопке, можно присвоить обработчик `onclick`, вот так:
|
||||
|
||||
```html
|
||||
<input value="Нажми меня" *!*onclick="alert('Клик!')"*/!* type="button">
|
||||
```
|
||||
|
||||
При клике мышкой на кнопке выполнится код, указанный в атрибуте `onclick`.
|
||||
|
||||
В действии:
|
||||
|
||||
<input value="Нажми меня" onclick="alert('Клик!');" type="button">
|
||||
|
||||
Обратите внимание, для строки *внутри* `alert('Клик!')` используются *одиночные кавычки*, так как сам атрибут находится в двойных.
|
||||
|
||||
Частая ошибка новичков в том, что они забывают, что код находится внутри атрибута. Запись вида `onclick="alert("Клик!")"` не будет работать. Если вам действительно нужно использовать именно двойные кавычки, то это можно сделать, заменив их на `"`: <code>onclick="alert(&quot;Клик!&quot;)"</code>.
|
||||
|
||||
Однако, обычно этого не требуется, так как в разметке пишутся только очень простые обработчики. Если нужно сделать что-то сложное, то имеет смысл описать это в функции, и в обработчике вызвать уже её.
|
||||
|
||||
Следующий пример по клику запускает функцию `countRabbits()`.
|
||||
|
||||
```html
|
||||
<!--+ src="2.html" run height=80 -->
|
||||
|
||||
```
|
||||
|
||||
Как мы помним, атрибут HTML-тега не чувствителен к регистру, поэтому `ONCLICK` будет работать так же, как `onClick` или `onclick`... Но, как правило, атрибуты пишут в нижнем регистре: `onclick`.
|
||||
|
||||
### Использование свойства DOM-объекта
|
||||
|
||||
Можно назначать обработчик, используя свойство DOM-элемента `on<событие>`.
|
||||
|
||||
Пример установки обработчика `click`:
|
||||
|
||||
```html
|
||||
<input id="elem" type="button" value="Нажми меня"/>
|
||||
<script>
|
||||
*!*
|
||||
elem.onclick = function() {
|
||||
alert('Спасибо');
|
||||
};
|
||||
*/!*
|
||||
</script>
|
||||
```
|
||||
|
||||
В действии:
|
||||
<input id="elem" type="button" value="Нажми меня"/>
|
||||
<script>
|
||||
elem.onclick = function() {
|
||||
alert('Спасибо');
|
||||
};
|
||||
</script>
|
||||
|
||||
Если обработчик задан через атрибут, то браузер читает HTML-разметку, создаёт новую функцию из содержимого атрибута и записывает в свойство `onclick`.
|
||||
|
||||
**Обработчик хранится именно в свойстве, а атрибут -- лишь один из способов его инициализации.**
|
||||
|
||||
Эти два примера кода работают одинаково:
|
||||
|
||||
<ol>
|
||||
<li>Только HTML:
|
||||
|
||||
```html
|
||||
<!--+ run height=50 -->
|
||||
<input type="button" *!*onclick="alert('Клик!')"*/!* value="Кнопка"/>
|
||||
```
|
||||
|
||||
</li>
|
||||
<li>HTML + JS:
|
||||
|
||||
```html
|
||||
<!--+ run height=50 -->
|
||||
<input type="button" id="button" value="Кнопка"/>
|
||||
<script>
|
||||
*!*
|
||||
button.onclick = function() {
|
||||
alert('Клик!');
|
||||
};
|
||||
*/!*
|
||||
</script>
|
||||
```
|
||||
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
**Так как свойство, в итоге, одно, то назначить более одного обработчика так нельзя.**
|
||||
|
||||
В примере ниже назначение через JavaScript перезапишет обработчик из атрибута:
|
||||
|
||||
```html
|
||||
<!--+ run height=50 autorun -->
|
||||
<input type="button" id="elem" onclick="alert('До')" value="Нажми меня"/>
|
||||
<script>
|
||||
*!*
|
||||
elem.onclick = function() { // перезапишет существующий обработчик
|
||||
alert('После');
|
||||
};
|
||||
*/!*
|
||||
</script>
|
||||
```
|
||||
|
||||
Кстати, обработчиком можно назначить и уже существующую функцию:
|
||||
|
||||
```js
|
||||
function sayThanks() {
|
||||
alert('Спасибо!');
|
||||
}
|
||||
|
||||
elem.onclick = sayThanks;
|
||||
```
|
||||
|
||||
Если обработчик надоел -- его всегда можно убрать назначением `elem.onclick = null`.
|
||||
|
||||
### Доступ к элементу через this
|
||||
|
||||
**Внутри обработчика события `this` ссылается на текущий элемент, то есть на тот, на котором он сработал.**
|
||||
|
||||
Это можно использовать, чтобы получить свойства или изменить элемент.
|
||||
|
||||
В коде ниже `button` выводит свое содержимое, используя `this.innerHTML`:
|
||||
|
||||
```html
|
||||
<button onclick="alert(this.innerHTML)">Нажми меня</button>
|
||||
```
|
||||
|
||||
В действии:
|
||||
<button onclick="alert(this.innerHTML)">Нажми меня</button>
|
||||
|
||||
|
||||
### Частые ошибки
|
||||
|
||||
Если вы только начинаете работать с событиями -- обратите внимание на следующие особенности.
|
||||
|
||||
<dl>
|
||||
<dt>Функция должна быть присвоена как `sayThanks`, а не `sayThanks()`.</dt>
|
||||
<dd>
|
||||
|
||||
```js
|
||||
button.onclick = sayThanks;
|
||||
```
|
||||
|
||||
Если добавить скобки, то `sayThanks()` -- будет уже *результат* выполнения функции (а так как в ней нет `return`, то в `onclick` попадёт `undefined`). Нам же нужна именно функция.
|
||||
|
||||
...А вот в разметке как раз скобки нужны:
|
||||
|
||||
```html
|
||||
<input type="button" id="button" onclick="sayThanks()"/>
|
||||
```
|
||||
|
||||
Это различие просто объяснить. При создании обработчика браузером по разметке, он автоматически создает функцию из его содержимого. Поэтому последний пример -- фактически то же самое, что:
|
||||
|
||||
```js
|
||||
button.onclick = function() {
|
||||
*!*
|
||||
sayThanks(); // содержимое атрибута
|
||||
*/!*
|
||||
};
|
||||
```
|
||||
|
||||
</dd>
|
||||
<dt>Используйте именно функции, а не строки.</dt>
|
||||
<dd>
|
||||
Назначение обработчика строкой `elem.onclick = 'alert(1)'` будет работать, но не рекомендуется, могут быть проблемы при сжатии JavaScript.
|
||||
|
||||
Передавать код в виде строки по меньшей мере странно в языке, который поддерживает Function Expressions, оно здесь доступно только по соображениям совместимости с древними временами.
|
||||
</dd>
|
||||
<dt>Не используйте `setAttribute`.</dt>
|
||||
<dd>
|
||||
Такой вызов работать не будет:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
// при нажатии на body будут ошибки
|
||||
// потому что при назначении в атрибут функция будет преобразована в строку
|
||||
document.body.setAttribute('onclick', function() { alert(1) });
|
||||
```
|
||||
|
||||
</dd>
|
||||
<dt>Регистр свойства имеет значение.</dt>
|
||||
<dd>Свойство называется `onclick`, а не `ONCLICK`.</dd>
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
## Специальные методы
|
||||
|
||||
Фундаментальный недостаток описанных выше способов назначения обработчика -- невозможность повесить *несколько* обработчиков на одно событие.
|
||||
|
||||
Например, одна часть кода хочет при клике на кнопку делать ее подсвеченной, а другая -- выдавать сообщение. Нужно в разных местах два обработчика повесить.
|
||||
|
||||
При этом новый обработчик будет затирать предыдущий. Например, следующий код на самом деле назначает один обработчик -- последний:
|
||||
|
||||
```js
|
||||
input.onclick = function() { alert(1); }
|
||||
// ...
|
||||
input.onclick = function() { alert(2); } // заменит предыдущий обработчик
|
||||
```
|
||||
|
||||
Разработчики стандартов достаточно давно это поняли и предложили альтернативный способ назначения обработчиков при помощи специальных методов, который свободен от указанного недостатка.
|
||||
|
||||
### addEventListener и removeEventListener
|
||||
|
||||
**Методы `addEventListener` и `removeEventListener` являются современным способом назначить или удалить обработчик, и при этом позволяют использовать сколько угодно любых обработчиков.**
|
||||
|
||||
Назначение обработчика осуществляется вызовом `addEventListener` с тремя аргументами:
|
||||
|
||||
```js
|
||||
element.addEventListener( event, handler, phase );
|
||||
```
|
||||
|
||||
<dl>
|
||||
<dt>`event`</dt>
|
||||
<dd>Имя события, например `click`</dd>
|
||||
<dt>`handler`</dt>
|
||||
<dd>Ссылка на функцию, которую надо поставить обработчиком.</dd>
|
||||
<dt>`phase`</dt>
|
||||
<dd>Фаза, на которой обработчик должен сработать. Этот аргумент мы рассмотрим далее в учебнике. Пока что будем использовать значение `phase = false`, которое нужно в 99% случаев.</dd>
|
||||
</dl>
|
||||
|
||||
Удаление обработчика осуществляется вызовом `removeEventListener`:
|
||||
|
||||
```js
|
||||
element.removeEventListener( event, handler, phase );
|
||||
```
|
||||
|
||||
[warn header="Удаление требует ту же функцию"]
|
||||
Для удаления нужно передать именно ту функцию-обработчик которая была назначена.
|
||||
|
||||
Вот так `removeEventListener` не сработает:
|
||||
|
||||
```js
|
||||
input.addEventListener( "click" , function() {alert('Спасибо!')}, false);
|
||||
// ....
|
||||
input.removeEventListener( "click", function() {alert('Спасибо!')}, false);
|
||||
```
|
||||
|
||||
Это не одна и та же функция, а две независимо созданные (с одинаковым кодом, но это не важно).
|
||||
|
||||
Вот так правильно:
|
||||
|
||||
```js
|
||||
function handler() {
|
||||
alert('Спасибо!');
|
||||
}
|
||||
|
||||
input.addEventListener( "click" , handler, false);
|
||||
// ....
|
||||
input.removeEventListener( "click", handler, false);
|
||||
```
|
||||
|
||||
[/warn]
|
||||
|
||||
**Использование `addEventListener` позволяет добавлять несколько обработчиков на одно событие одного элемента:**
|
||||
|
||||
```html
|
||||
<!--+ run -->
|
||||
<input id="elem" type="button" value="Нажми меня"/>
|
||||
|
||||
<script>
|
||||
function handler1() {
|
||||
alert('Спасибо!');
|
||||
};
|
||||
|
||||
function handler2() {
|
||||
alert('Спасибо ещё раз!');
|
||||
}
|
||||
|
||||
*!*
|
||||
elem.onclick = function() { alert("Привет"); };
|
||||
elem.addEventListener("click", handler1, false); // Спасибо!
|
||||
elem.addEventListener("click", handler2, false); // Спасибо ещё раз!
|
||||
*/!*
|
||||
</script>
|
||||
```
|
||||
|
||||
Как видно из примера выше, можно одновременно назначать обработчики и через `onсвойство` (только один) и через `addEventListener`. Однако, во избежание путаницы обычно рекомендуется выбрать один способ.
|
||||
|
||||
[warn header="`addEventListener` работает всегда, а `onсвойство` -- нет"]
|
||||
У специальных методов есть ещё одно перимущество перед "старой школой".
|
||||
|
||||
Есть некоторые события, которые нельзя назначить через `onсвойство`, но можно через `addEventListener`.
|
||||
|
||||
Например, таково событие `transitionend`, то есть окончание CSS-анимации. В большинстве браузеров оно требует назначения через `addEventListener`.
|
||||
|
||||
При нажатии на кнопку в примере ниже сработает второй обработчик, но не первый.
|
||||
|
||||
```html
|
||||
<!--+ autorun run -->
|
||||
<style>
|
||||
button {
|
||||
transition: width 3s;
|
||||
width: 100px;
|
||||
}
|
||||
.wide {
|
||||
width: 300px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<button id="elem" onclick="this.classList.toggle('wide');">
|
||||
Нажми меня
|
||||
</button>
|
||||
|
||||
<script>
|
||||
elem.ontransitionend = function() {
|
||||
alert("ontransitionend"); // не сработает
|
||||
};
|
||||
|
||||
*!*
|
||||
elem.addEventListener("transitionend", function() {
|
||||
alert("addEventListener"); // сработает по окончании анимации
|
||||
}, false);
|
||||
*/!*
|
||||
</script>
|
||||
```
|
||||
|
||||
[/warn]
|
||||
|
||||
|
||||
## Отличия IE8-
|
||||
|
||||
При работе с событиями в IE8- есть много отличий. Как правило, они формальны -- некое свойство или метод называются по-другому. Начиная с версии 9, также работают и стандартные свойства и методы, которые предпочтительны.
|
||||
|
||||
**В IE8- вместо `addEventListener/removeEventListener` используются свои методы.**
|
||||
|
||||
Назначение обработчика осуществляется вызовом `attachEvent`:
|
||||
|
||||
```js
|
||||
element.attachEvent( "on"+event, handler);
|
||||
```
|
||||
|
||||
Удаление обработчика -- вызовом `detachEvent`:
|
||||
|
||||
```js
|
||||
element.detachEvent( "on"+event, handler);
|
||||
```
|
||||
|
||||
Например:
|
||||
|
||||
```js
|
||||
function handler() {
|
||||
alert('Спасибо!');
|
||||
}
|
||||
button.attachEvent( "onclick" , handler) // Назначение обработчика
|
||||
// ....
|
||||
button.detachEvent( "onclick", handler) // Удаление обработчика
|
||||
```
|
||||
|
||||
Как видите, почти то же самое, только событие должно включать префикс `on` и нет третьего аргумента, который нам пока не нужен.
|
||||
|
||||
|
||||
[warn header="У обработчиков, назначенных с `attachEvent`, нет `this`"]
|
||||
Обработчики, назначенные с `attachEvent` не получают `this`!
|
||||
|
||||
Это важная особенность и подводный камень старых 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-), то это решение может подойти.
|
||||
|
||||
|
||||
## Итого
|
||||
|
||||
Есть три способа назначения обработчиков событий:
|
||||
|
||||
<ol>
|
||||
<li>Атрибут HTML: `onclick="..."`.</li>
|
||||
<li>Свойство: <code>elem.onclick = function</code>.</li>
|
||||
<li>Специальные методы:
|
||||
<ul>
|
||||
<li>Для IE8-: `elem.attachEvent( on+событие, handler )` (удаление через `detachEvent`).</li>
|
||||
<li>Для остальных: `elem.addEventListener( событие, handler, false )` (удаление через `removeEventListener`).</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
Сравнение `addEventListener` и `onclick`:
|
||||
[compare]
|
||||
+Некоторые события можно назначить только через `addEventListener`.
|
||||
+Метод `addEventListener` позволяет назначить много обработчиков на одно событие.
|
||||
-Обработчик, назначенный через `onclick`, проще удалить или заменить.
|
||||
-Метод `onclick` кросс-браузерный.
|
||||
[/compare]
|
||||
|
||||
**Этим введением мы только начинаем работу с событиями, но вы уже можете решать разнообразные задачи с их использованием.**
|
||||
|
||||
|
||||
[head]
|
||||
<style type="text/css">
|
||||
.d0 { text-align:center;margin:auto; }
|
||||
.d1 p { margin: 0 }
|
||||
.d1 {
|
||||
margin:2em;
|
||||
background-color:green;
|
||||
width:13em;
|
||||
height:13em;
|
||||
text-align:center;
|
||||
}
|
||||
.d1 .number {
|
||||
line-height: 2em;
|
||||
}
|
||||
.d2 {
|
||||
text-align:center;
|
||||
margin:auto;
|
||||
background-color:blue;
|
||||
width:9em;
|
||||
height:9em;
|
||||
}
|
||||
.d1 .d2 ,number {
|
||||
line-height: 2em;
|
||||
}
|
||||
.d3 {
|
||||
text-align:center;
|
||||
margin:auto;
|
||||
background-color:red;
|
||||
width:5em;
|
||||
height:5em;
|
||||
}
|
||||
.d1 .d2 .d3 .number {
|
||||
line-height: 5em;
|
||||
}
|
||||
.d1 .d2 .d2a {
|
||||
color:white;
|
||||
line-height: 2em;
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript">
|
||||
function highlightMe(elem) {
|
||||
elem.style.backgroundColor='yellow'
|
||||
alert(elem.className)
|
||||
elem.style.backgroundColor = ''
|
||||
}
|
||||
|
||||
function highlightMe2(e) {
|
||||
highlightMe(e.currentTarget);
|
||||
}
|
||||
</script>
|
||||
[/head]
|
Loading…
Add table
Add a link
Reference in a new issue