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,42 @@
Дело в том, что обработчик из атрибута `onclick` делается браузером как функция с заданным телом.
То есть, он будет таким:
```js
function(event) {
handler()
}
```
При этом возвращаемое `handler` значение никак не используется и не влияет на результат.
Рабочий вариант:
```html
<!--+ run -->
<script>
function handler() {
alert("...");
return false;
}
</script>
<a href="http://w3.org" onclick="*!*return handler()*/!*">w3.org</a>
```
Альтернатива -- передать и использовать объект события для вызова `event.preventDefault()` (или кросс-браузерного варианта для поддержки старых IE).
```html
<!--+ run -->
<script>
*!*
function handler(event) {
alert("...");
event.preventDefault ? event.preventDefault() : (event.returnValue=false);
}
*/!*
</script>
<a href="http://w3.org" onclick="*!*handler(event)*/!*">w3.org</a>
```

View file

@ -0,0 +1,21 @@
# Почему не работает return false?
[importance 3]
Почему в этом документе `return false` не работает?
```html
<!--+ autorun run -->
<script>
function handler() {
alert("...");
return false;
}
</script>
<a href="http://w3.org" onclick="handler()">w3.org</a>
```
По замыслу, переход на `w3.org` при клике должен отменяться. Однако, на самом деле он происходит.
В чём дело и как поправить, сохранив `onclick` в HTML?

View file

@ -0,0 +1,29 @@
Это -- классическая задача на тему делегирования.
В реальной жизни, мы можем перехватить событие и создать AJAX-запрос к серверу, который сохранит информацию о том, по какой ссылке ушел посетитель.
Мы перехватываем событие на `contents` и поднимаемся до `parentNode` пока не получим `A` или не упремся в контейнер.
```js
contents.onclick = function(evt) {
var target = evt.target;
function handleLink(href) {
var isLeaving = confirm('Уйти на '+href+'?');
if (!isLeaving) return false;
}
while(target != this) {
if (target.nodeName == 'A') {
*!*
return handleLink(target.getAttribute('href')); // (*)
*/!*
}
target = target.parentNode;
}
};
```
В строке `(*)` используется атрибут, а не свойство `href`, чтобы показать в `confirm` именно то, что написано в HTML-атрибуте, так как свойство может отличаться, оно обязано содержать полный валидный адрес.
[edit src="solution"]Полное решение[/edit].

View file

@ -0,0 +1 @@
{"name":"links","plunk":"I3ev2PoF4K7oKDy9y7pd"}

View file

@ -0,0 +1,33 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<div id="contents">
<p>
Как насчет почитать <a href="http://wikipedia.org">Википедию</a>, или посетить <a href="http://w3.org"><i>W3.org</i></a> и узнать про современные стандарты?
</p>
</div>
<script>
document.getElementById('contents').onclick = function(evt) {
var evt = evt || event
var target = evt.target || evt.srcElement
function handleLink(href) {
var isLeaving = confirm('Уйти на '+href+'?')
if (!isLeaving) return false
}
while(target != this) {
if (target.nodeName == 'A') {
return handleLink(target.getAttribute('href'));
}
target = target.parentNode
}
}
</script>
</body>
</html>

View file

@ -0,0 +1 @@
{"name":"links","plunk":"I3ev2PoF4K7oKDy9y7pd"}

View file

@ -0,0 +1,33 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<div id="contents">
<p>
Как насчет почитать <a href="http://wikipedia.org">Википедию</a>, или посетить <a href="http://w3.org"><i>W3.org</i></a> и узнать про современные стандарты?
</p>
</div>
<script>
document.getElementById('contents').onclick = function(evt) {
var evt = evt || event
var target = evt.target || evt.srcElement
function handleLink(href) {
var isLeaving = confirm('Уйти на '+href+'?')
if (!isLeaving) return false
}
while(target != this) {
if (target.nodeName == 'A') {
return handleLink(target.getAttribute('href'));
}
target = target.parentNode
}
}
</script>
</body>
</html>

View file

@ -0,0 +1,18 @@
# Поймайте переход по ссылке
[importance 5]
Сделайте так, чтобы при клике на ссылки внутри <code>&lt;DIV id="contents"&gt;</code> пользователю выводился вопрос о том, действительно ли он хочет покинуть страницу и если он не хочет, то прерывать переход по ссылке.
Так это должно работать:
[iframe height=100 border=1 src="solution"]
Детали:
<ul>
<li>Содержимое блока `DIV` может быть загружено динамически и присвоено при помощи `innerHTML`. Так что найти все ссылки и поставить на них обработчики нельзя. Используйте делегирование.</li>
<li>Содержимое может содержать вложенные теги, *в том числе внутри ссылок*, например, `<a href=".."><i>...</i></a>`.</li>
</ul>
[edit src="task" task/]

View file

@ -0,0 +1 @@
{"name":"links-src","plunk":"xRcQ5t44wND5znpLOjlp"}

View file

@ -0,0 +1,15 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<div id="contents">
<p>
Как насчет почитать <a href="http://wikipedia.org">Википедию</a>, или посетить <a href="http://w3.org"><i>W3.org</i></a> и узнать про современные стандарты?
</p>
</div>
</body>
</html>

View file

@ -0,0 +1,57 @@
Решение состоит в том, чтобы добавить обработчик на контейнер `#thumbs` и отслеживать клики на ссылках.
Когда происходит событие, обработчик должен изменять `src` `#largeImg` на `href` ссылки и заменять `alt` на ее `title`.
Код решения:
```js
var largeImg = document.getElementById('largeImg');
document.getElementById('thumbs').onclick = function(e) {
e = e || window.event;
var target = e.target || e.srcElement;
while(target != this) {
if (target.nodeName == 'A') {
showThumbnail(target.href, target.title);
return false;
}
target = target.parentNode;
}
}
function showThumbnail(href, title) {
largeImg.src = href;
largeImg.alt = title;
}
```
**Предзагрузка картинок**
Для того, чтобы картинка загрузилась, достаточно создать новый элемент `IMG` и указать ему `src`, вот так:
```js
var imgs = thumbs.getElementsByTagName('img');
for(var i=0; i<imgs.length; i++) {
var url = imgs[i].parentNode.href;
*!*
var img = document.createElement('img');
img.src = url;
*/!*
}
```
Как только элемент создан и ему назначен `src`, браузер сам начинает скачивать файл картинки.
При правильных настройках сервера как-то использовать этот элемент не обязательно -- картинка уже закеширована.
**Семантичная верстка**
Для списка картинок используется `DIV`. С точки зрения семантики более верный вариант -- список `UL/LI`.
[edit src="solution"]Полное решение[/edit]

View file

@ -0,0 +1 @@
{"name":"gallery","plunk":"xbazFxOWnIxSwcvEHrVb"}

View file

@ -0,0 +1,44 @@
body {
margin: 0;
padding: 0;
font: 75%/120% Arial, Helvetica, sans-serif;
}
h2 {
font: bold 190%/100% Arial, Helvetica, sans-serif;
margin: 0 0 .2em;
}
h2 em {
font: normal 80%/100% Arial, Helvetica, sans-serif;
color: #999999;
}
#largeImg {
border: solid 1px #ccc;
width: 550px;
height: 400px;
padding: 5px;
}
#thumbs a {
border: solid 1px #ccc;
width: 100px;
height: 100px;
padding: 3px;
margin: 2px;
float: left;
}
#thumbs a:hover {
border-color: #FF9900;
}
#thumbs li {
list-style: none;
}
#thumbs {
margin: 0;
padding: 0;
}

View file

@ -0,0 +1 @@
{"name":"gallery","plunk":"xbazFxOWnIxSwcvEHrVb"}

View file

@ -0,0 +1,44 @@
body {
margin: 0;
padding: 0;
font: 75%/120% Arial, Helvetica, sans-serif;
}
h2 {
font: bold 190%/100% Arial, Helvetica, sans-serif;
margin: 0 0 .2em;
}
h2 em {
font: normal 80%/100% Arial, Helvetica, sans-serif;
color: #999999;
}
#largeImg {
border: solid 1px #ccc;
width: 550px;
height: 400px;
padding: 5px;
}
#thumbs a {
border: solid 1px #ccc;
width: 100px;
height: 100px;
padding: 3px;
margin: 2px;
float: left;
}
#thumbs a:hover {
border-color: #FF9900;
}
#thumbs li {
list-style: none;
}
#thumbs {
margin: 0;
padding: 0;
}

View file

