up
This commit is contained in:
parent
1f61c2ab1d
commit
af0ee2a49e
66 changed files with 12263 additions and 2059 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,20 +0,0 @@
|
|||
importance: 5
|
||||
|
||||
---
|
||||
|
||||
# Улучшенный плейсхолдер
|
||||
|
||||
Реализуйте более удобный плейсхолдер-подсказку на JavaScript через атрибут `data-placeholder`.
|
||||
|
||||
Правила работы плейсхолдера:
|
||||
|
||||
- Элемент изначально содержит плейсхолдер. Специальный класс `placeholder` придает ему синий цвет.
|
||||
- При фокусировке плейсхолдер показывается уже над полем, становясь "подсказкой".
|
||||
- При снятии фокуса, подсказка убирается, если поле пустое -- плейсхолдер возвращается в него.
|
||||
|
||||
Демо:
|
||||
|
||||
[iframe src="solution" height=100]
|
||||
|
||||
В этой задаче плейсхолдер должен работать на одном конкретном input. Подумайте, если input много, как здесь применить делегирование?
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
|
||||
Нам нужно ловить `onclick` на мышонке и в `onkeydown` на нём смотреть коды символов. При скан-кодах стрелок двигать мышонка через `position:absolute` или `position:fixed`.
|
||||
|
||||
Скан-коды для клавиш стрелок можно узнать, нажимая на них на [тестовом стенде](info:keyboard-events#keyboard-test-stand). Вот они: 37-38-39-40 (влево-вверх-вправо-вниз).
|
||||
|
||||
Проблема может возникнуть одна -- `keydown` не возникает на элементе, если на нём нет фокуса.
|
||||
|
||||
Чтобы фокус был -- нужно добавить мышонку атрибут `tabindex` через JS или в HTML.
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
importance: 4
|
||||
|
||||
---
|
||||
|
||||
# Мышонок на "клавиатурном" приводе
|
||||
|
||||
Кликните по мышонку. Затем нажимайте клавиши со стрелками, и он будет двигаться.
|
||||
|
||||
[demo src="solution"]
|
||||
|
||||
В этой задаче запрещается ставить обработчики куда-либо, кроме элемента `#mouse`.
|
||||
|
||||
Можно изменять атрибуты и классы в HTML.
|
|
@ -0,0 +1,54 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="my.css">
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<ul>
|
||||
<li>Click the div to edit.</li>
|
||||
<li>Enter or blur saves the result.</li>
|
||||
</ul>
|
||||
|
||||
HTML is allowed.
|
||||
|
||||
<div id="view" class="view">Text</div>
|
||||
|
||||
<script>
|
||||
let area = null;
|
||||
let view = document.getElementById('view');
|
||||
|
||||
view.onclick = function() {
|
||||
editStart();
|
||||
};
|
||||
|
||||
function editStart() {
|
||||
area = document.createElement('textarea');
|
||||
area.className = 'edit';
|
||||
area.value = view.innerHTML;
|
||||
|
||||
area.onkeydown = function(event) {
|
||||
if (event.key == 'Enter') {
|
||||
this.blur();
|
||||
}
|
||||
};
|
||||
|
||||
area.onblur = function() {
|
||||
editEnd();
|
||||
};
|
||||
|
||||
view.replaceWith(area);
|
||||
area.focus();
|
||||
}
|
||||
|
||||
function editEnd() {
|
||||
view.innerHTML = area.value;
|
||||
area.replaceWith(view);
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,27 +1,25 @@
|
|||
#view,
|
||||
#area {
|
||||
.view,
|
||||
.edit {
|
||||
height: 150px;
|
||||
width: 400px;
|
||||
font-family: arial;
|
||||
font-size: 14px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
#view {
|
||||
.view {
|
||||
/* padding + border = 3px */
|
||||
|
||||
padding: 2px;
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
#area {
|
||||
display: none;
|
||||
.edit {
|
||||
/* replace padding with border (still 3px not to shift the contents) */
|
||||
|
||||
border: 3px groove blue;
|
||||
border: 3px solid blue;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
#area:focus {
|
||||
.edit:focus {
|
||||
outline: none;
|
||||
/* remove focus border in Safari */
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="my.css">
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<ul>
|
||||
<li>Click the div to edit.</li>
|
||||
<li>Enter or blur saves the result.</li>
|
||||
</ul>
|
||||
|
||||
HTML is allowed.
|
||||
|
||||
<div id="view" class="view">Text</div>
|
||||
|
||||
<script>
|
||||
// ...your code...
|
||||
// Note: <textarea> should have class="edit"
|
||||
// my.css has styles to make it the same size as div
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,27 +1,25 @@
|
|||
#view,
|
||||
#area {
|
||||
.view,
|
||||
.edit {
|
||||
height: 150px;
|
||||
width: 400px;
|
||||
font-family: arial;
|
||||
font-size: 14px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
#view {
|
||||
.view {
|
||||
/* padding + border = 3px */
|
||||
|
||||
padding: 2px;
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
#area {
|
||||
display: none;
|
||||
.edit {
|
||||
/* replace padding with border (still 3px not to shift the contents) */
|
||||
|
||||
border: 3px groove blue;
|
||||
border: 3px solid blue;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
#area:focus {
|
||||
.edit:focus {
|
||||
outline: none;
|
||||
/* remove focus border in Safari */
|
||||
}
|
||||
}
|
13
2-ui/4-forms-controls/2-focus-blur/3-editable-div/task.md
Normal file
13
2-ui/4-forms-controls/2-focus-blur/3-editable-div/task.md
Normal file
|
@ -0,0 +1,13 @@
|
|||
importance: 5
|
||||
|
||||
---
|
||||
|
||||
# Editable div
|
||||
|
||||
Create a `<div>` that turns into `<textarea>` when clicked.
|
||||
|
||||
The textarea allows to edit the HTML in the `<div>`.
|
||||
|
||||
When the user presses `key:Enter` or it looses focus, the `<textarea>` turns back into `<div>`, and its content becomes HTML in `<div>`.
|
||||
|
||||
[demo src="solution"]
|
|
@ -1,99 +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,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,14 +0,0 @@
|
|||
importance: 5
|
||||
|
||||
---
|
||||
|
||||
# Горячие клавиши
|
||||
|
||||
Создайте `<div>`, который при нажатии `key:Ctrl+E` превращается в `<textarea>`.
|
||||
|
||||
Изменения, внесенные в поле, можно сохранить обратно в `<div>` сочетанием клавиш `key:Ctrl+S`, при этом `<div>` получит в виде HTML содержимое `<textarea>`.
|
||||
|
||||
Если же нажать `key:Esc`, то `<textarea>` снова превращается в `<div>`, изменения не сохраняются.
|
||||
|
||||
[demo src="solution"].
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
|
||||
We can use `mouse.onclick` to handle the click and make the mouse "moveable" with `position:fixed`, then then `mouse.onkeydown` to handle arrow keys.
|
||||
|
||||
The only pitfall is that `keydown` only triggers on elements with focus. So we need to add `tabindex` to the element. As we're forbidden to change HTML, we can use `mouse.tabIndex` property for that.
|
||||
|
||||
P.S. We also can replace `mouse.onclick` with `mouse.onfocus`.
|
|
@ -9,7 +9,7 @@
|
|||
cursor: pointer;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
|
||||
#mouse:focus {
|
||||
outline: 1px dashed black;
|
||||
}
|
||||
|
@ -18,9 +18,9 @@
|
|||
|
||||
<body>
|
||||
|
||||
<p>Кликните на мышонка и передвигайте его, нажимая клавиши со стрелками.</p>
|
||||
<p>Click on the mouse and move it with arrow keys.</p>
|
||||
|
||||
<pre id="mouse" tabindex="0">
|
||||
<pre id="mouse">
|
||||
_ _
|
||||
(q\_/p)
|
||||
/. .\
|
||||
|
@ -34,7 +34,9 @@
|
|||
|
||||
|
||||
<script>
|
||||
document.getElementById('mouse').onclick = function() {
|
||||
mouse.tabIndex = 0;
|
||||
|
||||
mouse.onclick = function() {
|
||||
this.style.left = this.getBoundingClientRect().left + 'px';
|
||||
this.style.top = this.getBoundingClientRect().top + 'px';
|
||||
|
||||
|
@ -42,18 +44,18 @@
|
|||
};
|
||||
|
||||
|
||||
document.getElementById('mouse').onkeydown = function(e) {
|
||||
switch (e.keyCode) {
|
||||
case 37: // влево
|
||||
mouse.onkeydown = function(e) {
|
||||
switch (e.key) {
|
||||
case 'ArrowLeft':
|
||||
this.style.left = parseInt(this.style.left) - this.offsetWidth + 'px';
|
||||
return false;
|
||||
case 38: // вверх
|
||||
case 'ArrowUp':
|
||||
this.style.top = parseInt(this.style.top) - this.offsetHeight + 'px';
|
||||
return false;
|
||||
case 39: // вправо
|
||||
case 'ArrowRight':
|
||||
this.style.left = parseInt(this.style.left) + this.offsetWidth + 'px';
|
||||
return false;
|
||||
case 40: // вниз
|
||||
case 'ArrowDown':
|
||||
this.style.top = parseInt(this.style.top) + this.offsetHeight + 'px';
|
||||
return false;
|
||||
}
|
||||
|
@ -61,5 +63,4 @@
|
|||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
|
@ -9,7 +9,7 @@
|
|||
cursor: pointer;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
|
||||
#mouse:focus {
|
||||
outline: 1px dashed black;
|
||||
}
|
||||
|
@ -18,7 +18,7 @@
|
|||
|
||||
<body>
|
||||
|
||||
<p>Кликните на мышонка и передвигайте его, нажимая клавиши со стрелками.</p>
|
||||
<p>Click on the mouse and move it with arrow keys.</p>
|
||||
|
||||
<pre id="mouse">
|
||||
_ _
|
||||
|
@ -34,9 +34,9 @@
|
|||
|
||||
|
||||
<script>
|
||||
// ваш код
|
||||
// ...your code...
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
12
2-ui/4-forms-controls/2-focus-blur/5-keyboard-mouse/task.md
Normal file
12
2-ui/4-forms-controls/2-focus-blur/5-keyboard-mouse/task.md
Normal file
|
@ -0,0 +1,12 @@
|
|||
importance: 4
|
||||
|
||||
---
|
||||
|
||||
# Keyboard-driven mouse
|
||||
|
||||
Focus on the mouse. Then use arrow keys to move it:
|
||||
|
||||
[demo src="solution"]
|
||||
|
||||
P.S. Don't put event handlers anywhere except the `#mouse` element.
|
||||
P.P.S. Don't modify HTML/CSS, the approach should be generic and work with any element.
|
|
@ -1,21 +0,0 @@
|
|||
# Вёрстка
|
||||
|
||||
Для вёрстки можно использовать отрицательный `margin` у текста с подсказкой.
|
||||
|
||||
Решение в плане вёрстка есть в решении задачи <info: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);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
@ -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 @@
|
|||
importance: 5
|
||||
|
||||
---
|
||||
|
||||
# Красивый плейсхолдер для INPUT
|
||||
|
||||
Создайте для `<input type="password">` красивый, стилизованный плейсхолдер, например (кликните на тексте):
|
||||
|
||||
[iframe src="solution" height=90]
|
||||
|
||||
При клике плейсхолдер просто исчезает и дальше не показывается.
|
|
@ -1,86 +0,0 @@
|
|||
# Алгоритм
|
||||
|
||||
JavaScript не имеет доступа к текущему состоянию `key:CapsLock`. При загрузке страницы не известно, включён он или нет.
|
||||
|
||||
Но мы можем догадаться о его состоянии из событий:
|
||||
|
||||
1. Проверив символ, полученный по `keypress`. Символ в верхнем регистре без нажатого `key:Shift` означает, что включён `key:CapsLock`. Аналогично, символ в нижнем регистре, но с `key:Shift` говорят о включенном `key:CapsLock`. Свойство `event.shiftKey` показывает, нажат ли `key:Shift`. Так мы можем точно узнать, нажат ли `key:CapsLock`.
|
||||
2. Проверять `keydown`. Если нажат CapsLock (скан-код равен `20`), то переключить состояние, но лишь в том случае, когда оно уже известно.
|
||||
Под Mac так делать не получится, поскольку клавиатурные события с CapsLock [работают некорректно](info:keyboard-events#keyboard-events-order).
|
||||
|
||||
Имея состояние `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, чтобы уберечь его от неправильного ввода.
|
||||
|
||||
1. Для начала, когда пользователь сфокусировался на поле, мы должны вывести предупреждение о CapsLock, если он включен.
|
||||
2. Пользователь начинает ввод. Каждое событие `keypress` всплывает до обработчика `document.keypress`, который обновляет состояние `capsLockEnabled`.
|
||||
|
||||
Мы не можем использовать событие `input.onkeypress`, для отображения состояния пользователю, потому что оно сработает *до* `document.onkeypress` (из-за всплытия) и, следовательно, до того, как мы узнаем состояние `key:CapsLock`.
|
||||
|
||||
Есть много способов решить эту проблему. Можно, например, назначить обработчик состояния CapsLock на событие `input.onkeyup`. То есть, индикация будет с задержкой, но это несущественно.
|
||||
|
||||
Альтернативное решение -- добавить на `input` такой же обработчик, как и на `document.onkeypress`.
|
||||
3. ...И наконец, пользователь убирает фокус с поля. Предупреждение может быть видно, если `key:CapsLock` включен, но так как пользователь уже ушел с поля, то нам нужно спрятать предупреждение.
|
||||
|
||||
Код проверки поля:
|
||||
|
||||
```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>
|
||||
```
|
||||
|
|
@ -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,12 +0,0 @@
|
|||
importance: 3
|
||||
|
||||
---
|
||||
|
||||
# Поле, предупреждающее о включенном CapsLock
|
||||
|
||||
Создайте поле, которое будет предупреждать пользователя, если включен `key:CapsLock`. Выключение `key:CapsLock` уберёт предупреждение.
|
||||
|
||||
Такое поле может помочь избежать ошибок при вводе пароля.
|
||||
|
||||
[iframe height=80 src="solution"]
|
||||
|
|
@ -1,58 +1,63 @@
|
|||
# Фокусировка: focus/blur
|
||||
# Focusing: focus/blur
|
||||
|
||||
Говорят, что элемент "получает фокус", когда посетитель фокусируется на нём. Обычно фокусировка автоматически происходит при нажатии на элементе мышкой, но также можно перейти на нужный элемент клавиатурой -- через клавишу `key:Tab`, нажатие пальцем на планшете и так далее.
|
||||
An element receives a focus when the user either clicks on it or uses the `key:Tab` key on the keyboard. There's also an `autofocus` HTML attribute that puts the focus into an element by default when a page loads and other means of getting a focus.
|
||||
|
||||
Момент получения фокуса и потери очень важен.
|
||||
Focusing generally means: "prepare to accept the data here", so that's the moment when we can run the code to initialize or load something.
|
||||
|
||||
При получении фокуса мы можем подгрузить данные для автодополнения, начать отслеживать изменения. При потере -- проверить данные, которые ввёл посетитель.
|
||||
The moment of loosing the focus ("blur") can be even more important. That's when a user clicks somewhere else or presses `key:Tab` to go to the next form field, or there are other means as well.
|
||||
|
||||
Кроме того, иногда полезно "вручную", из JavaScript перевести фокус на нужный элемент, например, на поле в динамически созданной форме.
|
||||
Loosing the focus generally means: "the data has been entered", so we can run the code to check it or even to save it to the server and so on.
|
||||
|
||||
There are important peculiarities when working with focus events. We'll do the best to cover them here.
|
||||
|
||||
[cut]
|
||||
|
||||
## События focus/blur
|
||||
## Events focus/blur
|
||||
|
||||
Событие `focus` вызывается тогда, когда пользователь фокусируется на элементе, а `blur` -- когда фокус исчезает, например посетитель кликает на другом месте экрана.
|
||||
The `focus` event is called on focusing, and `blur` -- when the element loooses the focus.
|
||||
|
||||
Давайте сразу посмотрим на них в деле, используем для проверки ("валидации") введённых в форму значений.
|
||||
Let's use them for validation of an input field.
|
||||
|
||||
В примере ниже:
|
||||
In the example below:
|
||||
|
||||
- Обработчик `onblur` проверяет, что в поле введено число, если нет -- показывает ошибку.
|
||||
- Обработчик `onfocus`, если текущее состояние поля ввода -- "ошибка" -- скрывает её (потом при `onblur` будет повторная проверка).
|
||||
|
||||
В примере ниже, если набрать что-нибудь в поле "возраст" и завершить ввод, нажав `key:Tab` или кликнув в другое место страницы, то введённое значение будет автоматически проверено:
|
||||
- The `blur` handler checks if the field the email is entered, and if not -- shows an error.
|
||||
- The `focus` handler hides the error message (on `blur` it will be checked again):
|
||||
|
||||
```html run autorun height=60
|
||||
<style> .error { border-color: red; } </style>
|
||||
<style>
|
||||
.invalid { border-color: red; }
|
||||
#error { color: red }
|
||||
</style>
|
||||
|
||||
Введите ваш возраст: <input type="text" id="input">
|
||||
Your email please: <input type="email" id="input">
|
||||
|
||||
<div id="error"></div>
|
||||
|
||||
<script>
|
||||
*!*input.onblur*/!* = function() {
|
||||
if (isNaN(this.value)) { // введено не число
|
||||
// показать ошибку
|
||||
this.className = "error";
|
||||
error.innerHTML = 'Вы ввели не число. Исправьте, пожалуйста.'
|
||||
if (!input.value.includes('@')) { // not email
|
||||
input.classList.add('invalid');
|
||||
error.innerHTML = 'Please enter a correct email.'
|
||||
}
|
||||
};
|
||||
|
||||
*!*input.onfocus*/!* = function() {
|
||||
if (this.className == 'error') { // сбросить состояние "ошибка", если оно есть
|
||||
this.className = "";
|
||||
if (this.classList.contains('invalid')) { // сбросить состояние "ошибка", если оно есть
|
||||
this.classList.remove('invalid');
|
||||
error.innerHTML = "";
|
||||
}
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
## Методы focus/blur
|
||||
Modern HTML allows to do many validations using input attributes: `required`, `pattern` and so on. And sometimes they are just what we need. Javascript can be used when we want more flexibility. Also we could automatically send the changed value on the server if it's correct.
|
||||
|
||||
Методы с теми же названиями переводят/уводят фокус с элемента.
|
||||
|
||||
Для примера модифицируем пример выше, чтобы при неверном вводе посетитель просто не мог уйти с элемента:
|
||||
## Methods focus/blur
|
||||
|
||||
Methods `elem.focus()` and `elem.blur()` set/unset the focus on the element.
|
||||
|
||||
For instance, let's make the visitor unable to leave the input if the value is invalid:
|
||||
|
||||
```html run autorun height=80
|
||||
<style>
|
||||
|
@ -61,22 +66,16 @@
|
|||
}
|
||||
</style>
|
||||
|
||||
<div>Возраст:
|
||||
<input type="text" id="age">
|
||||
</div>
|
||||
|
||||
<div>Имя:
|
||||
<input type="text">
|
||||
</div>
|
||||
Your email please: <input type="email" id="input">
|
||||
|
||||
<script>
|
||||
age.onblur = function() {
|
||||
if (isNaN(this.value)) { // введено не число
|
||||
// показать ошибку
|
||||
input.onblur = function() {
|
||||
if (!this.value.includes('@')) { // not email
|
||||
// show the error
|
||||
this.classList.add("error");
|
||||
*!*
|
||||
//... и вернуть фокус обратно
|
||||
age.focus();
|
||||
// ...and put the focus back
|
||||
input.focus();
|
||||
*/!*
|
||||
} else {
|
||||
this.classList.remove("error");
|
||||
|
@ -85,115 +84,42 @@
|
|||
</script>
|
||||
```
|
||||
|
||||
Этот пример работает во всех браузерах, кроме Firefox ([ошибка](https://bugzilla.mozilla.org/show_bug.cgi?id=53579)).
|
||||
It works in all browsers except Firefox ([bug](https://bugzilla.mozilla.org/show_bug.cgi?id=53579)).
|
||||
|
||||
Если ввести что-то нецифровое в поле "возраст", и потом попытаться табом или мышкой перейти на другой `<input>`, то обработчик `onblur` вернёт фокус обратно.
|
||||
If we enter something into the input and then try to use `key:Tab` or click away from the `<input>`, then `onblur` returns the focus back.
|
||||
|
||||
Обратим внимание -- если из `onblur` сделать `event.preventDefault()`, то такого же эффекта не будет, потому что `onblur` срабатывает уже *после* того, как элемент потерял фокус.
|
||||
Please note that we can't "prevent loosing focus" by calling `event.preventDefault()` in `onblur`, because `onblur` works *after* the element lost the focus.
|
||||
|
||||
## HTML5 и CSS3 вместо focus/blur
|
||||
## Allow focusing on any element: tabindex
|
||||
|
||||
Прежде чем переходить к более сложным примерам, использующим JavaScript, мы рассмотрим три примера, когда его использовать не надо, а достаточно современного HTML/CSS.
|
||||
By default many element do not support focusing.
|
||||
|
||||
### Подсветка при фокусировке
|
||||
The list varies between browsers, but one thing is always correct: `focus/blur` support is guaranteed for elements that a visitor can interact with: `<button>`, `<input>`, `<select>`, `<a>` and so on.
|
||||
|
||||
Стилизация полей ввода может быть решена средствами CSS (CSS2.1), а именно -- селектором `:focus`:
|
||||
From the other hand, elements that exist to format something like `<div>`, `<span>`, `<table>` -- are unfocusable by default. The method `elem.focus()` doesn't work on them, and `focus/blur` events are never triggered.
|
||||
|
||||
```html autorun height=100
|
||||
<style>
|
||||
*!*input:focus*/!* {
|
||||
background: #FA6;
|
||||
outline: none; /* убрать рамку */
|
||||
}
|
||||
</style>
|
||||
<input type="text">
|
||||
This can be changed using HTML-attribute `tabindex`.
|
||||
|
||||
<p>Селектор :focus выделит элемент при фокусировке на нем и уберёт рамку, которой браузер выделяет этот элемент по умолчанию.</p>
|
||||
```
|
||||
The purpose of this attribute is to specify the order number of the element when `key:Tab` is used to switch between them.
|
||||
|
||||
В IE (включая более старые) скрыть фокус также может установка специального атрибута [hideFocus](http://msdn.microsoft.com/en-us/library/ie/ms533783.aspx).
|
||||
That is: if we have two elements, the first has `tabindex="1"`, and the second has `tabindex="2"`, then pressing `key:Tab` while in the first element -- moves us to the second one.
|
||||
|
||||
### Автофокус
|
||||
There are two special values:
|
||||
|
||||
При загрузке страницы, если на ней существует элемент с атрибутом `autofocus` -- браузер автоматически фокусируется на этом элементе. Работает во всех браузерах, кроме IE9-.
|
||||
- `tabindex="0"` makes the element the last one.
|
||||
- `tabindex="-1"` means that `key:Tab` should ignore that element.
|
||||
|
||||
```html run link
|
||||
<input type="text" name="search" *!*autofocus*/!*>
|
||||
```
|
||||
**Any element supports focusing if it has `tabindex`.**
|
||||
|
||||
Если нужны старые 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` при фокусе на первом элементе -- переведёт его на второй.
|
||||
|
||||
Исключением являются специальные значения:
|
||||
|
||||
- `tabindex="0"` делает элемент всегда последним.
|
||||
- `tabindex="-1"` означает, что клавиша `key:Tab` будет элемент игнорировать.
|
||||
|
||||
**Любой элемент поддерживает фокусировку, если у него есть `tabindex`.**
|
||||
|
||||
В примере ниже есть список элементов. Кликните на любой из них и нажмите "tab".
|
||||
For instance, here's a list. Click the first item and press `key:Tab`:
|
||||
|
||||
```html autorun no-beautify
|
||||
Кликните на первый элемент списка и нажмите Tab. Внимание! Дальнейшие нажатия Tab могут вывести за границы iframe'а с примером.
|
||||
Click the first item and press Tab. Keep track of the order. Please note that many subsequent Tabs can move the focus out of the iframe with the example.
|
||||
<ul>
|
||||
<li tabindex="1">Один</li>
|
||||
<li tabindex="0">Ноль</li>
|
||||
<li tabindex="2">Два</li>
|
||||
<li tabindex="-1">Минус один</li>
|
||||
<li tabindex="1">One</li>
|
||||
<li tabindex="0">Zero</li>
|
||||
<li tabindex="2">Two</li>
|
||||
<li tabindex="-1">Minus one</li>
|
||||
</ul>
|
||||
|
||||
<style>
|
||||
|
@ -202,121 +128,78 @@
|
|||
</style>
|
||||
```
|
||||
|
||||
Порядок перемещения по клавише "Tab" в примере выше должен быть таким: `1 - 2 - 0` (ноль всегда последний). Продвинутые пользователи частенько используют "Tab" для навигации, и ваше хорошее отношение к ним будет вознаграждено :)
|
||||
The order is like this: `1 - 2 - 0` (zero is always the last). Normally, `<li>` does not support focusing, but `tabindex` full enables it, along with events and styling with `:focus`.
|
||||
|
||||
Обычно `<li>` не поддерживает фокусировку, но здесь есть `tabindex`.
|
||||
## Delegation: focusin/focusout
|
||||
|
||||
## Делегирование с focus/blur
|
||||
Events `focus` and `blur` do not bubble.
|
||||
|
||||
События `focus` и `blur` не всплывают.
|
||||
For instance, we can't put `onfocus` on the `<form>` to highlight it, like this:
|
||||
|
||||
Это грустно, поскольку мы не можем использовать делегирование с ними. Например, мы не можем сделать так, чтобы при фокусировке в форме она вся подсвечивалась:
|
||||
|
||||
```html autorun height=100
|
||||
<!-- при фокусировке на форме ставим ей класс -->
|
||||
```html autorun height=80
|
||||
<!-- on focusing in the form -- add the class -->
|
||||
<form *!*onfocus="this.className='focused'"*/!*>
|
||||
<input type="text" name="name" value="Ваше имя">
|
||||
<input type="text" name="surname" value="Ваша фамилия">
|
||||
<input type="text" name="name" value="Name">
|
||||
<input type="text" name="surname" value="Surname">
|
||||
</form>
|
||||
|
||||
<style> .focused { outline: 1px solid red; } </style>
|
||||
```
|
||||
|
||||
Пример выше не работает, т.к. при фокусировке на любом `<input>` событие `focus` срабатывает только на этом элементе и не всплывает наверх. Так что обработчик `onfocus` на форме никогда не сработает.
|
||||
The example above doesn't work, because when user focuses on an `<input>`, the `focus` event triggers on that input only. It doesn't bubble up. So `form.onfocus` never triggers.
|
||||
|
||||
Что делать? Неужели мы должны присваивать обработчик каждому полю `<input>`?
|
||||
There are two solutions.
|
||||
|
||||
**Это забавно, но хотя `focus/blur` не всплывают, они могут быть пойманы на фазе перехвата.**
|
||||
First, there's a funny historical feature: `focus/blur` do not bubble up, but propagate down on the capturing phase.
|
||||
|
||||
Вот так сработает:
|
||||
This will work:
|
||||
|
||||
```html autorun height=100
|
||||
```html autorun height=80
|
||||
<form id="form">
|
||||
<input type="text" name="name" value="Ваше имя">
|
||||
<input type="text" name="surname" value="Ваша фамилия">
|
||||
<input type="text" name="name" value="Name">
|
||||
<input type="text" name="surname" value="Surname">
|
||||
</form>
|
||||
|
||||
<style>
|
||||
.focused {
|
||||
outline: 1px solid red;
|
||||
}
|
||||
</style>
|
||||
<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);
|
||||
// put the handler on capturing phase (last argument true)
|
||||
form.addEventListener("focus", () => form.classList.add('focused'), true);
|
||||
form.addEventListener("blur", () => form.classList.remove('focused'), true);
|
||||
*/!*
|
||||
</script>
|
||||
```
|
||||
|
||||
### События focusin/focusout
|
||||
Second, there are `focusin` and `focusout` events -- exactly the same as `focus/blur`, but they bubble.
|
||||
|
||||
События `focusin/focusout` -- то же самое, что и `focus/blur`, только они всплывают.
|
||||
Note that they must be assigned using `elem.addEventListener`, not `on<event>`.
|
||||
|
||||
У них две особенности:
|
||||
So here's another working variant:
|
||||
|
||||
- Не поддерживаются Firefox (хотя поддерживаются даже старейшими IE), см. <https://bugzilla.mozilla.org/show_bug.cgi?id=687787>.
|
||||
- Должны быть назначены не через `on`-свойство, а при помощи `elem.addEventListener`.
|
||||
|
||||
Из-за отсутствия подержки 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="Ваша фамилия">
|
||||
```html autorun height=80
|
||||
<form id="form">
|
||||
<input type="text" name="name" value="Name">
|
||||
<input type="text" name="surname" value="Surname">
|
||||
</form>
|
||||
<style>
|
||||
.focused {
|
||||
outline: 1px solid red;
|
||||
}
|
||||
</style>
|
||||
|
||||
<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;
|
||||
}
|
||||
*!*
|
||||
// put the handler on capturing phase (last argument true)
|
||||
form.addEventListener("focusin", () => form.classList.add('focused'));
|
||||
form.addEventListener("focusout", () => form.classList.remove('focused'));
|
||||
*/!*
|
||||
</script>
|
||||
```
|
||||
|
||||
## Итого
|
||||
События `focus/blur` происходят при получении и снятия фокуса с элемента.
|
||||
## Summary
|
||||
|
||||
У них есть особенности:
|
||||
Events `focus` and `blur` trigger on focusing/loosing focus on the element.
|
||||
|
||||
- Они не всплывают. Но на фазе перехвата их можно перехватить. Это странно, но это так, не спрашивайте почему.
|
||||
|
||||
Везде, кроме Firefox, поддерживаются всплывающие альтернативы `focusin/focusout`.
|
||||
- По умолчанию многие элементы не могут получить фокус. Например, если вы кликните по `DIV`, то фокусировка на нем не произойдет.
|
||||
|
||||
Но это можно изменить, если поставить элементу атрибут `tabIndex`. Этот атрибут также дает возможность контролировать порядок перехода при нажатии `key:Tab`.
|
||||
|
||||
```smart header="Текущий элемент: `document.activeElement`"
|
||||
Кстати, текущий элемент, на котором фокус, доступен как `document.activeElement`.
|
||||
```
|
||||
Their specials are:
|
||||
- They do not bubble. Can use capturing state instead or `focusin/focusout`.
|
||||
- Most elements do not support focus by default. Use `tabindex` to make anything focusable.
|
||||
|
||||
The current focused element is available as `document.activeElement`.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue