This commit is contained in:
Ilya Kantor 2014-10-26 22:10:13 +03:00
parent 06f61d8ce8
commit f301cb744d
2271 changed files with 103162 additions and 0 deletions

View file

@ -0,0 +1 @@
[edit src="solution"]Решение задачи[/edit]

View file

@ -0,0 +1 @@
{"name":"hideOther","plunk":"FlYF4rg31bMg3wxvsKUR"}

View file

@ -0,0 +1 @@
{"name":"hideOther","plunk":"FlYF4rg31bMg3wxvsKUR"}

View file

@ -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>

View file

@ -0,0 +1 @@
{"name":"hideOtherSource","plunk":"JvpxZMeQJqNYSfdBucCY"}

View file

@ -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>

View file

@ -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>

View file

@ -0,0 +1,10 @@
# Спрятать при клике
[importance 5]
Используя JavaScript, сделайте так, чтобы при клике на кнопку исчезал элемент с `id="hide"`.
Демо:
[iframe border=1 src="solution"]
[edit src="solution" task/]

View file

@ -0,0 +1,7 @@
Решение задачи заключается в использовании `this` в обработчике.
```html
<!--+ run height=50 -->
<input type="button" onclick="this.style.display='none'" value="Нажми, чтобы меня спрятать"/>
```

View file

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

View file

@ -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`.

View file

@ -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); };
```

View file

@ -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]

View file

@ -0,0 +1 @@
{"name":"sliding","plunk":"j2lESMZ26M810rbWHKA0"}

View file

@ -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>

View file

@ -0,0 +1 @@
{"name":"sliding","plunk":"j2lESMZ26M810rbWHKA0"}

View file

@ -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>

View file

@ -0,0 +1,11 @@
# Раскрывающееся меню
[importance 5]
Создайте меню, которое раскрывается/сворачивается при клике:
[iframe border=1 height=100 src="solution"]
HTML/CSS исходного документа, возможно, понадобится изменить.
[edit src="task" task/]

View file

@ -0,0 +1 @@
{"name":"sliding-src","plunk":"VIa8tNExKI5araaxM1H7"}

View file

@ -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>

View file

@ -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`.

View file

@ -0,0 +1 @@
{"name":"messages","plunk":"5ZNzdrGeixJuC9qYlpY6"}

View file

@ -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>

View file

@ -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;
}

View file

@ -0,0 +1 @@
{"name":"messages","plunk":"5ZNzdrGeixJuC9qYlpY6"}

View file

@ -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>

View file

@ -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;
}

View file

@ -0,0 +1,12 @@
# Спрятать сообщение
[importance 5]
Есть список сообщений. Добавьте каждому сообщению по кнопке для его скрытия.
Результат:
[iframe src="solution"]
Как лучше отобразить кнопку справа-сверху: через `position:absolute` или `float`?
[edit src="task" task/]

View file

@ -0,0 +1 @@
{"name":"messages-src","plunk":"M4NIMfkzN3YQNGeayS1F"}

View file

@ -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>

View file

@ -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

View file

@ -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]

View file

@ -0,0 +1 @@
{"name":"carousel","plunk":"0XReVAKUmkxra8Pb4Wld"}

View file

@ -0,0 +1 @@
{"name":"carousel","plunk":"0XReVAKUmkxra8Pb4Wld"}

View file

@ -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">&#8249; </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">&#8250; </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>

View file

@ -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;
}

View file

@ -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">&#8249; </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">&#8250; </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>

View file

@ -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;
}

View file

@ -0,0 +1,13 @@
# Карусель
[importance 5]
Напишите "Карусель" -- ленту изображений, которую можно листать влево-вправо нажатием на стрелочки.
[iframe height=200 src="solution"]
В дальнейшем к ней можно легко добавить анимацию, динамическую подгрузку и другие возможности.
В этой задаче разработка HTML/CSS-структуры составляет 90% решения.
[edit src="task" task/]

View file

@ -0,0 +1 @@
{"name":"carousel-src","plunk":"upp5iHCJxVDJl5WMLiil"}

View file

@ -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>

View 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>

View file

@ -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("Клик!")"` не будет работать. Если вам действительно нужно использовать именно двойные кавычки, то это можно сделать, заменив их на `&quot;`: <code>onclick="alert(&amp;quot;Клик!&amp;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]