up
This commit is contained in:
parent
4bca225593
commit
547854a151
1655 changed files with 847 additions and 89231 deletions
|
@ -1,3 +0,0 @@
|
|||
В данном случае достаточно событий `input.focus/input.blur`.
|
||||
|
||||
Если бы мы хотели реализовать это на уровне документа, то применили бы делегирование и события `focusin/focusout` (эмуляцию для firefox), так как обычные `focus/blur` не всплывают.
|
|
@ -1,74 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<style>
|
||||
.placeholder {
|
||||
color: blue;
|
||||
font-family: Georgia;
|
||||
}
|
||||
|
||||
.placeholder-tooltip {
|
||||
color: blue;
|
||||
font-family: Georgia;
|
||||
position: fixed;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<p>Красивый placeholder:</p>
|
||||
|
||||
<input type="email" data-placeholder="E-mail">
|
||||
|
||||
|
||||
<script>
|
||||
var input = document.querySelector('[data-placeholder]');
|
||||
|
||||
showPlaceholder(input);
|
||||
|
||||
// Показать placeholder внутри input
|
||||
// Также можно сделать это при помощи вёрстки, отдельным элементом
|
||||
function showPlaceholder(input) {
|
||||
input.classList.add('placeholder');
|
||||
input.value = input.dataset.placeholder;
|
||||
}
|
||||
|
||||
// Показать подсказку над элементом (будет вместо placeholder)
|
||||
function showTooltip(input) {
|
||||
var tooltip = document.createElement('span');
|
||||
tooltip.innerHTML = input.dataset.placeholder;
|
||||
tooltip.className = 'placeholder-tooltip';
|
||||
tooltip.style.fontSize = getComputedStyle(input).fontSize;
|
||||
tooltip.style.left = input.getBoundingClientRect().left + 'px';
|
||||
document.body.appendChild(tooltip);
|
||||
tooltip.style.top = input.getBoundingClientRect().top - tooltip.offsetHeight - 4 + 'px';
|
||||
input.tooltip = tooltip;
|
||||
}
|
||||
|
||||
input.onfocus = function() {
|
||||
if (input.classList.contains('placeholder')) {
|
||||
input.classList.remove('placeholder');
|
||||
input.value = '';
|
||||
}
|
||||
|
||||
showTooltip(input);
|
||||
};
|
||||
|
||||
input.onblur = function() {
|
||||
document.body.removeChild(input.tooltip);
|
||||
delete input.tooltip;
|
||||
|
||||
// показываем placeholder обратно, если input пуст
|
||||
if (input.value == '') {
|
||||
showPlaceholder(input);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,48 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<style>
|
||||
/* стиль для input с плейсхолдером */
|
||||
|
||||
.placeholder {
|
||||
color: blue;
|
||||
font-family: Georgia;
|
||||
}
|
||||
/* стиль для подсказки над элементом (вместо плейсхолдера при фокусировке) */
|
||||
|
||||
.placeholder-tooltip {
|
||||
color: blue;
|
||||
font-family: Georgia;
|
||||
position: fixed;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<p>Красивый placeholder:</p>
|
||||
|
||||
<input type="email" data-placeholder="E-mail">
|
||||
|
||||
|
||||
<script>
|
||||
var input = document.querySelector('[data-placeholder]');
|
||||
|
||||
showPlaceholder(input);
|
||||
|
||||
// Показать placeholder внутри input
|
||||
// Также можно сделать это при помощи вёрстки, отдельным элементом
|
||||
function showPlaceholder(input) {
|
||||
input.classList.add('placeholder');
|
||||
input.value = input.dataset.placeholder;
|
||||
}
|
||||
|
||||
// ...ваш код для input...
|
||||
</script>
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,19 +0,0 @@
|
|||
# Улучшенный плейсхолдер
|
||||
|
||||
[importance 5]
|
||||
|
||||
Реализуйте более удобный плейсхолдер-подсказку на JavaScript через атрибут `data-placeholder`.
|
||||
|
||||
Правила работы плейсхолдера:
|
||||
<ul>
|
||||
<li>Элемент изначально содержит плейсхолдер. Специальный класс `placeholder` придает ему синий цвет.</li>
|
||||
<li>При фокусировке плейсхолдер показывается уже над полем, становясь "подсказкой".</li>
|
||||
<li>При снятии фокуса, подсказка убирается, если поле пустое -- плейсхолдер возвращается в него.</li>
|
||||
</ul>
|
||||
|
||||
Демо:
|
||||
|
||||
[iframe src="solution" height=100]
|
||||
|
||||
В этой задаче плейсхолдер должен работать на одном конкретном input. Подумайте, если input много, как здесь применить делегирование?
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
|
||||
Нам нужно ловить `onclick` на мышонке и в `onkeydown` на нём смотреть коды символов. При скан-кодах стрелок двигать мышонка через `position:absolute` или `position:fixed`.
|
||||
|
||||
Скан-коды для клавиш стрелок можно узнать, нажимая на них на [тестовом стенде](#keyboard-test-stand). Вот они: 37-38-39-40 (влево-вверх-вправо-вниз).
|
||||
|
||||
Проблема может возникнуть одна -- `keydown` не возникает на элементе, если на нём нет фокуса.
|
||||
|
||||
Чтобы фокус был -- нужно добавить мышонку атрибут `tabindex` через JS или в HTML.
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
#mouse {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#mouse:focus {
|
||||
outline: 1px dashed black;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<p>Кликните на мышонка и передвигайте его, нажимая клавиши со стрелками.</p>
|
||||
|
||||
<pre id="mouse" tabindex="0">
|
||||
_ _
|
||||
(q\_/p)
|
||||
/. .\
|
||||
=\_t_/= __
|
||||
/ \ (
|
||||
(( )) )
|
||||
/\) (/\ /
|
||||
\ Y /-'
|
||||
nn^nn
|
||||
</pre>
|
||||
|
||||
|
||||
<script>
|
||||
document.getElementById('mouse').onclick = function() {
|
||||
this.style.left = this.getBoundingClientRect().left + 'px';
|
||||
this.style.top = this.getBoundingClientRect().top + 'px';
|
||||
|
||||
this.style.position = 'fixed';
|
||||
};
|
||||
|
||||
|
||||
document.getElementById('mouse').onkeydown = function(e) {
|
||||
switch (e.keyCode) {
|
||||
case 37: // влево
|
||||
this.style.left = parseInt(this.style.left) - this.offsetWidth + 'px';
|
||||
return false;
|
||||
case 38: // вверх
|
||||
this.style.top = parseInt(this.style.top) - this.offsetHeight + 'px';
|
||||
return false;
|
||||
case 39: // вправо
|
||||
this.style.left = parseInt(this.style.left) + this.offsetWidth + 'px';
|
||||
return false;
|
||||
case 40: // вниз
|
||||
this.style.top = parseInt(this.style.top) + this.offsetHeight + 'px';
|
||||
return false;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,42 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
#mouse {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#mouse:focus {
|
||||
outline: 1px dashed black;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<p>Кликните на мышонка и передвигайте его, нажимая клавиши со стрелками.</p>
|
||||
|
||||
<pre id="mouse">
|
||||
_ _
|
||||
(q\_/p)
|
||||
/. .\
|
||||
=\_t_/= __
|
||||
/ \ (
|
||||
(( )) )
|
||||
/\) (/\ /
|
||||
\ Y /-'
|
||||
nn^nn
|
||||
</pre>
|
||||
|
||||
|
||||
<script>
|
||||
// ваш код
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,11 +0,0 @@
|
|||
# Мышонок на "клавиатурном" приводе
|
||||
|
||||
[importance 4]
|
||||
|
||||
Кликните по мышонку. Затем нажимайте клавиши со стрелками, и он будет двигаться.
|
||||
|
||||
[demo src="solution"]
|
||||
|
||||
В этой задаче запрещается ставить обработчики куда-либо, кроме элемента `#mouse`.
|
||||
|
||||
Можно изменять атрибуты и классы в HTML.
|
|
@ -1,100 +0,0 @@
|
|||
# CSS для решения
|
||||
|
||||
Как видно из исходного кода, `#view` -- это `<div>`, который будет содержать результат, а `#area` - это редактируемое текстовое поле.
|
||||
|
||||
Так как мы преобразуем `<div>` в `<textarea>` и обратно, нам нужно сделать их практически одинаковыми с виду:
|
||||
|
||||
```css
|
||||
#view,
|
||||
#area {
|
||||
height: 150px;
|
||||
width: 400px;
|
||||
font-family: arial;
|
||||
font-size: 14px;
|
||||
}
|
||||
```
|
||||
|
||||
Текстовое поле нужно как-то выделить. Можно добавить границу, но тогда изменится блок: он увеличится в размерах и немного съедет текст.
|
||||
|
||||
Для того, чтобы сделать размер `#area` таким же, как и `#view`, добавим поля(padding):
|
||||
|
||||
```css
|
||||
#view {
|
||||
/* padding + border = 3px */
|
||||
|
||||
padding: 2px;
|
||||
border: 1px solid black;
|
||||
}
|
||||
```
|
||||
|
||||
CSS для `#area` заменяет поля границами:
|
||||
|
||||
```css
|
||||
#area {
|
||||
border: 3px groove blue;
|
||||
padding: 0px;
|
||||
display: none;
|
||||
}
|
||||
```
|
||||
|
||||
По умолчанию, текстовое поле скрыто. Кстати, этот код убирает дополнительную рамку в ряде браузеров, которая появляется вокруг поля, когда на него попадает фокус:
|
||||
|
||||
```css
|
||||
/*+ no-beautify */
|
||||
#area:focus {
|
||||
outline: none; /* убирает рамку при фокусе */
|
||||
}
|
||||
```
|
||||
|
||||
# Горячие клавиши
|
||||
|
||||
Чтобы отследить горячие клавиши, нам нужны их скан-коды, а не символы. Это важно, потому что горячие клавиши должны работать независимо от языковой раскладки. Поэтому, мы будем использовать <code>keydown</code>:
|
||||
|
||||
```js
|
||||
document.onkeydown = function(e) {
|
||||
if (e.keyCode == 27) { // escape
|
||||
cancel();
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((e.ctrlKey && e.keyCode == 'E'.charCodeAt(0)) && !area.offsetHeight) {
|
||||
edit();
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((e.ctrlKey && e.keyCode == 'S'.charCodeAt(0)) && area.offsetHeight) {
|
||||
save();
|
||||
return false;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
В примере выше, `offsetHeight` используется для того, чтобы проверить, отображается элемент или нет. Это очень надежный способ для всех элементов, кроме `<tr>` в некоторых старых браузерах.
|
||||
|
||||
В отличие от простой проверки `display=='none'`, этот способ работает с элементом, спрятанным с помощью стилей, а так же для элементов, у которых скрыты родители.
|
||||
|
||||
# Редактирование
|
||||
|
||||
Следующие функции переключают режимы. HTML-код разрешен, поэтому возможна прямая трансформация в `<textarea>` и обратно.
|
||||
|
||||
```js
|
||||
function edit() {
|
||||
view.style.display = 'none';
|
||||
area.value = view.innerHTML;
|
||||
area.style.display = 'block';
|
||||
area.focus();
|
||||
}
|
||||
|
||||
function save() {
|
||||
area.style.display = 'none';
|
||||
view.innerHTML = area.value;
|
||||
view.style.display = 'block';
|
||||
}
|
||||
|
||||
function cancel() {
|
||||
area.style.display = 'none';
|
||||
view.style.display = 'block';
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="my.css">
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<ul>
|
||||
<li>Ctrl-E для начала редактирования.</li>
|
||||
<li>Во время редактирования: Ctrl-S для сохранения, Esc для отмены.</li>
|
||||
</ul>
|
||||
|
||||
HTML разрешён.
|
||||
|
||||
<textarea id="area"></textarea>
|
||||
<div id="view">Текст</div>
|
||||
|
||||
<script>
|
||||
document.onkeydown = function(e) {
|
||||
if (e.keyCode == 27) { // escape
|
||||
cancel();
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((e.ctrlKey && e.keyCode == 'E'.charCodeAt(0)) && !area.offsetHeight) {
|
||||
edit();
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((e.ctrlKey && e.keyCode == 'S'.charCodeAt(0)) && area.offsetHeight) {
|
||||
save();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function edit() {
|
||||
view.style.display = 'none';
|
||||
area.value = view.innerHTML;
|
||||
area.style.display = 'block';
|
||||
area.focus();
|
||||
}
|
||||
|
||||
function save() {
|
||||
area.style.display = 'none';
|
||||
view.innerHTML = area.value;
|
||||
view.style.display = 'block';
|
||||
}
|
||||
|
||||
function cancel() {
|
||||
area.style.display = 'none';
|
||||
view.style.display = 'block';
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,27 +0,0 @@
|
|||
#view,
|
||||
#area {
|
||||
height: 150px;
|
||||
width: 400px;
|
||||
font-family: arial;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
#view {
|
||||
/* padding + border = 3px */
|
||||
|
||||
padding: 2px;
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
#area {
|
||||
display: none;
|
||||
/* replace padding with border (still 3px not to shift the contents) */
|
||||
|
||||
border: 3px groove blue;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
#area:focus {
|
||||
outline: none;
|
||||
/* remove focus border in Safari */
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="my.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<ul>
|
||||
<li>Ctrl-E to start editing.</li>
|
||||
<li>While editing: Ctrl-S to save, Esc to cancel.</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<textarea id="area"></textarea>
|
||||
<div id="view">Text</div>
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,27 +0,0 @@
|
|||
#view,
|
||||
#area {
|
||||
height: 150px;
|
||||
width: 400px;
|
||||
font-family: arial;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
#view {
|
||||
/* padding + border = 3px */
|
||||
|
||||
padding: 2px;
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
#area {
|
||||
display: none;
|
||||
/* replace padding with border (still 3px not to shift the contents) */
|
||||
|
||||
border: 3px groove blue;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
#area:focus {
|
||||
outline: none;
|
||||
/* remove focus border in Safari */
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
# Горячие клавиши
|
||||
|
||||
[importance 5]
|
||||
|
||||
Создайте `<div>`, который при нажатии [key Ctrl+E] превращается в `<textarea>`.
|
||||
|
||||
Изменения, внесенные в поле, можно сохранить обратно в `<div>` сочетанием клавиш [key Ctrl+S], при этом `<div>` получит в виде HTML содержимое `<textarea>`.
|
||||
|
||||
Если же нажать [key Esc], то `<textarea>` снова превращается в `<div>`, изменения не сохраняются.
|
||||
|
||||
[demo src="solution"].
|
||||
|
||||
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
<ol>
|
||||
<li>При клике -- заменяем `innerHTML` ячейки на `<textarea>` с размерами "под ячейку", без рамки.</li>
|
||||
<li>В `textarea.value` присваиваем содержимое ячейки.</li>
|
||||
<li>Фокусируем посетителя на ячейке вызовом `focus()`.</li>
|
||||
<li>Показываем кнопки OK/CANCEL под ячейкой.</li>
|
||||
</ol>
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
/* общие стили для таблицы */
|
||||
|
||||
th {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
td {
|
||||
width: 150px;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.nw {
|
||||
background-color: #999;
|
||||
}
|
||||
|
||||
.n {
|
||||
background-color: #03f;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.ne {
|
||||
background-color: #ff6;
|
||||
}
|
||||
|
||||
.w {
|
||||
background-color: #ff0;
|
||||
}
|
||||
|
||||
.c {
|
||||
background-color: #60c;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.e {
|
||||
background-color: #09f;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.sw {
|
||||
background-color: #963;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.s {
|
||||
background-color: #f60;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.se {
|
||||
background-color: #0c3;
|
||||
color: #fff;
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<link rel="stylesheet" href="bagua.css">
|
||||
<link rel="stylesheet" href="my.css">
|
||||
|
||||
|
||||
<p>Кликните на ячейке для начала редактирования. Когда закончите -- нажмите OK или CANCEL.</p>
|
||||
|
||||
<table id="bagua-table">
|
||||
<tr>
|
||||
<th colspan="3"><em>Bagua</em> Chart: Direction, Element, Color, Meaning</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nw"><strong>Northwest</strong>
|
||||
<br>Metal
|
||||
<br>Silver
|
||||
<br>Elders
|
||||
</td>
|
||||
<td class="n"><strong>North</strong>
|
||||
<br>Water
|
||||
<br>Blue
|
||||
<br>Change
|
||||
</td>
|
||||
<td class="ne"><strong>Northeast</strong>
|
||||
<br>Earth
|
||||
<br>Yellow
|
||||
<br>Direction
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="w"><strong>West</strong>
|
||||
<br>Metal
|
||||
<br>Gold
|
||||
<br>Youth
|
||||
</td>
|
||||
<td class="c"><strong>Center</strong>
|
||||
<br>All
|
||||
<br>Purple
|
||||
<br>Harmony
|
||||
</td>
|
||||
<td class="e"><strong>East</strong>
|
||||
<br>Wood
|
||||
<br>Blue
|
||||
<br>Future
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="sw"><strong>Southwest</strong>
|
||||
<br>Earth
|
||||
<br>Brown
|
||||
<br>Tranquility
|
||||
</td>
|
||||
<td class="s"><strong>South</strong>
|
||||
<br>Fire
|
||||
<br>Orange
|
||||
<br>Fame
|
||||
</td>
|
||||
<td class="se"><strong>Southeast</strong>
|
||||
<br>Wood
|
||||
<br>Green
|
||||
<br>Romance
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
|
||||
<script src="script.js"></script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,23 +0,0 @@
|
|||
.edit-td .edit-area {
|
||||
border: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: block;
|
||||
resize: none;
|
||||
/* remove resizing handle in Firefox */
|
||||
|
||||
outline: none;
|
||||
/* remove outline on focus in Chrome */
|
||||
|
||||
overflow: auto;
|
||||
/* remove scrollbar in IE */
|
||||
}
|
||||
|
||||
.edit-controls {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.edit-td {
|
||||
position: relative;
|
||||
padding: 0;
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
var table = document.getElementById('bagua-table');
|
||||
|
||||
var editingTd;
|
||||
|
||||
table.onclick = function(event) {
|
||||
|
||||
var target = event.target;
|
||||
|
||||
while (target != table) {
|
||||
if (target.className == 'edit-cancel') {
|
||||
finishTdEdit(editingTd.elem, false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (target.className == 'edit-ok') {
|
||||
finishTdEdit(editingTd.elem, true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (target.nodeName == 'TD') {
|
||||
if (editingTd) return; // already editing
|
||||
|
||||
makeTdEditable(target);
|
||||
return;
|
||||
}
|
||||
|
||||
target = target.parentNode;
|
||||
}
|
||||
}
|
||||
|
||||
function makeTdEditable(td) {
|
||||
editingTd = {
|
||||
elem: td,
|
||||
data: td.innerHTML
|
||||
};
|
||||
|
||||
td.classList.add('edit-td'); // td, not textarea! the rest of rules will cascade
|
||||
|
||||
var textArea = document.createElement('textarea');
|
||||
textArea.style.width = td.clientWidth + 'px';
|
||||
textArea.style.height = td.clientHeight + 'px';
|
||||
textArea.className = 'edit-area';
|
||||
|
||||
textArea.value = td.innerHTML;
|
||||
td.innerHTML = '';
|
||||
td.appendChild(textArea);
|
||||
textArea.focus();
|
||||
|
||||
td.insertAdjacentHTML("beforeEnd",
|
||||
'<div class="edit-controls"><button class="edit-ok">OK</button><button class="edit-cancel">CANCEL</button></div>'
|
||||
);
|
||||
}
|
||||
|
||||
function finishTdEdit(td, isOk) {
|
||||
if (isOk) {
|
||||
td.innerHTML = td.firstChild.value;
|
||||
} else {
|
||||
td.innerHTML = editingTd.data;
|
||||
}
|
||||
td.classList.remove('edit-td'); // remove edit class
|
||||
editingTd = null;
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
/* общие стили для таблицы */
|
||||
|
||||
th {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
td {
|
||||
width: 150px;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.nw {
|
||||
background-color: #999;
|
||||
}
|
||||
|
||||
.n {
|
||||
background-color: #03f;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.ne {
|
||||
background-color: #ff6;
|
||||
}
|
||||
|
||||
.w {
|
||||
background-color: #ff0;
|
||||
}
|
||||
|
||||
.c {
|
||||
background-color: #60c;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.e {
|
||||
background-color: #09f;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.sw {
|
||||
background-color: #963;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.s {
|
||||
background-color: #f60;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.se {
|
||||
background-color: #0c3;
|
||||
color: #fff;
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<link rel="stylesheet" href="bagua.css">
|
||||
<link rel="stylesheet" href="my.css">
|
||||
|
||||
|
||||
<p>Кликните на ячейке для начала редактирования. Когда закончите -- нажмите OK или CANCEL.</p>
|
||||
|
||||
<table id="bagua-table">
|
||||
<tr>
|
||||
<th colspan="3"><em>Bagua</em> Chart: Direction, Element, Color, Meaning</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nw"><strong>Northwest</strong>
|
||||
<br>Metal
|
||||
<br>Silver
|
||||
<br>Elders
|
||||
</td>
|
||||
<td class="n"><strong>North</strong>
|
||||
<br>Water
|
||||
<br>Blue
|
||||
<br>Change
|
||||
</td>
|
||||
<td class="ne"><strong>Northeast</strong>
|
||||
<br>Earth
|
||||
<br>Yellow
|
||||
<br>Direction
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="w"><strong>West</strong>
|
||||
<br>Metal
|
||||
<br>Gold
|
||||
<br>Youth
|
||||
</td>
|
||||
<td class="c"><strong>Center</strong>
|
||||
<br>All
|
||||
<br>Purple
|
||||
<br>Harmony
|
||||
</td>
|
||||
<td class="e"><strong>East</strong>
|
||||
<br>Wood
|
||||
<br>Blue
|
||||
<br>Future
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="sw"><strong>Southwest</strong>
|
||||
<br>Earth
|
||||
<br>Brown
|
||||
<br>Tranquility
|
||||
</td>
|
||||
<td class="s"><strong>South</strong>
|
||||
<br>Fire
|
||||
<br>Orange
|
||||
<br>Fame
|
||||
</td>
|
||||
<td class="se"><strong>Southeast</strong>
|
||||
<br>Wood
|
||||
<br>Green
|
||||
<br>Romance
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
|
||||
<script src="script.js"></script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1 +0,0 @@
|
|||
/* ваши стили */
|
|
@ -1,3 +0,0 @@
|
|||
var table = document.getElementById('bagua-table');
|
||||
|
||||
/* ваш код */
|
|
@ -1,16 +0,0 @@
|
|||
# Редактирование TD по клику
|
||||
|
||||
[importance 5]
|
||||
|
||||
Сделать ячейки таблицы `td` редактируемыми по клику.
|
||||
|
||||
<ul>
|
||||
<li>При клике -- ячейка `<td>` превращается в редактируемую, можно менять HTML. Размеры ячеек при этом не должны меняться.</li>
|
||||
<li>В один момент может редактироваться одна ячейка.</li>
|
||||
<li>При редактировании под ячейкой появляются кнопки для приема и отмена редактирования, только клик на них заканчивает редактирование.</li>
|
||||
</ul>
|
||||
|
||||
Демо:
|
||||
|
||||
[iframe src="solution"]
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
# Вёрстка
|
||||
|
||||
Для вёрстки можно использовать отрицательный `margin` у текста с подсказкой.
|
||||
|
||||
Решение в плане вёрстка есть в решении задачи [](/task/position-text-into-input).
|
||||
|
||||
# Решение
|
||||
|
||||
```js
|
||||
placeholder.onclick = function() {
|
||||
input.focus();
|
||||
}
|
||||
|
||||
// onfocus сработает и вызове input.focus() и при клике на input
|
||||
input.onfocus = function() {
|
||||
if (placeholder.parentNode) {
|
||||
placeholder.parentNode.removeChild(placeholder);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
[edit src="solution"]Открыть полный код решения[/edit]
|
|
@ -1,33 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div>Добро пожаловать</div>
|
||||
<input type="password" id="input">
|
||||
|
||||
<div id="placeholder">Скажи пароль, друг</div>
|
||||
|
||||
<div>.. и заходи</div>
|
||||
|
||||
<script>
|
||||
var placeholder = document.getElementById('placeholder');
|
||||
var input = document.getElementById('input');
|
||||
|
||||
placeholder.onclick = function() {
|
||||
input.focus();
|
||||
}
|
||||
|
||||
input.onfocus = function() {
|
||||
placeholder.parentNode && placeholder.parentNode.removeChild(placeholder);
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,17 +0,0 @@
|
|||
body {
|
||||
font: 14px/14px Arial, sans-serif;
|
||||
}
|
||||
|
||||
input {
|
||||
font: 14px/14px Arial, sans-serif;
|
||||
width: 12em;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#placeholder {
|
||||
font: 14px/14px Arial, sans-serif;
|
||||
position: absolute;
|
||||
margin: -1.2em 0 0 0.2em;
|
||||
color: red;
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
|
||||
<div>Добро пожаловать</div>
|
||||
<input type="password" id="input">
|
||||
|
||||
<div id="placeholder">Скажи пароль, друг</div>
|
||||
|
||||
<div>.. и заходи</div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,17 +0,0 @@
|
|||
body {
|
||||
font: 14px/14px Arial, sans-serif;
|
||||
}
|
||||
|
||||
input {
|
||||
font: 14px/14px Arial, sans-serif;
|
||||
width: 12em;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#placeholder {
|
||||
font: 14px/14px Arial, sans-serif;
|
||||
position: absolute;
|
||||
margin: -1.2em 0 0 0.2em;
|
||||
color: red;
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
# Красивый плейсхолдер для INPUT
|
||||
|
||||
[importance 5]
|
||||
|
||||
Создайте для `<input type="password">` красивый, стилизованный плейсхолдер, например (кликните на тексте):
|
||||
|
||||
[iframe src="solution" height=90]
|
||||
|
||||
|
||||
|
||||
При клике плейсхолдер просто исчезает и дальше не показывается.
|
|
@ -1,91 +0,0 @@
|
|||
# Алгоритм
|
||||
|
||||
JavaScript не имеет доступа к текущему состоянию [key CapsLock]. При загрузке страницы не известно, включён он или нет.
|
||||
|
||||
Но мы можем догадаться о его состоянии из событий:
|
||||
<ol>
|
||||
<li>Проверив символ, полученный по `keypress`. Символ в верхнем регистре без нажатого [key Shift] означает, что включён [key CapsLock]. Аналогично, символ в нижнем регистре, но с [key Shift] говорят о включенном [key CapsLock]. Свойство `event.shiftKey` показывает, нажат ли [key Shift]. Так мы можем точно узнать, нажат ли [key CapsLock].</li>
|
||||
<li>Проверять `keydown`. Если нажат CapsLock (скан-код равен `20`), то переключить состояние, но лишь в том случае, когда оно уже известно.
|
||||
Под Mac так делать не получится, поскольку клавиатурные события с CapsLock [работают некорректно](#keyboard-events-order).</li>
|
||||
</ol>
|
||||
|
||||
Имея состояние `CapsLock` в переменной, можно при фокусировке на `INPUT` выдавать предупреждение.
|
||||
|
||||
Отслеживать оба события: `keydown` и `keypress` хорошо бы на уровне документа, чтобы уже на момент входа в поле ввода мы знали состояние CapsLock.
|
||||
|
||||
Но при вводе сразу в нужный `input` событие `keypress` событие доплывёт до `document` и поставит состояние CapsLock *после того, как сработает на `input`*. Как это обойти -- подумайте сами.
|
||||
|
||||
# Решение
|
||||
|
||||
При загрузке страницы, когда еще ничего не набрано, мы ничего не знаем о состоянии [key CapsLock], поэтому оно равно `null`:
|
||||
|
||||
```js
|
||||
var capsLockEnabled = null;
|
||||
```
|
||||
|
||||
Когда нажата клавиша, мы можем попытаться проверить, совпадает ли регистр символа и состояние [key Shift]:
|
||||
|
||||
```js
|
||||
document.onkeypress = function(e) {
|
||||
|
||||
var chr = getChar(e);
|
||||
if (!chr) return; // специальная клавиша
|
||||
|
||||
if (chr.toLowerCase() == chr.toUpperCase()) {
|
||||
// символ, который не имеет регистра, такой как пробел,
|
||||
// мы не можем использовать для определения состояния CapsLock
|
||||
return;
|
||||
}
|
||||
|
||||
capsLockEnabled = (chr.toLowerCase() == chr && e.shiftKey) || (chr.toUpperCase() == chr && !e.shiftKey);
|
||||
}
|
||||
```
|
||||
|
||||
Когда пользователь нажимает [key CapsLock], мы должны изменить его текущее состояние. Но мы можем сделать это только если знаем, что был нажат [key CapsLock].
|
||||
|
||||
Например, когда пользователь открыл страницу, мы не знаем, включен ли [key CapsLock]. Затем, мы получаем событие `keydown` для [key CapsLock]. Но мы все равно не знаем его состояния, был ли [key CapsLock] *выключен* или, наоборот, включен.
|
||||
|
||||
```js
|
||||
if (navigator.platform.substr(0, 3) != 'Mac') { // событие для CapsLock глючит под Mac
|
||||
document.onkeydown = function(e) {
|
||||
if (e.keyCode == 20 && capsLockEnabled !== null) {
|
||||
capsLockEnabled = !capsLockEnabled;
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
Теперь поле. Задание состоит в том, чтобы предупредить пользователя о включенном CapsLock, чтобы уберечь его от неправильного ввода.
|
||||
|
||||
<ol>
|
||||
<li>Для начала, когда пользователь сфокусировался на поле, мы должны вывести предупреждение о CapsLock, если он включен.</li>
|
||||
<li>Пользователь начинает ввод. Каждое событие `keypress` всплывает до обработчика `document.keypress`, который обновляет состояние `capsLockEnabled`.
|
||||
|
||||
Мы не можем использовать событие `input.onkeypress`, для отображения состояния пользователю, потому что оно сработает *до* `document.onkeypress` (из-за всплытия) и, следовательно, до того, как мы узнаем состояние [key CapsLock].
|
||||
|
||||
Есть много способов решить эту проблему. Можно, например, назначить обработчик состояния CapsLock на событие `input.onkeyup`. То есть, индикация будет с задержкой, но это несущественно.
|
||||
|
||||
Альтернативное решение -- добавить на `input` такой же обработчик, как и на `document.onkeypress`.
|
||||
</li>
|
||||
<li>...И наконец, пользователь убирает фокус с поля. Предупреждение может быть видно, если [key CapsLock] включен, но так как пользователь уже ушел с поля, то нам нужно спрятать предупреждение.</li>
|
||||
</ol>
|
||||
|
||||
Код проверки поля:
|
||||
|
||||
```html
|
||||
<input type="text" onkeyup="checkCapsWarning(event)" onfocus="checkCapsWarning(event)" onblur="removeCapsWarning()" />
|
||||
|
||||
<div style="display:none;color:red" id="caps">Внимание: нажат CapsLock!</div>
|
||||
|
||||
<script>
|
||||
function checkCapsWarning() {
|
||||
document.getElementById('caps').style.display = capsLockEnabled ? 'block' : 'none';
|
||||
}
|
||||
|
||||
function removeCapsWarning() {
|
||||
document.getElementById('caps').style.display = 'none';
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
[edit src="solution"]Полный код решения[/edit]
|
|
@ -1,77 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
Введите текст(например, пароль) с нажатым CapsLock:
|
||||
<input type="text" onkeyup="checkCapsWarning(event)" onfocus="checkCapsWarning(event)" onblur="removeCapsWarning()" />
|
||||
|
||||
<div style="display:none;color:red" id="capsIndicator">Внимание: нажат CapsLock!</div>
|
||||
|
||||
|
||||
<script>
|
||||
/**
|
||||
* Текущее состояние CapsLock
|
||||
* - null : неизвестно
|
||||
* - true/false : CapsLock включен/выключен
|
||||
*/
|
||||
var capsLockEnabled = null;
|
||||
|
||||
function getChar(event) {
|
||||
if (event.which == null) {
|
||||
if (event.keyCode < 32) return null;
|
||||
return String.fromCharCode(event.keyCode) // IE
|
||||
}
|
||||
|
||||
if (event.which != 0 && event.charCode != 0) {
|
||||
if (event.which < 32) return null;
|
||||
return String.fromCharCode(event.which) // остальные
|
||||
}
|
||||
|
||||
return null; // специальная клавиша
|
||||
}
|
||||
|
||||
if (navigator.platform.substr(0, 3) != 'Mac') { // событие для CapsLock глючит под Mac
|
||||
document.onkeydown = function(e) {
|
||||
if (e.keyCode == 20 && capsLockEnabled !== null) {
|
||||
capsLockEnabled = !capsLockEnabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
document.onkeypress = function(e) {
|
||||
e = e || event;
|
||||
|
||||
var chr = getChar(e);
|
||||
if (!chr) return // special key
|
||||
|
||||
if (chr.toLowerCase() == chr.toUpperCase()) {
|
||||
// символ, не зависящий от регистра, например пробел
|
||||
// не может быть использован для определения CapsLock
|
||||
return;
|
||||
}
|
||||
|
||||
capsLockEnabled = (chr.toLowerCase() == chr && e.shiftKey) || (chr.toUpperCase() == chr && !e.shiftKey);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Проверить CapsLock
|
||||
*/
|
||||
function checkCapsWarning() {
|
||||
document.getElementById('capsIndicator').style.display = capsLockEnabled ? 'block' : 'none';
|
||||
}
|
||||
|
||||
function removeCapsWarning() {
|
||||
document.getElementById('capsIndicator').style.display = 'none';
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,19 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
|
||||
Введите текст(например, пароль) с нажатым CapsLock:
|
||||
<input type="text" />
|
||||
|
||||
<div style="display:none;color:red" id="capsIndicator">Внимание: нажат CapsLock!</div>
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,10 +0,0 @@
|
|||
# Поле, предупреждающее о включенном CapsLock
|
||||
|
||||
[importance 3]
|
||||
|
||||
Создайте поле, которое будет предупреждать пользователя, если включен [key CapsLock]. Выключение [key CapsLock] уберёт предупреждение.
|
||||
|
||||
Такое поле может помочь избежать ошибок при вводе пароля.
|
||||
|
||||
[iframe height=80 src="solution"]
|
||||
|
|
@ -1,340 +0,0 @@
|
|||
# Фокусировка: focus/blur
|
||||
|
||||
Говорят, что элемент "получает фокус", когда посетитель фокусируется на нём. Обычно фокусировка автоматически происходит при нажатии на элементе мышкой, но также можно перейти на нужный элемент клавиатурой -- через клавишу [key Tab], нажатие пальцем на планшете и так далее.
|
||||
|
||||
Момент получения фокуса и потери очень важен.
|
||||
|
||||
При получении фокуса мы можем подгрузить данные для автодополнения, начать отслеживать изменения. При потере -- проверить данные, которые ввёл посетитель.
|
||||
|
||||
Кроме того, иногда полезно "вручную", из JavaScript перевести фокус на нужный элемент, например, на поле в динамически созданной форме.
|
||||
|
||||
[cut]
|
||||
## События focus/blur
|
||||
|
||||
Событие `focus` вызывается тогда, когда пользователь фокусируется на элементе, а `blur` -- когда фокус исчезает, например посетитель кликает на другом месте экрана.
|
||||
|
||||
Давайте сразу посмотрим на них в деле, используем для проверки ("валидации") введённых в форму значений.
|
||||
|
||||
В примере ниже:
|
||||
<ul>
|
||||
<li>Обработчик `onblur` проверяет, что в поле введено число, если нет -- показывает ошибку.</li>
|
||||
<li>Обработчик `onfocus`, если текущее состояние поля ввода -- "ошибка" -- скрывает её (потом при `onblur` будет повторная проверка).</li>
|
||||
</ul>
|
||||
|
||||
В примере ниже, если набрать что-нибудь в поле "возраст" и завершить ввод, нажав [key Tab] или кликнув в другое место страницы, то введённое значение будет автоматически проверено:
|
||||
|
||||
```html
|
||||
<!--+ run autorun height=60 -->
|
||||
<style> .error { border-color: red; } </style>
|
||||
|
||||
Введите ваш возраст: <input type="text" id="input">
|
||||
|
||||
<div id="error"></div>
|
||||
|
||||
<script>
|
||||
*!*input.onblur*/!* = function() {
|
||||
if (isNaN(this.value)) { // введено не число
|
||||
// показать ошибку
|
||||
this.className = "error";
|
||||
error.innerHTML = 'Вы ввели не число. Исправьте, пожалуйста.'
|
||||
}
|
||||
};
|
||||
|
||||
*!*input.onfocus*/!* = function() {
|
||||
if (this.className == 'error') { // сбросить состояние "ошибка", если оно есть
|
||||
this.className = "";
|
||||
error.innerHTML = "";
|
||||
}
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
## Методы focus/blur
|
||||
|
||||
Методы с теми же названиями переводят/уводят фокус с элемента.
|
||||
|
||||
Для примера модифицируем пример выше, чтобы при неверном вводе посетитель просто не мог уйти с элемента:
|
||||
|
||||
```html
|
||||
<!--+ run autorun height=80 -->
|
||||
<style>
|
||||
.error {
|
||||
background: red;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div>Возраст:
|
||||
<input type="text" id="age">
|
||||
</div>
|
||||
|
||||
<div>Имя:
|
||||
<input type="text">
|
||||
</div>
|
||||
|
||||
<script>
|
||||
age.onblur = function() {
|
||||
if (isNaN(this.value)) { // введено не число
|
||||
// показать ошибку
|
||||
this.classList.add("error");
|
||||
*!*
|
||||
//... и вернуть фокус обратно
|
||||
age.focus();
|
||||
*/!*
|
||||
} else {
|
||||
this.classList.remove("error");
|
||||
}
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
Этот пример работает во всех браузерах, кроме Firefox ([ошибка](https://bugzilla.mozilla.org/show_bug.cgi?id=53579)).
|
||||
|
||||
|
||||
Если ввести что-то нецифровое в поле "возраст", и потом попытаться табом или мышкой перейти на другой `<input>`, то обработчик `onblur` вернёт фокус обратно.
|
||||
|
||||
Обратим внимание -- если из `onblur` сделать `event.preventDefault()`, то такого же эффекта не будет, потому что `onblur` срабатывает уже *после* того, как элемент потерял фокус.
|
||||
|
||||
|
||||
## HTML5 и CSS3 вместо focus/blur
|
||||
|
||||
Прежде чем переходить к более сложным примерам, использующим JavaScript, мы рассмотрим три примера, когда его использовать не надо, а достаточно современного HTML/CSS.
|
||||
|
||||
### Подсветка при фокусировке
|
||||
|
||||
Стилизация полей ввода может быть решена средствами CSS (CSS2.1), а именно -- селектором `:focus`:
|
||||
|
||||
```html
|
||||
<!--+ autorun height=100 -->
|
||||
<style>
|
||||
*!*input:focus*/!* {
|
||||
background: #FA6;
|
||||
outline: none; /* убрать рамку */
|
||||
}
|
||||
</style>
|
||||
<input type="text">
|
||||
|
||||
<p>Селектор :focus выделит элемент при фокусировке на нем и уберёт рамку, которой браузер выделяет этот элемент по умолчанию.</p>
|
||||
```
|
||||
|
||||
В IE (включая более старые) скрыть фокус также может установка специального атрибута [hideFocus](http://msdn.microsoft.com/en-us/library/ie/ms533783.aspx).
|
||||
|
||||
### Автофокус
|
||||
|
||||
При загрузке страницы, если на ней существует элемент с атрибутом `autofocus` -- браузер автоматически фокусируется на этом элементе. Работает во всех браузерах, кроме IE9-.
|
||||
|
||||
```html
|
||||
<!--+ run link -->
|
||||
<input type="text" name="search" *!*autofocus*/!*>
|
||||
```
|
||||
|
||||
Если нужны старые IE, то же самое может сделать JavaScript:
|
||||
|
||||
```html
|
||||
<input type="text" name="search">
|
||||
<script>
|
||||
document.getElementsByName('search')[0].focus();
|
||||
</script>
|
||||
```
|
||||
|
||||
Как правило, этот атрибут используется при изначальной загрузке, для страниц поиска и так далее, где главный элемент очевиден.
|
||||
|
||||
### Плейсхолдер
|
||||
|
||||
*Плейсхолдер* -- это значение-подсказка внутри `INPUT`, которое автоматически исчезает при фокусировке и существует, пока посетитель не начал вводить текст.
|
||||
|
||||
Во всех браузерах, кроме IE9-, это реализуется специальным атрибутом `placeholder`:
|
||||
|
||||
```html
|
||||
<!--+ autorun height=80 -->
|
||||
<input type="text" placeholder="E-mail">
|
||||
```
|
||||
|
||||
В некоторых браузерах этот текст можно стилизовать:
|
||||
|
||||
```html
|
||||
<!--+ autorun height=80 -->
|
||||
<style>
|
||||
.my*!*::-webkit-input-placeholder*/!* {
|
||||
color: red;
|
||||
font-style: italic;
|
||||
}
|
||||
.my*!*::-moz-input-placeholder*/!* {
|
||||
color: red;
|
||||
font-style: italic;
|
||||
}
|
||||
.my*!*::-ms-input-placeholder*/!* {
|
||||
color: red;
|
||||
font-style: italic;
|
||||
}
|
||||
</style>
|
||||
|
||||
<input class="my" type="text" placeholder="E-mail">
|
||||
Стилизованный плейсхолдер
|
||||
```
|
||||
|
||||
## Разрешаем фокус на любом элементе: tabindex
|
||||
|
||||
По умолчанию не все элементы поддерживают фокусировку.
|
||||
|
||||
Перечень элементов немного рознится от браузера к браузеру, например, список для IE описан <a href="http://msdn.microsoft.com/en-us/library/ms536934.aspx">в MSDN</a>, одно лишь верно всегда -- заведомо поддерживают `focus/blur` те элементы, c которыми посетитель может взаимодействовать: `<button>`, `<input>`, `<select>`, `<a>` и т.д.
|
||||
|
||||
С другой стороны, на элементах для форматирования, таких как `<div>`, `<span>`, `<table>` -- по умолчанию сфокусироваться нельзя. Впрочем, существует способ включить фокусировку и для них.
|
||||
|
||||
В HTML есть атрибут `tabindex`.
|
||||
|
||||
Его основной смысл -- это указать номер элемента при переборе клавишей [key Tab].
|
||||
|
||||
То есть, если есть два элемента, первый имеет `tabindex="1"`, а второй `tabindex="2"`, то нажатие [key Tab] при фокусе на первом элементе -- переведёт его на второй.
|
||||
|
||||
Исключением являются специальные значения:
|
||||
<ul>
|
||||
<li>`tabindex="0"` делает элемент всегда последним.</li>
|
||||
<li>`tabindex="-1"` означает, что клавиша [key Tab] будет элемент игнорировать.</li>
|
||||
</ul>
|
||||
|
||||
**Любой элемент поддерживает фокусировку, если у него есть `tabindex`.**
|
||||
|
||||
В примере ниже есть список элементов. Кликните на любой из них и нажмите "tab".
|
||||
|
||||
```html
|
||||
<!--+ autorun no-beautify -->
|
||||
Кликните на первый элемент списка и нажмите Tab. Внимание! Дальнейшие нажатия Tab могут вывести за границы iframe'а с примером.
|
||||
<ul>
|
||||
<li tabindex="1">Один</li>
|
||||
<li tabindex="0">Ноль</li>
|
||||
<li tabindex="2">Два</li>
|
||||
<li tabindex="-1">Минус один</li>
|
||||
</ul>
|
||||
|
||||
<style>
|
||||
li { cursor: pointer; }
|
||||
:focus { outline: 1px dashed green; }
|
||||
</style>
|
||||
```
|
||||
|
||||
Порядок перемещения по клавише "Tab" в примере выше должен быть таким: `1 - 2 - 0` (ноль всегда последний). Продвинутые пользователи частенько используют "Tab" для навигации, и ваше хорошее отношение к ним будет вознаграждено :)
|
||||
|
||||
Обычно `<li>` не поддерживает фокусировку, но здесь есть `tabindex`.
|
||||
|
||||
## Делегирование с focus/blur
|
||||
|
||||
События `focus` и `blur` не всплывают.
|
||||
|
||||
Это грустно, поскольку мы не можем использовать делегирование с ними. Например, мы не можем сделалать так, чтобы при фокусировке в форме она вся подсвечивалась:
|
||||
|
||||
```html
|
||||
<!--+ autorun height=100 -->
|
||||
<!-- при фокусировке на форме ставим ей класс -->
|
||||
<form *!*onfocus="this.className='focused'"*/!*>
|
||||
<input type="text" name="name" value="Ваше имя">
|
||||
<input type="text" name="surname" value="Ваша фамилия">
|
||||
</form>
|
||||
|
||||
<style> .focused { outline: 1px solid red; } </style>
|
||||
```
|
||||
|
||||
Пример выше не работает, т.к. при фокусировке на любом `<input>` событие `focus` срабатывает только на этом элементе и не всплывает наверх. Так что обработчик `onfocus` на форме никогда не сработает.
|
||||
|
||||
Что делать? Неужели мы должны присваивать обработчик каждому полю `<input>`?
|
||||
|
||||
**Это забавно, но хотя `focus/blur` не всплывают, они могут быть пойманы на фазе перехвата.**
|
||||
|
||||
Вот так сработает:
|
||||
|
||||
```html
|
||||
<!--+ autorun height=100 -->
|
||||
<form id="form">
|
||||
<input type="text" name="name" value="Ваше имя">
|
||||
<input type="text" name="surname" value="Ваша фамилия">
|
||||
</form>
|
||||
|
||||
<style>
|
||||
.focused {
|
||||
outline: 1px solid red;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
*!*
|
||||
// ставим обработчики на фазе перехвата, последний аргумент true
|
||||
form.addEventListener("focus", function() {
|
||||
this.classList.add('focused');
|
||||
}, true);
|
||||
|
||||
form.addEventListener("blur", function() {
|
||||
this.classList.remove('focused');
|
||||
}, true);
|
||||
*/!*
|
||||
</script>
|
||||
```
|
||||
|
||||
### События focusin/focusout
|
||||
|
||||
События `focusin/focusout` -- то же самое, что и `focus/blur`, только они всплывают.
|
||||
|
||||
У них две особенности:
|
||||
<ul>
|
||||
<li>Не поддерживаются Firefox (хотя поддерживаются даже старейшими IE), см. [](https://bugzilla.mozilla.org/show_bug.cgi?id=687787).</li>
|
||||
<li>Должны быть назначены не через `on`-свойство, а при помощи `elem.addEventListener`.</li>
|
||||
</ul>
|
||||
|
||||
Из-за отсутствия подержки Firefox эти события используют редко. Получается, что во всех браузерах можно использовать `focus` на стадии перехвата, ну а `focusin/focusout` -- в IE8-, где стадии перехвата нет.
|
||||
|
||||
Подсветка формы в примере ниже работает во всех браузерах.
|
||||
|
||||
```html
|
||||
<!--+ autorun height=60 run -->
|
||||
<form name="form">
|
||||
<input type="text" name="name" value="Ваше имя">
|
||||
<input type="text" name="surname" value="Ваша фамилия">
|
||||
</form>
|
||||
<style>
|
||||
.focused {
|
||||
outline: 1px solid red;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
function onFormFocus() {
|
||||
this.className = 'focused';
|
||||
}
|
||||
|
||||
function onFormBlur() {
|
||||
this.className = '';
|
||||
}
|
||||
|
||||
var form = document.forms.form;
|
||||
|
||||
if (form.addEventListener) {
|
||||
// focus/blur на стадии перехвата срабатывают во всех браузерах
|
||||
// поэтому используем их
|
||||
form.addEventListener('focus', onFormFocus, true);
|
||||
form.addEventListener('blur', onFormBlur, true);
|
||||
} else {
|
||||
// ветка для IE8-, где нет стадии перехвата, но есть focusin/focusout
|
||||
form.onfocusin = onFormFocus;
|
||||
form.onfocusout = onFormBlur;
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
## Итого
|
||||
События `focus/blur` происходят при получении и снятия фокуса с элемента.
|
||||
|
||||
У них есть особенности:
|
||||
<ul>
|
||||
<li>Они не всплывают. Но на фазе перехвата их можно перехватить. Это странно, но это так, не спрашивайте почему.
|
||||
|
||||
Везде, кроме Firefox, поддерживаются всплывающие альтернативы `focusin/focusout`.</li>
|
||||
<li>По умолчанию многие элементы не могут получить фокус. Например, если вы кликните по `DIV`, то фокусировка на нем не произойдет.
|
||||
|
||||
Но это можно изменить, если поставить элементу атрибут `tabIndex`. Этот атрибут также дает возможность контролировать порядок перехода при нажатии [key Tab].
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
[smart header="Текущий элемент: `document.activeElement`"]
|
||||
Кстати, текущий элемент, на котором фокус, доступен как `document.activeElement`.
|
||||
[/smart]
|
||||
|
||||
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue