up
This commit is contained in:
parent
4bca225593
commit
547854a151
1655 changed files with 847 additions and 89231 deletions
|
@ -1,25 +0,0 @@
|
|||
Решение:
|
||||
|
||||
```html
|
||||
<!--+ run -->
|
||||
<select>
|
||||
<option value="Rock">Рок</option>
|
||||
<option value="Blues" selected>Блюз</option>
|
||||
</select>
|
||||
|
||||
<script>
|
||||
var select = document.body.children[0];
|
||||
|
||||
// 1)
|
||||
var selectedOption = select.options[select.selectedIndex];
|
||||
alert( selectedOption.value );
|
||||
|
||||
// 2)
|
||||
var newOption = new Option("Classic", "Классика");
|
||||
select.appendChild(newOption);
|
||||
|
||||
// 3)
|
||||
newOption.selected = true;
|
||||
</script>
|
||||
```
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
# Добавьте опцию к селекту
|
||||
|
||||
[importance 5]
|
||||
|
||||
Есть селект:
|
||||
|
||||
```html
|
||||
<select>
|
||||
<option value="Rock">Рок</option>
|
||||
<option value="Blues" selected>Блюз</option>
|
||||
</select>
|
||||
```
|
||||
|
||||
При помощи JavaScript:
|
||||
<ol>
|
||||
<li>Выведите значение и текст текущей выбранной опции.</li>
|
||||
<li>Добавьте опцию: `<option value="Classic">Классика</option>`.</li>
|
||||
<li>Сделайте её выбранной.</li>
|
||||
</ol>
|
|
@ -1,318 +0,0 @@
|
|||
# Навигация и свойства элементов формы
|
||||
|
||||
Элементы управления, такие как `<form>`, `<input>` и другие имеют большое количество своих важных свойств и ссылок.
|
||||
|
||||
[cut]
|
||||
|
||||
## Псевдомассив form.elements
|
||||
|
||||
Элементы `FORM` можно получить по имени или номеру, используя свойство `document.forms[name/index]`.
|
||||
|
||||
Например:
|
||||
|
||||
```js
|
||||
//+ no-beautify
|
||||
document.forms.my -- форма с именем 'my'
|
||||
document.forms[0] -- первая форма в документе
|
||||
```
|
||||
|
||||
**Любой *элемент* формы `form` можно получить аналогичным образом, используя свойство `form.elements`.**
|
||||
|
||||
<img src="form.png">
|
||||
|
||||
Например:
|
||||
|
||||
```html
|
||||
<!--+ run height=40 -->
|
||||
<body>
|
||||
<form name="my">
|
||||
<input name="one" value="1">
|
||||
<input name="two" value="2">
|
||||
</form>
|
||||
|
||||
<script>
|
||||
var form = document.forms.my; // можно document.forms[0]
|
||||
|
||||
var elem = form.elements.one; // можно form.elements[0]
|
||||
|
||||
alert( elem.value ); // "один"
|
||||
</script>
|
||||
</body>
|
||||
```
|
||||
|
||||
**Может быть несколько элементов с *одинаковым именем*. В таком случае `form.elements[name]` вернет коллекцию элементов**, например:
|
||||
|
||||
```html
|
||||
<!--+ run height=40 -->
|
||||
<body>
|
||||
<form>
|
||||
<input type="radio" name="*!*age*/!*" value="10">
|
||||
<input type="radio" name="*!*age*/!*" value="20">
|
||||
</form>
|
||||
|
||||
<script>
|
||||
var form = document.forms[0];
|
||||
|
||||
var elems = form.elements.age;
|
||||
|
||||
alert(elems[0].value); // 10, первый input
|
||||
</script>
|
||||
</body>
|
||||
```
|
||||
|
||||
Эти ссылки не зависят от окружающих тегов. Элемент может быть "зарыт" где-то глубоко в форме, но он всё равно доступен через `form.elements`.
|
||||
|
||||
**Свойство `elements` также есть у элементов `<fieldset>`.**
|
||||
Вот пример:
|
||||
|
||||
```html
|
||||
<!--+ run height=80 -->
|
||||
<body>
|
||||
<form>
|
||||
<fieldset name="set">
|
||||
<legend>fieldset</legend>
|
||||
<input name="text" type="text">
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
var form = document.forms[0];
|
||||
|
||||
alert( form.elements.text ); // INPUT
|
||||
*!*
|
||||
alert( form.elements.set.elements.text ); // INPUT
|
||||
*/!*
|
||||
</script>
|
||||
</body>
|
||||
```
|
||||
|
||||
Спецификация: [HTML5 Forms](https://html.spec.whatwg.org/multipage/forms.html).
|
||||
|
||||
[warn header="Доступ `form.name` тоже работает, но с особенностями"]
|
||||
Получить доступ к элементам формы можно не только через `form.elements[name/index]`, но и проще: `form[index/name]`.
|
||||
|
||||
Этот способ короче, так как обладает одной неприятной особенностью: если к элементу обратиться по его `name`, а потом свойство `name` изменить, то он по-прежнему будет доступен под старым именем.
|
||||
|
||||
Звучит странно, поэтому посмотрим на примере.
|
||||
|
||||
```html
|
||||
<!--+ run height=40 -->
|
||||
<form name="myform">
|
||||
<input name="text">
|
||||
</form>
|
||||
|
||||
<script>
|
||||
var form = document.forms.myform;
|
||||
|
||||
alert( form.elements.text == form.text ); // true, это тот самый INPUT
|
||||
|
||||
form.text.name = "new-name"; // меняем name ему
|
||||
|
||||
// нет больше элемента с таким именем
|
||||
alert( form.elements.text ); // undefined
|
||||
|
||||
alert( form.text ); // INPUT (а должно быть undefined!)
|
||||
</script>
|
||||
```
|
||||
|
||||
[/warn]
|
||||
|
||||
## Ссылка на форму element.form
|
||||
|
||||
По элементу можно получить его форму, используя свойство `element.form`.
|
||||
|
||||
Пример:
|
||||
|
||||
```html
|
||||
<!--+ run height=40 -->
|
||||
<body>
|
||||
<form>
|
||||
<input type="text" name="*!*surname*/!*">
|
||||
</form>
|
||||
|
||||
<script>
|
||||
var form = document.forms[0];
|
||||
|
||||
var elem = form.elements.surname;
|
||||
|
||||
*!*
|
||||
alert(elem.form == form); // true
|
||||
*/!*
|
||||
</script>
|
||||
</body>
|
||||
```
|
||||
|
||||
Познакомиться с другими свойствами элементов можно в спецификации [HTML5 Forms](https://html.spec.whatwg.org/multipage/forms.html).
|
||||
|
||||
## Элемент label
|
||||
|
||||
Элемент `label` -- один из самых важных в формах.
|
||||
|
||||
**Клик на `label` засчитывается как фокусировка или клик на элементе формы, к которому он относится.**
|
||||
|
||||
Это позволяет посетителям кликать на большой красивой метке, а не на маленьком квадратике `input type=checkbox` (`radio`). Конечно, это очень удобно.
|
||||
|
||||
Есть два способа показать, какой элемент относится к `label`:
|
||||
|
||||
<ol>
|
||||
<li>Дать метке атрибут `for`, равный `id` соответствующего `input`:
|
||||
|
||||
```html
|
||||
<!--+ autorun -->
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<label for="agree">Согласен с правилами</label>
|
||||
</td>
|
||||
<td>
|
||||
<input id="agree" type="checkbox">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<label for="not-a-robot">Я не робот</label>
|
||||
</td>
|
||||
<td>
|
||||
<input id="not-a-robot" type="checkbox">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
```
|
||||
|
||||
</li>
|
||||
<li>Завернуть элемент в `label`. В этом случае можно обойтись без дополнительных атрибутов:
|
||||
|
||||
```html
|
||||
<!--+ autorun no-beautify -->
|
||||
<label>Кликни меня <input type="checkbox"></label>
|
||||
```
|
||||
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
## Элементы input и textarea
|
||||
|
||||
Для большинства типов `input` значение ставится/читается через свойство `value`.
|
||||
|
||||
```js
|
||||
input.value = "Новое значение";
|
||||
textarea.value = "Новый текст";
|
||||
```
|
||||
|
||||
[warn header="Не используйте `textarea.innerHTML`"]
|
||||
Для элементов `textarea` также доступно свойство `innerHTML`, но лучше им не пользоваться: оно хранит только HTML, изначально присутствовавший в элементе, и не меняется при изменении значения.
|
||||
[/warn]
|
||||
|
||||
Исключения -- `input type="checkbox"` и `input type="radio"`
|
||||
|
||||
**Текущее "отмеченное" состояние для `checkbox` и `radio` находится в свойстве `checked` (`true/false`).**
|
||||
|
||||
```js
|
||||
if (input.checked) {
|
||||
alert( "Чекбокс выбран" );
|
||||
}
|
||||
```
|
||||
|
||||
## Элементы select и option
|
||||
|
||||
Селект в JavaScript можно установить двумя путями: поставив значение `select.value`, либо установив свойство `select.selectedIndex` в номер нужной опции.:
|
||||
|
||||
```js
|
||||
select.selectedIndex = 0; // первая опция
|
||||
```
|
||||
|
||||
Установка `selectedIndex = -1` очистит выбор.
|
||||
|
||||
**Список элементов-опций доступен через `select.options`.**
|
||||
|
||||
Если `select` допускает множественный выбор (атрибут `multiple`), то значения можно получить/установить, сделав цикл по `select.options`. При этом выбранные опции будут иметь свойство `option.selected = true`.
|
||||
|
||||
Пример:
|
||||
|
||||
```html
|
||||
<!--+ run -->
|
||||
<form name="form">
|
||||
<select name="genre" *!*multiple*/!*>
|
||||
<option value="blues" selected>Мягкий блюз</option>
|
||||
<option value="rock" selected>Жёсткий рок</option>
|
||||
<option value="classic">Классика</option>
|
||||
</select>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
var form = document.forms[0];
|
||||
var select = form.elements.genre;
|
||||
|
||||
for (var i = 0; i < select.options.length; i++) {
|
||||
var option = select.options[i];
|
||||
*!*
|
||||
if(option.selected) {
|
||||
alert( option.value );
|
||||
}
|
||||
*/!*
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
Спецификация: [the select element](https://html.spec.whatwg.org/multipage/forms.html#the-select-element).
|
||||
|
||||
[smart header="`new Option`"]
|
||||
В стандарте [the option element](https://html.spec.whatwg.org/multipage/forms.html#the-option-element) есть любопытный короткий синтаксис для создания элемента с тегом `option`:
|
||||
|
||||
```js
|
||||
option = new Option(text, value, defaultSelected, selected);
|
||||
```
|
||||
|
||||
Параметры:
|
||||
<ul>
|
||||
<li>`text` -- содержимое,</li>
|
||||
<li>`value` -- значение,</li>
|
||||
<li>`defaultSelected` и `selected` поставьте в `true`, чтобы сделать элемент выбранным.</li>
|
||||
</ul>
|
||||
|
||||
Его можно использовать вместо `document.createElement('option')`, например:
|
||||
|
||||
```js
|
||||
var option = new Option("Текст", "value");
|
||||
// создаст <option value="value">Текст</option>
|
||||
```
|
||||
|
||||
Такой же элемент, но выбранный:
|
||||
|
||||
```js
|
||||
var option = new Option("Текст", "value", true, true);
|
||||
```
|
||||
|
||||
[/smart]
|
||||
|
||||
[smart header="Дополнительные свойства `option`"]
|
||||
|
||||
У элементов `option` также есть особые свойства, которые могут оказаться полезными (см. [the option element](https://html.spec.whatwg.org/multipage/forms.html#the-option-element)):
|
||||
|
||||
<dl>
|
||||
<dt>`selected`</dt>
|
||||
<dd>выбрана ли опция</dd>
|
||||
<dt>`index`</dt>
|
||||
<dd>номер опции в списке селекта</dd>
|
||||
<dt>`text`</dt>
|
||||
<dd>Текстовое содержимое опции (то, что видит посетитель).</dd>
|
||||
</dl>
|
||||
|
||||
[/smart]
|
||||
|
||||
|
||||
## Итого
|
||||
|
||||
Свойства для навигации по формам:
|
||||
|
||||
<dl>
|
||||
<dt>`document.forms`</dt>
|
||||
<dd>Форму можно получить как `document.forms[name/index]`.</dd>
|
||||
<dt>`form.elements`</dt>
|
||||
<dd>Элементы в форме: `form.elements[name/index]`. Каждый элемент имеет ссылку на форму в свойстве `form`. Свойство `elements` также есть у `<fieldset>`.</dd>
|
||||
</dl>
|
||||
|
||||
Значение элементов читается/ставится через `value` или `checked`.
|
||||
|
||||
Для элемента `select` можно задать опцию по номеру через `select.selectedIndex` и перебрать опции через `select.options`. При этом выбранные опции (в том числе при мультиселекте) будут иметь свойство `option.selected = true`.
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 3.8 KiB |
|
@ -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]
|
||||
|
||||
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
Алгоритм решения такой.
|
||||
|
||||
Только численный ввод в поле с суммой разрешаем, повесив обработчик на `keypress`.
|
||||
|
||||
Отслеживаем события изменения для перевычисления результатов:
|
||||
<ul>
|
||||
<li>На `input`: событие `input` и дополнительно `propertychange/keyup` для совместимости со старыми IE.</li>
|
||||
<li>На `checkbox`: событие `click` вместо `change` для совместимости с IE8-.</li>
|
||||
<li>На `select`: событие `change`.</li>
|
||||
</ul>
|
|
@ -1,153 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
td select,
|
||||
td input {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
#diagram td {
|
||||
vertical-align: bottom;
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
#diagram div {
|
||||
margin: auto;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
|
||||
Калькулятор процентов, из расчёта 12% годовых.
|
||||
<form name="calculator">
|
||||
<table>
|
||||
<tr>
|
||||
<td>Сумма</td>
|
||||
<td>
|
||||
<input name="money" type="text" value="10000">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Срок в месяцах</td>
|
||||
<td>
|
||||
<select name="months">
|
||||
<option value="3">3 (минимум)</option>
|
||||
<option value="6">6 (полгода)</option>
|
||||
<option value="12" selected>12 (год)</option>
|
||||
<option value="18">18 (1.5 года)</option>
|
||||
<option value="24">24 (2 года)</option>
|
||||
<option value="30">30 (2.5 года)</option>
|
||||
<option value="36">36 (3 года)</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>С капитализацией</td>
|
||||
<td>
|
||||
<input name="capitalization" type="checkbox">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
<table id="diagram">
|
||||
<tr>
|
||||
<th>Было:</th>
|
||||
<th>Станет:</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th id="money-before"></th>
|
||||
<th id="money-after"></th>
|
||||
</tr>
|
||||
<td>
|
||||
<div style="background: red;width:40px;height:100px"></div>
|
||||
</td>
|
||||
<td>
|
||||
<div style="background: green;width:40px;height:0" id="height-after"></div>
|
||||
</td>
|
||||
</table>
|
||||
|
||||
<script>
|
||||
// event.type должен быть keypress
|
||||
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; // специальная клавиша
|
||||
}
|
||||
|
||||
var form = document.forms.calculator;
|
||||
|
||||
|
||||
var moneyElem = form.elements.money;
|
||||
moneyElem.onkeypress = function(e) {
|
||||
e = e || event;
|
||||
var chr = getChar(e);
|
||||
|
||||
if (e.ctrlKey || e.altKey || chr == null) return; // специальная клавиша
|
||||
if (chr < '0' || chr > '9') return false;
|
||||
}
|
||||
|
||||
// клавиатура, вставить/вырезать клавиатурой
|
||||
moneyElem.onkeyup = calculate;
|
||||
|
||||
// любые действия, кроме IE. В IE9 также работает, кроме удаления
|
||||
moneyElem.oninput = calculate;
|
||||
|
||||
moneyElem.onpropertychange = function() { // для IE8- изменение значения, кроме удаления
|
||||
event.propertyName == "value" && calculate();
|
||||
}
|
||||
|
||||
var capitalizationElem = form.elements.capitalization;
|
||||
capitalizationElem.onclick = calculate;
|
||||
|
||||
var monthsElem = form.elements.months;
|
||||
monthsElem.onchange = calculate;
|
||||
|
||||
|
||||
function calculate() {
|
||||
var sum = +moneyElem.value;
|
||||
if (!sum) return;
|
||||
|
||||
var monthlyIncrease = 0.01;
|
||||
|
||||
if (!capitalizationElem.checked) {
|
||||
sum = sum * (1 + monthlyIncrease * monthsElem.value);
|
||||
} else {
|
||||
|
||||
for (var i = 0; i < monthsElem.value; i++) {
|
||||
// 1000 1010 1020.1
|
||||
sum = sum * (1 + monthlyIncrease);
|
||||
}
|
||||
}
|
||||
sum = Math.round(sum);
|
||||
|
||||
var height = sum / moneyElem.value * 100 + 'px';
|
||||
document.getElementById('height-after').style.height = height;
|
||||
document.getElementById('money-before').innerHTML = moneyElem.value;
|
||||
document.getElementById('money-after').innerHTML = sum;
|
||||
}
|
||||
|
||||
calculate();
|
||||
</script>
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,102 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
td select,
|
||||
td input {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
#diagram td {
|
||||
vertical-align: bottom;
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
#diagram div {
|
||||
margin: auto;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
|
||||
Калькулятор процентов, из расчёта 12% годовых.
|
||||
<form name="calculator">
|
||||
<table>
|
||||
<tr>
|
||||
<td>Сумма</td>
|
||||
<td>
|
||||
<input name="money" type="text" value="10000">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Срок в месяцах</td>
|
||||
<td>
|
||||
<select name="months">
|
||||
<option value="3">3 (минимум)</option>
|
||||
<option value="6">6 (полгода)</option>
|
||||
<option value="12" selected>12 (год)</option>
|
||||
<option value="18">18 (1.5 года)</option>
|
||||
<option value="24">24 (2 года)</option>
|
||||
<option value="30">30 (2.5 года)</option>
|
||||
<option value="36">36 (3 года)</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>С капитализацией</td>
|
||||
<td>
|
||||
<input name="capitalization" type="checkbox">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
<table id="diagram">
|
||||
<tr>
|
||||
<th>Было:</th>
|
||||
<th>Станет:</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th id="money-before"></th>
|
||||
<th id="money-after"></th>
|
||||
</tr>
|
||||
<td>
|
||||
<div style="background: red;width:40px;height:100px"></div>
|
||||
</td>
|
||||
<td>
|
||||
<div style="background: green;width:40px;height:0" id="height-after"></div>
|
||||
</td>
|
||||
</table>
|
||||
|
||||
<script>
|
||||
// вспомогательная функция для получения символа из события keypress
|
||||
// (вдруг понадобится))
|
||||
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; // специальная клавиша
|
||||
}
|
||||
|
||||
// ваш код...
|
||||
</script>
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,20 +0,0 @@
|
|||
# Автовычисление процентов по вкладу
|
||||
|
||||
[importance 5]
|
||||
|
||||
Создайте интерфейс для автоматического вычисления процентов по вкладу.
|
||||
|
||||
Ставка фиксирована: 12% годовых. При включённом поле "капитализация" -- проценты приплюсовываются к сумме вклада каждый месяц ([сложный процент](http://damoney.ru/finance/slozniy-procent.php)).
|
||||
|
||||
Пример:
|
||||
[iframe src="solution" height="350" border="1"]
|
||||
|
||||
Технические требования:
|
||||
<ul>
|
||||
<li>В поле с суммой должно быть нельзя ввести не-цифру. При этом пусть в нём работают специальные клавиши и сочетания Ctrl-X/Ctrl-V.</li>
|
||||
<li>Изменения в форме отражаются в результатах сразу.</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,218 +0,0 @@
|
|||
# Изменение: change, input, cut, copy, paste
|
||||
|
||||
На элементах формы происходят события клавиатуры и мыши, но есть и несколько других, особенных событий.
|
||||
|
||||
## Событие change
|
||||
|
||||
Событие [change](http://www.w3.org/TR/html5/forms.html#event-input-change) происходит по окончании изменении значения элемента формы, когда это изменение зафиксировано.
|
||||
|
||||
Для текстовых элементов это означает, что событие произойдёт не при каждом вводе, а при потере фокуса.
|
||||
|
||||
Например, пока вы набираете что-то в текстовом поле ниже -- события нет. Но как только вы уведёте фокус на другой элемент, например, нажмёте кнопку -- произойдет событие `onchange`.
|
||||
|
||||
```html
|
||||
<!--+ autorun height=40 -->
|
||||
<input type="text" onchange="alert(this.value)">
|
||||
<input type="button" value="Кнопка">
|
||||
```
|
||||
|
||||
Для остальных же элементов: `select`, `input type=checkbox/radio` оно срабатывает сразу при выборе значения.
|
||||
|
||||
[warn header="Поздний `onchange` в IE8-"]
|
||||
В IE8- `checkbox/radio` при изменении мышью не инициируют событие сразу, а ждут потери фокуса.
|
||||
|
||||
Для того, чтобы видеть изменения `checkbox/radio` тут же -- в IE8- нужно повесить обработчик на событие `click` (оно произойдет и при изменении значения с клавиатуры) или воспользоваться событием `propertychange`, описанным далее.
|
||||
[/warn]
|
||||
|
||||
## Событие input
|
||||
|
||||
Событие `input` срабатывает *тут же* при изменении значения текстового элемента и поддерживается всеми браузерами, кроме IE8-.
|
||||
|
||||
В IE9 оно поддерживается частично, а именно -- *не возникает при удалении символов* (как и `onpropertychange`).
|
||||
|
||||
Пример использования (не работает в IE8-):
|
||||
|
||||
```html
|
||||
<!--+ autorun height=40 -->
|
||||
<input type="text"> oninput: <span id="result"></span>
|
||||
<script>
|
||||
var input = document.body.children[0];
|
||||
|
||||
input.oninput = function() {
|
||||
document.getElementById('result').innerHTML = input.value;
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
В современных браузерах `oninput` -- самое главное событие для работы с элементом формы. Именно его, а не `keydown/keypress` следует использовать.
|
||||
|
||||
Если бы ещё не проблемы со старыми IE... Впрочем, их можно решить при помощи события `propertychange`.
|
||||
|
||||
## IE10-, событие propertychange
|
||||
|
||||
Это событие происходит только в IE10-, при любом изменении свойства. Оно позволяет отлавливать изменение тут же. Оно нестандартное, и его основная область использования -- исправление недочётов обработки событий в старых IE.
|
||||
|
||||
Если поставить его на `checkbox` в IE8-, то получится "правильное" событие `change`:
|
||||
|
||||
```html
|
||||
<!--+ autorun height=40 -->
|
||||
<input type="checkbox"> Чекбокс с "onchange", работающим везде одинаково
|
||||
<script>
|
||||
var checkbox = document.body.children[0];
|
||||
|
||||
if ("onpropertychange" in checkbox) {
|
||||
// старый IE
|
||||
*!*
|
||||
checkbox.onpropertychange = function() {
|
||||
// проверим имя изменённого свойства
|
||||
if (event.propertyName == "checked") {
|
||||
alert( checkbox.checked );
|
||||
}
|
||||
};
|
||||
*/!*
|
||||
} else {
|
||||
// остальные браузеры
|
||||
checkbox.onchange = function() {
|
||||
alert( checkbox.checked );
|
||||
};
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
Это событие также срабатывает при изменении значения текстового элемента. Поэтому его можно использовать в старых IE вместо `oninput`.
|
||||
|
||||
К сожалению, в IE9 у него недочёт: оно не срабатывает при удалении символов. Поэтому сочетания `onpropertychange` + `oninput` недостаточно, чтобы поймать любое изменение поля в старых IE. Далее мы рассмотрим пример, как это можно сделать иначе.
|
||||
|
||||
|
||||
## События cut, copy, paste
|
||||
|
||||
Эти события используются редко. Они происходят при вырезании/вставке/копировании значения.
|
||||
|
||||
К сожалению, кросс-браузерного способа получить данные, которые вставляются/копируются, не существует, поэтому их основное применение -- это отмена соответствующей операции.
|
||||
|
||||
Например, вот так:
|
||||
|
||||
```html
|
||||
<!--+ autorun height=40 -->
|
||||
<input type="text" id="input"> event: <span id="result"></span>
|
||||
<script>
|
||||
input.oncut = input.oncopy = input.onpaste = function(event) {
|
||||
result.innerHTML = event.type + ' ' + input.value;
|
||||
return false;
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
## Пример: поле с контролем СМС
|
||||
|
||||
Как видим, событий несколько и они взаимно дополняют друг друга.
|
||||
|
||||
Посмотрим, как их использовать, на примере.
|
||||
|
||||
Сделаем поле для СМС, рядом с которым должно показываться число символов, обновляющееся при каждом изменении поля.
|
||||
|
||||
Как такое реализовать?
|
||||
|
||||
Событие `input` идеально решит задачу во всех браузерах, кроме IE9-. Собственно, если IE9- нам не нужен, то на этом можно и остановиться.
|
||||
|
||||
### IE9-
|
||||
|
||||
В IE8- событие `input` не поддерживается, но, как мы видели ранее, есть `onpropertychange`, которое может заменить его.
|
||||
|
||||
Что же касается IE9 -- там поддерживаются и `input` и `onpropertychange`, но они оба не работают при удалении символов. Поэтому мы будем отслеживать удаление при помощи `keyup` на [key Delete] и [key BackSpace] . А вот удаление командой "вырезать" из меню -- сможет отловить лишь `oncut`.
|
||||
|
||||
Получается вот такая комбинация:
|
||||
|
||||
```html
|
||||
<!--+ autorun run height=60 -->
|
||||
<input type="text" id="sms"> символов: <span id="result"></span>
|
||||
<script>
|
||||
function showCount() {
|
||||
result.innerHTML = sms.value.length;
|
||||
}
|
||||
|
||||
sms.onkeyup = sms.oninput = showCount;
|
||||
sms.onpropertychange = function() {
|
||||
if (event.propertyName == "value") showCount();
|
||||
}
|
||||
sms.oncut = function() {
|
||||
setTimeout(showCount, 0); // на момент oncut значение еще старое
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
Здесь мы добавили вызов `showCount` на все события, которые могут приводить к изменению значения. Да, иногда изменение будет обрабатываться несколько раз, но зато с гарантией. А лишние вызовы легко убрать, например, при помощи `throttle`-декоратора, описанного в задаче [](/task/throttle).
|
||||
|
||||
**Есть и совсем другой простой, но действенный вариант: через `setInterval` регулярно проверять значение и, если оно слишком длинное, обрезать его.**
|
||||
|
||||
Чтобы сэкономить ресурсы браузера, мы можем начинать отслеживание по `onfocus`, а прекращать -- по `onblur`, вот так:
|
||||
|
||||
```html
|
||||
<!--+ autorun height=60 -->
|
||||
<input type="text" id="sms"> символов: <span id="result"></span>
|
||||
|
||||
<script>
|
||||
var timerId;
|
||||
|
||||
sms.onfocus = function() {
|
||||
|
||||
var lastValue = sms.value;
|
||||
timerId = setInterval(function() {
|
||||
if (sms.value != lastValue) {
|
||||
showCount();
|
||||
lastValue = sms.value;
|
||||
}
|
||||
}, 20);
|
||||
};
|
||||
|
||||
sms.onblur = function() {
|
||||
clearInterval(timerId);
|
||||
};
|
||||
|
||||
function showCount() {
|
||||
result.innerHTML = sms.value.length;
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
Обратим внимание -- весь этот "танец с бубном" нужен только для поддержки IE8-, в которых не поддерживается `oninput` и IE9, где `oninput` не работает при удалении.
|
||||
|
||||
## Итого
|
||||
|
||||
События изменения данных:
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Событие</th>
|
||||
<th>Описание</th>
|
||||
<th>Особенности</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>`change`</td>
|
||||
<td>Изменение значения любого элемента формы. Для текстовых элементов срабатывает при потере фокуса.</td>
|
||||
<td>В IE8- на чекбоксах ждет потери фокуса, поэтому для мгновенной реакции ставят также `onclick`-обработчик или `onpropertychange`.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>`input`</td>
|
||||
<td>Событие срабатывает только на текстовых элементах. Оно не ждет потери фокуса, в отличие от `change`.</td>
|
||||
<td>В IE8- не поддерживается, в IE9 не работает при удалении символов.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>`propertychange`</td>
|
||||
<td>Только для IE10-. Универсальное событие для отслеживания изменения свойств элементов. Имя изменённого свойства содержится в `event.propertyName`. Используют для мгновенной реакции на изменение значения в старых IE.
|
||||
</td>
|
||||
<td>В IE9 не срабатывает при удалении символов.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>`cut/copy/paste`</td>
|
||||
<td>Срабатывают при вставке/копировании/удалении текста. Если в их обработчиках отменить действие браузера, то вставки/копирования/удаления не произойдёт.</td>
|
||||
<td>Вставляемое значение получить нельзя: на момент срабатывания события в элементе всё ещё *старое* значение, а новое недоступно.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
Ещё особенность: в IE8- события `change`, `propertychange`, `cut` и аналогичные не всплывают. То есть, обработчики нужно назначать на сам элемент, без делегирования.
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
Модальное окно делается путём добавления к документу `DIV`, полностью перекрывающего документ и имеющего больший `z-index`.
|
||||
|
||||
В результате все клики будут доставаться этому `DIV'у`:
|
||||
|
||||
Стиль:
|
||||
|
||||
```css
|
||||
#cover-div {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 9000;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: gray;
|
||||
opacity: 0.3;
|
||||
}
|
||||
```
|
||||
|
||||
Самой форме можно дать еще больший `z-index`, чтобы она была над `DIV'ом`. Мы не помещаем форму в контейнер, чтобы она не унаследовала полупрозрачность.
|
||||
|
|
@ -1,155 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#prompt-form {
|
||||
display: inline-block;
|
||||
padding: 5px 5px 5px 70px;
|
||||
width: 200px;
|
||||
border: 1px solid black;
|
||||
background: white url(https://js.cx/clipart/prompt.png) no-repeat left 5px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#prompt-form-container {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 9999;
|
||||
display: none;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#prompt-form-container:before {
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
content: '';
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#cover-div {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 9000;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: gray;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
#prompt-form input[name="text"] {
|
||||
display: block;
|
||||
margin: 5px;
|
||||
width: 180px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body style="height:3000px">
|
||||
|
||||
<h1>Нажмите на кнопку ниже</h1>
|
||||
|
||||
<input type="button" value="Нажмите для показа формы ввода" id="show-button">
|
||||
|
||||
|
||||
<div id="prompt-form-container">
|
||||
<form id="prompt-form">
|
||||
<div id="prompt-message"></div>
|
||||
<input name="text" type="text">
|
||||
<input type="submit" value="Ок">
|
||||
<input type="button" name="cancel" value="Отмена">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Показать полупрозрачный DIV, затеняющий всю страницу
|
||||
// (а форма будет не в нем, а рядом с ним, чтобы не полупрозрачная)
|
||||
function showCover() {
|
||||
var coverDiv = document.createElement('div');
|
||||
coverDiv.id = 'cover-div';
|
||||
document.body.appendChild(coverDiv);
|
||||
}
|
||||
|
||||
function hideCover() {
|
||||
document.body.removeChild(document.getElementById('cover-div'));
|
||||
}
|
||||
|
||||
function showPrompt(text, callback) {
|
||||
showCover();
|
||||
var form = document.getElementById('prompt-form');
|
||||
var container = document.getElementById('prompt-form-container');
|
||||
document.getElementById('prompt-message').innerHTML = text;
|
||||
form.elements.text.value = '';
|
||||
|
||||
function complete(value) {
|
||||
hideCover();
|
||||
container.style.display = 'none';
|
||||
document.onkeydown = null;
|
||||
callback(value);
|
||||
}
|
||||
|
||||
form.onsubmit = function() {
|
||||
var value = form.elements.text.value;
|
||||
if (value == '') return false; // игнорировать пустой submit
|
||||
|
||||
complete(value);
|
||||
return false;
|
||||
};
|
||||
|
||||
form.elements.cancel.onclick = function() {
|
||||
complete(null);
|
||||
};
|
||||
|
||||
document.onkeydown = function(e) {
|
||||
if (e.keyCode == 27) { // escape
|
||||
complete(null);
|
||||
}
|
||||
};
|
||||
|
||||
var lastElem = form.elements[form.elements.length - 1];
|
||||
var firstElem = form.elements[0];
|
||||
|
||||
lastElem.onkeydown = function(e) {
|
||||
if (e.keyCode == 9 && !e.shiftKey) {
|
||||
firstElem.focus();
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
firstElem.onkeydown = function(e) {
|
||||
if (e.keyCode == 9 && e.shiftKey) {
|
||||
lastElem.focus();
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
container.style.display = 'block';
|
||||
form.elements.text.focus();
|
||||
}
|
||||
|
||||
document.getElementById('show-button').onclick = function() {
|
||||
showPrompt("Введите что-нибудь<br>...умное :)", function(value) {
|
||||
alert("Вы ввели: " + value);
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,65 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
html,
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#prompt-form {
|
||||
display: inline-block;
|
||||
padding: 5px 5px 5px 70px;
|
||||
width: 200px;
|
||||
border: 1px solid black;
|
||||
background: white url(https://js.cx/clipart/prompt.png) no-repeat left 5px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#prompt-form-container {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 9999;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#prompt-form-container:before {
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
content: '';
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#prompt-form input[name="text"] {
|
||||
display: block;
|
||||
margin: 5px;
|
||||
width: 180px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
|
||||
<div id="prompt-form-container">
|
||||
<form id="prompt-form">
|
||||
<div id="prompt-message">Введите, пожалуйста...
|
||||
<br>Что-то..</div>
|
||||
<input name="text" type="text">
|
||||
<input type="submit" value="Ок">
|
||||
<input type="button" name="cancel" value="Отмена">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,34 +0,0 @@
|
|||
# Модальное диалоговое окно
|
||||
|
||||
[importance 5]
|
||||
|
||||
Создайте функцию `showPrompt(text, callback)`, которая выводит форму для ввода с сообщением `text` и кнопками `ОК/Отмена`.
|
||||
|
||||
<ul>
|
||||
<li>При отправке формы (OK/ввод в текстовом поле) -- должна вызываться функция `callback` со значением поля.</li>
|
||||
<li>При нажатии на `Отмена` или на клавишу [key Esc] -- должна вызываться функция `callback(null)`. Клавиша [key Esc] должна закрывать форму всегда, даже если поле для ввода сообщения не в фокусе.</li>
|
||||
</ul>
|
||||
|
||||
Особенности реализации:
|
||||
<ul>
|
||||
<li>Форма должна показываться в центре окна (и оставаться в центре при изменении его размеров, а также при прокрутке окна!).</li>
|
||||
<li>Текст может состоять из нескольких строк, возможен любой HTML</li>
|
||||
<li>При показе формы остальные элементы страницы использовать нельзя, не работают другие кнопки и т.п, это окно -- *модальное*.</li>
|
||||
<li>При показе формы -- сразу фокус на `INPUT` для ввода.</li>
|
||||
<li>Нажатия [key Tab]/[key Shift+Tab] переключают в цикле только по полям формы, они не позволяют переключиться на другие элементы страницы.</li>
|
||||
</ul>
|
||||
|
||||
Пример использования:
|
||||
|
||||
```js
|
||||
showPrompt("Введите что-нибудь<br>... умное :)", function(value) {
|
||||
alert( value );
|
||||
});
|
||||
```
|
||||
|
||||
Демо в ифрейме:
|
||||
[iframe src="solution" height=160 border=1]
|
||||
|
||||
Исходный HTML/CSS для формы с готовым fixed-позиционированием - в песочнице.
|
||||
|
||||
|
|
@ -1,118 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
td select,
|
||||
td input {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.error input,
|
||||
.error textarea {
|
||||
border: 1px solid red;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
|
||||
<form>
|
||||
<table>
|
||||
<tr>
|
||||
<td>От кого</td>
|
||||
<td>
|
||||
<input name="from" type="text">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Ваш пароль</td>
|
||||
<td>
|
||||
<input name="password" type="password">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Повторите пароль</td>
|
||||
<td>
|
||||
<input name="password2" type="password">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Куда</td>
|
||||
<td>
|
||||
<select name="to">
|
||||
<option></option>
|
||||
<option value="1">Отдел снабжения</option>
|
||||
<option value="2">Отдел разработки</option>
|
||||
<option value="3">Директору</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
Сообщение:
|
||||
<label>
|
||||
<textarea name="message" style="display:block;width:400px;height:80px"></textarea>
|
||||
</label>
|
||||
|
||||
<input type="button" onclick="validate(this.form)" value="Проверить">
|
||||
</form>
|
||||
|
||||
<script>
|
||||
function showError(container, errorMessage) {
|
||||
container.className = 'error';
|
||||
var msgElem = document.createElement('span');
|
||||
msgElem.className = "error-message";
|
||||
msgElem.innerHTML = errorMessage;
|
||||
container.appendChild(msgElem);
|
||||
}
|
||||
|
||||
function resetError(container) {
|
||||
container.className = '';
|
||||
if (container.lastChild.className == "error-message") {
|
||||
container.removeChild(container.lastChild);
|
||||
}
|
||||
}
|
||||
|
||||
function validate(form) {
|
||||
var elems = form.elements;
|
||||
|
||||
resetError(elems.from.parentNode);
|
||||
if (!elems.from.value) {
|
||||
showError(elems.from.parentNode, ' Укажите от кого.');
|
||||
}
|
||||
|
||||
resetError(elems.password.parentNode);
|
||||
if (!elems.password.value) {
|
||||
showError(elems.password.parentNode, ' Укажите пароль.');
|
||||
} else if (elems.password.value != elems.password2.value) {
|
||||
showError(elems.password.parentNode, ' Пароли не совпадают.');
|
||||
}
|
||||
|
||||
resetError(elems.to.parentNode);
|
||||
if (!elems.to.value) {
|
||||
showError(elems.to.parentNode, ' Укажите, куда.');
|
||||
}
|
||||
|
||||
resetError(elems.message.parentNode);
|
||||
if (!elems.message.value) {
|
||||
showError(elems.message.parentNode, ' Отсутствует текст.');
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,74 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
td select,
|
||||
td input {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
}
|
||||
/* ваши стили */
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
|
||||
<form>
|
||||
<table>
|
||||
<tr>
|
||||
<td>От кого</td>
|
||||
<td>
|
||||
<input name="from" type="text">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Ваш пароль</td>
|
||||
<td>
|
||||
<input name="password" type="password">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Повторите пароль</td>
|
||||
<td>
|
||||
<input name="password2" type="password">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Куда</td>
|
||||
<td>
|
||||
<select name="to">
|
||||
<option></option>
|
||||
<option value="1">Отдел снабжения</option>
|
||||
<option value="2">Отдел разработки</option>
|
||||
<option value="3">Директору</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
Сообщение:
|
||||
<label>
|
||||
<textarea name="message" style="display:block;width:400px;height:100px"></textarea>
|
||||
</label>
|
||||
|
||||
<input type="button" onclick="validate(this.form)" value="Проверить">
|
||||
</form>
|
||||
|
||||
<script>
|
||||
function validate(form) {
|
||||
/* ваш код */
|
||||
}
|
||||
|
||||
/* ваш код */
|
||||
</script>
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,17 +0,0 @@
|
|||
# Валидация формы
|
||||
|
||||
[importance 3]
|
||||
|
||||
Напишите функцию `validate(form)`, которая проверяет содержимое формы по клику на кнопку "Проверить".
|
||||
|
||||
Ошибки:
|
||||
<ol>
|
||||
<li>Одно из полей не заполнено.</li>
|
||||
<li>Пароли не совпадают.</li>
|
||||
</ol>
|
||||
|
||||
Ошибка должна сопровождаться сообщением у поля. Например:
|
||||
[iframe height=280 src="solution"]
|
||||
|
||||
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
# Формы: отправка, событие и метод submit
|
||||
|
||||
Событие `submit` возникает при отправке формы. Наиболее частое его применение -- это *валидация* (проверка) формы перед отправкой.
|
||||
|
||||
Метод `submit` позволяет инициировать отправку формы из JavaScript, без участия пользователя. Далее мы рассмотрим детали их использования.
|
||||
[cut]
|
||||
## Событие submit
|
||||
|
||||
Чтобы отправить форму на сервер, у посетителя есть два способа:
|
||||
|
||||
<ol>
|
||||
<li>**Первый -- это нажать кнопку `<input type="submit">` или `<input type="image">`.**</li>
|
||||
<li>**Второй -- нажать Enter, находясь на каком-нибудь поле.**</li>
|
||||
</ol>
|
||||
|
||||
Какой бы способ ни выбрал посетитель -- будет сгенерировано событие `submit`. Обработчик в нём может проверить данные и, если они неверны, то вывести ошибку и сделать `event.preventDefault()` -- тогда форма не отправится на сервер.
|
||||
|
||||
Например, в таком HTML оба способа выведут `alert`, форма не будет отправлена:
|
||||
|
||||
```html
|
||||
<!--+ autorun height=80 no-beautify -->
|
||||
<form onsubmit="alert('submit!');return false">
|
||||
Первый: Enter в текстовом поле <input type="text" value="Текст"><br>
|
||||
Второй: Нажать на "Отправить": <input type="submit" value="Отправить">
|
||||
</form>
|
||||
```
|
||||
|
||||
Ожидаемое поведение:
|
||||
|
||||
<ol><li>Перейдите в текстовое поле и нажмите Enter, будет событие, но форма не отправится на сервер благодаря `return false` в обработчике.</li>
|
||||
<li>То же самое произойдет при клике на `<input type="submit">`.</li>
|
||||
</ol>
|
||||
|
||||
[smart header="Взаимосвязь событий `submit` и `click`"]
|
||||
|
||||
При отправке формы путём нажатия Enter на текстовом поле, на элементе `<input type="submit">` везде, кроме IE8-, генерируется событие `click`.
|
||||
|
||||
Это довольно забавно, учитывая что клика-то и не было.
|
||||
|
||||
```html
|
||||
<!--+ autorun height=80 -->
|
||||
<form onsubmit="alert('submit');return false">
|
||||
<input type="text" size="30" value="При нажатии Enter будет click">
|
||||
<input type="submit" value="Submit" *!*onclick="alert('click')"*/!*>
|
||||
</form>
|
||||
```
|
||||
|
||||
[/smart]
|
||||
|
||||
[warn header="В IE8- событие `submit` не всплывает"]
|
||||
В IE8- событие `submit` не всплывает. Нужно вешать обработчик `submit` на сам элемент формы, без использования делегирования.
|
||||
[/warn]
|
||||
|
||||
|
||||
## Метод submit
|
||||
|
||||
Чтобы отправить форму на сервер из JavaScript -- нужно вызвать на элементе формы метод `form.submit()`.
|
||||
|
||||
При этом само событие `submit` не генерируется. Предполагается, что если программист вызывает метод `form.submit()`, то он выполнил все проверки.
|
||||
|
||||
Это используют, в частности, для искусственной генерации и отправки формы.
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
# Формы, элементы управления
|
||||
|
||||
Особые свойства, методы и события для работы с формами `<form>` и элементами ввода: `<input>`, `<select>` и другими.
|
Loading…
Add table
Add a link
Reference in a new issue