@ -0,0 +1,59 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Галерея</title>
<link rel="stylesheet" type="text/css" href="gallery.css">
<meta charset="utf-8">
</head>
<body>
<p><img id="largeImg" src="http://js.cx/gallery/img1-lg.jpg" alt="Large image"></p>
<ul id="thumbs">
<!-- При наведении на изображение показывается встроенная подсказка браузера (title) -->
<li><a href="http://js.cx/gallery/img2-lg.jpg" title="Image 2"><img src="http://js.cx/gallery/img2-thumb.jpg"></a></li>
<li><a href="http://js.cx/gallery/img3-lg.jpg" title="Image 3"><img src="http://js.cx/gallery/img3-thumb.jpg"></a></li>
<li><a href="http://js.cx/gallery/img4-lg.jpg" title="Image 4"><img src="http://js.cx/gallery/img4-thumb.jpg"></a></li>
<li><a href="http://js.cx/gallery/img5-lg.jpg" title="Image 5"><img src="http://js.cx/gallery/img5-thumb.jpg"></a></li>
<li><a href="http://js.cx/gallery/img6-lg.jpg" title="Image 6"><img src="http://js.cx/gallery/img6-thumb.jpg"></a></li>
</ul>
<script>
var largeImg = document.getElementById('largeImg');
var thumbs = document.getElementById('thumbs');
thumbs.onclick = function(e) {
e = e || window.event;
var target = e.target || e.srcElement;
while(target != this) {
if (target.nodeName == 'A') {
showThumbnail(target.href, target.title);
return false;
}
target = target.parentNode;
}
}
function showThumbnail(href, title) {
largeImg.src = href;
largeImg.alt = title;
}
/* предзагрузка */
var imgs = thumbs.getElementsByTagName('img');
for(var i=0; i<imgs.length; i++) {
var url = imgs[i].parentNode.href;
var img = document.createElement('img');
img.src = url;
}
</script>
</body>
</html>

View file

@ -0,0 +1,59 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Галерея</title>
<link rel="stylesheet" type="text/css" href="gallery.css">
<meta charset="utf-8">
</head>
<body>
<p><img id="largeImg" src="http://js.cx/gallery/img1-lg.jpg" alt="Large image"></p>
<ul id="thumbs">
<!-- При наведении на изображение показывается встроенная подсказка браузера (title) -->
<li><a href="http://js.cx/gallery/img2-lg.jpg" title="Image 2"><img src="http://js.cx/gallery/img2-thumb.jpg"></a></li>
<li><a href="http://js.cx/gallery/img3-lg.jpg" title="Image 3"><img src="http://js.cx/gallery/img3-thumb.jpg"></a></li>
<li><a href="http://js.cx/gallery/img4-lg.jpg" title="Image 4"><img src="http://js.cx/gallery/img4-thumb.jpg"></a></li>
<li><a href="http://js.cx/gallery/img5-lg.jpg" title="Image 5"><img src="http://js.cx/gallery/img5-thumb.jpg"></a></li>
<li><a href="http://js.cx/gallery/img6-lg.jpg" title="Image 6"><img src="http://js.cx/gallery/img6-thumb.jpg"></a></li>
</ul>
<script>
var largeImg = document.getElementById('largeImg');
var thumbs = document.getElementById('thumbs');
thumbs.onclick = function(e) {
e = e || window.event;
var target = e.target || e.srcElement;
while(target != this) {
if (target.nodeName == 'A') {
showThumbnail(target.href, target.title);
return false;
}
target = target.parentNode;
}
}
function showThumbnail(href, title) {
largeImg.src = href;
largeImg.alt = title;
}
/* предзагрузка */
var imgs = thumbs.getElementsByTagName('img');
for(var i=0; i<imgs.length; i++) {
var url = imgs[i].parentNode.href;
var img = document.createElement('img');
img.src = url;
}
</script>
</body>
</html>

View file

@ -0,0 +1,22 @@
# Галерея изображений
[importance 5]
Создайте галерею изображений, в которой основное изображение изменяется при клике на уменьшенный вариант.
Результат должен выглядеть так:
[iframe src="solution" height=600]
Для обработки событий используйте делегирование, т.е. не более одного обработчика.
[edit src="task" task/]
P.S. Обратите внимание -- клик может быть как на маленьком изображении `IMG`, так и на `A` вне него. При этом `event.target` будет, соответственно, либо `IMG`, либо `A`.
Дополнительно:
<ul>
<li>Если получится -- сделайте предзагрузку больших изображений, чтобы при клике они появлялись сразу.</li>
<li>Всё ли в порядке с семантической вёрсткой в HTML исходного документа? Если нет -- поправьте, чтобы было, как нужно.</li>
</ul>

View file

@ -0,0 +1 @@
{"name":"gallery-src","plunk":"kzfUa6aitkLvyQYfgAsv"}

View file

@ -0,0 +1,35 @@
body {
margin: 0;
padding: 0;
font: 75%/120% Arial, Helvetica, sans-serif;
}
h2 {
font: bold 190%/100% Arial, Helvetica, sans-serif;
margin: 0 0 .2em;
}
h2 em {
font: normal 80%/100% Arial, Helvetica, sans-serif;
color: #999999;
}
#largeImg {
border: solid 1px #ccc;
width: 550px;
height: 400px;
padding: 5px;
}
#thumbs a {
border: solid 1px #ccc;
width: 100px;
height: 100px;
padding: 3px;
margin: 2px;
float: left;
}
#thumbs a:hover {
border-color: #FF9900;
}

View file

@ -0,0 +1,23 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Галерея</title>
<link rel="stylesheet" type="text/css" href="gallery.css">
<meta charset="utf-8">
</head>
<body>
<p><img id="largeImg" src="http://js.cx/gallery/img1-lg.jpg" alt="Large image"></p>
<div id="thumbs">
<!-- При наведении на изображение показывается встроенная подсказка браузера (title) -->
<a href="http://js.cx/gallery/img2-lg.jpg" title="Image 2"><img src="http://js.cx/gallery/img2-thumb.jpg"></a>
<a href="http://js.cx/gallery/img3-lg.jpg" title="Image 3"><img src="http://js.cx/gallery/img3-thumb.jpg"></a>
<a href="http://js.cx/gallery/img4-lg.jpg" title="Image 4"><img src="http://js.cx/gallery/img4-thumb.jpg"></a>
<a href="http://js.cx/gallery/img5-lg.jpg" title="Image 5"><img src="http://js.cx/gallery/img5-thumb.jpg"></a>
<a href="http://js.cx/gallery/img6-lg.jpg" title="Image 6"><img src="http://js.cx/gallery/img6-thumb.jpg"></a>
</div>
</body>
</html>

View file

@ -0,0 +1,152 @@
# Действия браузера по умолчанию
Многие события влекут за собой действие браузера.
Например:
<ul>
<li>Клик по ссылке инициирует переход на новый URL</li>
<li>Нажатие на кнопку "отправить" в форме -- посылку ее на сервер</li>
<li>Двойной клик на тексте -- инициирует его выделение.</li>
</ul>
**Зачастую, мы полностью обрабатываем событие в JavaScript, и такое действие браузера нам не нужно.**
К счастью, его можно отменить.
[cut]
## Отмена действия браузера
Есть два способа отменить действие браузера:
<ul>
<li>**Основной способ -- это воспользоваться объектом события. Для отмены действия браузера существует стандартный метод `event.preventDefault()`.**</li>
<li>Если же обработчик назначен через `on...` (не через `addEventListener/attachEvent`), то можно просто вернуть `false` из обработчика.</li>
</ul>
В следующем примере при клике по ссылке переход не произойдет:
```html
<!--+ autorun -->
<a href="/" onclick="return false">Нажми здесь</a>
или
<a href="/" onclick="event.preventDefault()">здесь</a>
```
[warn header="Возвращать `true` не нужно"]
Вообще говоря, значение, которое возвращает обработчик, игнорируется.
Единственное исключение -- это `return false` из обработчика, назначенного через `onсобытие`.
Иногда в коде начинающих разработчиков можно увидеть `return` других значений. Но они не нужны и никак не обрабатываются.
[/warn]
### Пример: меню
Рассмотрим задачу, когда нужно создать меню для сайта, например такое:
```html
<ul id="menu" class="menu">
<li><a href="/php">PHP</a></li>
<li><a href="/html">HTML</a></li>
<li><a href="/javascript">JavaScript</a></li>
<li><a href="/flash">Flash</a></li>
</ul>
```
Данный пример при помощи CSS может выводиться так:
[iframe height=70 src="menu" link edit]
**Все элементы меню являются ссылками, то есть тегами `<a>`.**
Это потому, что некоторые посетители очень любят сочетание "правый клик - открыть в новом окне". Да, мы можем использовать и `<button>` и `<span>`, но если правый клик не работает -- это их огорчает. Кроме того, если на сайт зайдёт поисковик, то по ссылке из `<a href="...">` он перейдёт, а выполнить сложный JavaScript и получить результат -- вряд ли захочет.
**Значение `<a href="...">` -- это "запасной вариант", для правого клика и для поисковиков, а обычно клик будет обрабатываться JavaScript.**
Например, вот так:
```js
menu.onclick = function(event) {
if (event.target.nodeName != 'A') return;
var href = event.target.getAttribute('href');
alert(href); // может быть подгрузка с сервера, генерация интерфейса и т.п.
*!*
return false; // отменить переход по url
*/!*
};
```
В конце `return false`, иначе браузер перейдёт по адресу из `href`.
Так как мы применили делегирование, то меню может увеличиваться, можно добавить вложенные списки `ul/li`, стилизовать их при помощи CSS -- меню продолжит работать.
## Другие действия браузера
Действий браузера по умолчанию достаточно много.
Вот некоторые примеры событий, которые вызывают действие браузера:
<ul>
<li>`mousedown` -- нажатие кнопкой мыши в то время как курсор находится на тексте начинает его выделение.</li>
<li>`click` на `<input type="checkbox">` -- ставит или убирает галочку.</li>
<li>`submit` -- при нажатии на `<input type="submit">` в форме данные отправляются на сервер.</li>
<li>`wheel` -- движение колёсика мыши инициирует прокрутку.</li>
<li>`keydown` -- при нажатии клавиши в поле ввода появляется символ.</li>
<li>`contextmenu` -- при правом клике показывается контекстное меню браузера.</li>
<li>...</li>
</ul>
Все эти действия можно отменить, если мы хотим обработать событие исключительно при помощи JavaScript.
[warn header="События могут быть связаны между собой"]
Некоторые события естественным образом вытекают друг из друга.
Например, нажатие мышкой `mousedown` на поле ввода `<input>` приводит к фокусировке внутрь него. Если отменить действие `mousedown`, то и фокуса не будет.
Попробуйте нажать мышкой на первый `<input>` -- произойдёт событие `onfocus`. Это обычная ситуация.
Но если нажать на второй, то фокусировки не произойдёт.
```html
<!--+ run autorun -->
<input value="Фокус работает" onfocus="this.value=''">
<input *!*onmousedown="return false"*/!* onfocus="this.value=''" value="Кликни меня">
```
...С другой стороны, во второй `<input>` можно перейти с первого нажатием клавиши [key Tab], и тогда фокусировка сработает. То есть, дело здесь именно в `onmousedown="return false"`.
[/warn]
## Особенности IE8-
**В IE8- для отмены действия по умолчанию нужно назначить свойство `event.returnValue = false`.**
Кроссбраузерный код для отмены действия по умолчанию:
```js
element.onclick = function(event) {
event = event || window.event;
if (event.preventDefault) { // если метод существует
event.preventDefault(); // то вызвать его
} else { // иначе вариант IE<9:
event.returnValue = false;
}
}
```
Можно записать в одну строку:
```js
...
event.preventDefault ? event.preventDefault() : (event.returnValue=false);
...
```
## Итого
<ul>
<li>Браузер имеет встроенные действия при ряде событий -- переход по ссылке, отправка формы и т.п. Как правило, их можно отменить.</li>
<li>Есть два способа отменить действие по умолчанию: первый -- использовать `event.preventDefault()` (IE<9: `event.returnValue=false`), второй -- `return false` из обработчика. Второй способ работает только если обработчик назначен через `onсобытие`.</li>
</ul>

View file

@ -0,0 +1 @@
{"name":"menu","plunk":"TV5XwdELtOWY1cdMYEc5"}

View file

@ -0,0 +1,19 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="menu.css"/>
</head>
<body>
<ul id="menu" class="menu">
<li><a href="/php">PHP</a></li>
<li><a href="/html">HTML</a></li>
<li><a href="/javascript">JavaScript</a></li>
<li><a href="/flash">Flash</a></li>
</ul>
<script src="menu.js"></script>
</body>
</html>

View file

@ -0,0 +1,25 @@
.menu li {
display: inline-block;
margin: 0;
}
.menu > li a {
display: inline-block;
margin: 0 2px;
outline: none;
text-align: center;
text-decoration: none;
font: 14px/100% Arial, Helvetica, sans-serif;
padding: .5em 2em .55em;
text-shadow: 0 1px 1px rgba(0,0,0,.3);
border-radius: .5em;
box-shadow: 0 1px 2px rgba(0,0,0,.2);
color: #d9eef7;
border: solid 1px #0076a3;
background: #0095cd;
}
.menu > li:hover a {
text-decoration: none;
background: #007ead;
}

View file

@ -0,0 +1,8 @@
menu.onclick = function(event) {
if (event.target.nodeName != 'A') return;
var href = event.target.getAttribute('href');
alert(href);
return false; // prevent url change
};