ok
This commit is contained in:
parent
af0ee2a49e
commit
e2443e8de6
115 changed files with 3177 additions and 866 deletions
|
@ -1,6 +1,5 @@
|
|||
|
||||
1. При клике -- заменяем `innerHTML` ячейки на `<textarea>` с размерами "под ячейку", без рамки.
|
||||
2. В `textarea.value` присваиваем содержимое ячейки.
|
||||
3. Фокусируем посетителя на ячейке вызовом `focus()`.
|
||||
4. Показываем кнопки OK/CANCEL под ячейкой.
|
||||
|
||||
1. On click -- replace `innerHTML` of the cell by `<textarea>` with same sizes and no border. Can use Javascript or CSS to set the right size.
|
||||
2. Set `textarea.value` to `td.innerHTML`.
|
||||
3. Focus on the textarea.
|
||||
4. Show buttons OK/CANCEL under the cell, handle clicks on them.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* общие стили для таблицы */
|
||||
/* common styles for the table, no need to modify these */
|
||||
|
||||
th {
|
||||
text-align: center;
|
||||
|
@ -53,4 +53,4 @@ td {
|
|||
.se {
|
||||
background-color: #0c3;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<link rel="stylesheet" href="my.css">
|
||||
|
||||
|
||||
<p>Кликните на ячейке для начала редактирования. Когда закончите -- нажмите OK или CANCEL.</p>
|
||||
<p>Click on a table cell to edit it. Press OK or CANCEL when you finish.</p>
|
||||
|
||||
<table id="bagua-table">
|
||||
<tr>
|
||||
|
@ -75,4 +75,4 @@
|
|||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
|
|
@ -1,32 +1,25 @@
|
|||
var table = document.getElementById('bagua-table');
|
||||
let table = document.getElementById('bagua-table');
|
||||
|
||||
var editingTd;
|
||||
let editingTd;
|
||||
|
||||
table.onclick = function(event) {
|
||||
|
||||
var target = event.target;
|
||||
// 3 possible targets
|
||||
let target = event.target.closest('.edit-cancel,.edit-ok,td');
|
||||
|
||||
while (target != table) {
|
||||
if (target.className == 'edit-cancel') {
|
||||
finishTdEdit(editingTd.elem, false);
|
||||
return;
|
||||
}
|
||||
if (!table.contains(target)) return;
|
||||
|
||||
if (target.className == 'edit-ok') {
|
||||
finishTdEdit(editingTd.elem, true);
|
||||
return;
|
||||
}
|
||||
if (target.className == 'edit-cancel') {
|
||||
finishTdEdit(editingTd.elem, false);
|
||||
} else if (target.className == 'edit-ok') {
|
||||
finishTdEdit(editingTd.elem, true);
|
||||
} else if (target.nodeName == 'TD') {
|
||||
if (editingTd) return; // already editing
|
||||
|
||||
if (target.nodeName == 'TD') {
|
||||
if (editingTd) return; // already editing
|
||||
|
||||
makeTdEditable(target);
|
||||
return;
|
||||
}
|
||||
|
||||
target = target.parentNode;
|
||||
makeTdEditable(target);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
function makeTdEditable(td) {
|
||||
editingTd = {
|
||||
|
@ -34,9 +27,9 @@ function makeTdEditable(td) {
|
|||
data: td.innerHTML
|
||||
};
|
||||
|
||||
td.classList.add('edit-td'); // td, not textarea! the rest of rules will cascade
|
||||
td.classList.add('edit-td'); // td is in edit state, CSS also styles the area inside
|
||||
|
||||
var textArea = document.createElement('textarea');
|
||||
let textArea = document.createElement('textarea');
|
||||
textArea.style.width = td.clientWidth + 'px';
|
||||
textArea.style.height = td.clientHeight + 'px';
|
||||
textArea.className = 'edit-area';
|
||||
|
@ -57,6 +50,6 @@ function finishTdEdit(td, isOk) {
|
|||
} else {
|
||||
td.innerHTML = editingTd.data;
|
||||
}
|
||||
td.classList.remove('edit-td'); // remove edit class
|
||||
td.classList.remove('edit-td');
|
||||
editingTd = null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* общие стили для таблицы */
|
||||
/* common styles for the table, no need to modify these */
|
||||
|
||||
th {
|
||||
text-align: center;
|
||||
|
@ -53,4 +53,4 @@ td {
|
|||
.se {
|
||||
background-color: #0c3;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<link rel="stylesheet" href="my.css">
|
||||
|
||||
|
||||
<p>Кликните на ячейке для начала редактирования. Когда закончите -- нажмите OK или CANCEL.</p>
|
||||
<p>Click on a table cell to edit it. Press OK or CANCEL when you finish.</p>
|
||||
|
||||
<table id="bagua-table">
|
||||
<tr>
|
||||
|
@ -75,4 +75,4 @@
|
|||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
|
|
@ -1 +1 @@
|
|||
/* ваши стили */
|
||||
/* your styles */
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
var table = document.getElementById('bagua-table');
|
||||
let table = document.getElementById('bagua-table');
|
||||
|
||||
/* ваш код */
|
||||
/* your code */
|
||||
|
|
|
@ -2,15 +2,15 @@ importance: 5
|
|||
|
||||
---
|
||||
|
||||
# Редактирование TD по клику
|
||||
# Edit TD on click
|
||||
|
||||
Сделать ячейки таблицы `td` редактируемыми по клику.
|
||||
Make table cells editable on click.
|
||||
|
||||
- При клике -- ячейка `<td>` превращается в редактируемую, можно менять HTML. Размеры ячеек при этом не должны меняться.
|
||||
- В один момент может редактироваться одна ячейка.
|
||||
- При редактировании под ячейкой появляются кнопки для приема и отмена редактирования, только клик на них заканчивает редактирование.
|
||||
- On click -- the cell should became "editable" (textarea appears inside), we can change HTML. There should be no resize, all geometry should remain the same.
|
||||
- Buttons OK and CANCEL appear below the cell to finish/cancel the editing.
|
||||
- Only one cell may be editable at a moment. While a `<td>` is in "edit mode", clicks on other cells are ignored.
|
||||
- The table may have many cells. Use event delegation.
|
||||
|
||||
Демо:
|
||||
|
||||
[iframe src="solution"]
|
||||
The demo:
|
||||
|
||||
[iframe src="solution" height=400]
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
Алгоритм решения такой.
|
||||
|
||||
Только численный ввод в поле с суммой разрешаем, повесив обработчик на `keypress`.
|
||||
|
||||
Отслеживаем события изменения для перевычисления результатов:
|
||||
|
||||
- На `input`: событие `input` и дополнительно `propertychange/keyup` для совместимости со старыми IE.
|
||||
- На `checkbox`: событие `click` вместо `change` для совместимости с IE8-.
|
||||
- На `select`: событие `change`.
|
|
@ -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,104 +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>
|
||||
<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>
|
||||
</tr>
|
||||
</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,19 +0,0 @@
|
|||
importance: 5
|
||||
|
||||
---
|
||||
|
||||
# Автовычисление процентов по вкладу
|
||||
|
||||
Создайте интерфейс для автоматического вычисления процентов по вкладу.
|
||||
|
||||
Ставка фиксирована: 12% годовых. При включённом поле "капитализация" -- проценты приплюсовываются к сумме вклада каждый месяц ([сложный процент](http://damoney.ru/finance/slozniy-procent.php)).
|
||||
|
||||
Пример:
|
||||
|
||||
[iframe src="solution" height="350" border="1"]
|
||||
|
||||
Технические требования:
|
||||
|
||||
- В поле с суммой должно быть нельзя ввести не-цифру. При этом пусть в нём работают специальные клавиши и сочетания Ctrl-X/Ctrl-V.
|
||||
- Изменения в форме отражаются в результатах сразу.
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
<!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>
|
||||
|
||||
Deposit calculator.
|
||||
|
||||
<form name="calculator">
|
||||
<table>
|
||||
<tr>
|
||||
<td>Initial deposit</td>
|
||||
<td>
|
||||
<input name="money" type="number" value="10000" required>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>How many months?</td>
|
||||
<td>
|
||||
<select name="months">
|
||||
<option value="3">3 (minimum)</option>
|
||||
<option value="6">6 (half-year)</option>
|
||||
<option value="12" selected>12 (one year)</option>
|
||||
<option value="18">18 (1.5 years)</option>
|
||||
<option value="24">24 (2 years)</option>
|
||||
<option value="30">30 (2.5 years)</option>
|
||||
<option value="36">36 (3 years)</option>
|
||||
<option value="60">60 (5 years)</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Interest per year?</td>
|
||||
<td>
|
||||
<input name="interest" type="number" value="5" required>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
<table id="diagram">
|
||||
<tr>
|
||||
<th>Was:</th>
|
||||
<th>Becomes:</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>
|
||||
|
||||
let form = document.forms.calculator;
|
||||
|
||||
form.money.oninput = calculate;
|
||||
form.months.onchange = calculate;
|
||||
form.interest.oninput = calculate;
|
||||
|
||||
function calculate() {
|
||||
let initial = +form.money.value;
|
||||
if (!initial) return;
|
||||
|
||||
let interest = form.interest.value * 0.01;
|
||||
|
||||
if (!interest) return;
|
||||
|
||||
let years = form.months.value / 12;
|
||||
if (!years) return;
|
||||
|
||||
let result = Math.round(initial * (1 + interest * years));
|
||||
|
||||
let height = result / form.money.value * 100 + 'px';
|
||||
document.getElementById('height-after').style.height = height;
|
||||
document.getElementById('money-before').innerHTML = form.money.value;
|
||||
document.getElementById('money-after').innerHTML = result;
|
||||
}
|
||||
|
||||
calculate();
|
||||
</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,89 @@
|
|||
<!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>
|
||||
|
||||
Deposit calculator.
|
||||
|
||||
<form name="calculator">
|
||||
<table>
|
||||
<tr>
|
||||
<td>Initial deposit</td>
|
||||
<td>
|
||||
<input name="money" type="number" value="10000" required>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>How many months?</td>
|
||||
<td>
|
||||
<select name="months">
|
||||
<option value="3">3 (minimum)</option>
|
||||
<option value="6">6 (half-year)</option>
|
||||
<option value="12" selected>12 (one year)</option>
|
||||
<option value="18">18 (1.5 years)</option>
|
||||
<option value="24">24 (2 years)</option>
|
||||
<option value="30">30 (2.5 years)</option>
|
||||
<option value="36">36 (3 years)</option>
|
||||
<option value="60">60 (5 years)</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Interest per year?</td>
|
||||
<td>
|
||||
<input name="interest" type="number" value="5" required>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
<table id="diagram">
|
||||
<tr>
|
||||
<th>Was:</th>
|
||||
<th>Becomes:</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>
|
||||
|
||||
let form = document.forms.calculator;
|
||||
|
||||
// your code
|
||||
</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,21 @@
|
|||
importance: 5
|
||||
|
||||
---
|
||||
|
||||
# Deposit calculator
|
||||
|
||||
Create an interface that allows to enter a sum of bank deposit and percentage, then calculates how much it will be after given periods of time.
|
||||
|
||||
Here's the demo:
|
||||
|
||||
[iframe src="solution" height="350" border="1"]
|
||||
|
||||
Any input change should be processed immediately.
|
||||
|
||||
The formula is:
|
||||
```js
|
||||
// initial: the initial money sum
|
||||
// interest: e.g. 0.05 means 5% per year
|
||||
// years: how many years to wait
|
||||
let result = Math.round(initial * (1 + interest * years));
|
||||
```
|
|
@ -1,185 +1,79 @@
|
|||
# Изменение: change, input, cut, copy, paste
|
||||
# Handling change, input, cut, copy, paste
|
||||
|
||||
На элементах формы происходят события клавиатуры и мыши, но есть и несколько других, особенных событий.
|
||||
Let's discuss various events that accompany data updates.
|
||||
|
||||
## Событие change
|
||||
## Event: change
|
||||
|
||||
Событие [change](http://www.w3.org/TR/html5/forms.html#event-input-change) происходит по окончании изменении значения элемента формы, когда это изменение зафиксировано.
|
||||
The [change](http://www.w3.org/TR/html5/forms.html#event-input-change) event triggers when the element has finished changing.
|
||||
|
||||
Для текстовых элементов это означает, что событие произойдёт не при каждом вводе, а при потере фокуса.
|
||||
For text inputs that means that the event occurs when it looses focus.
|
||||
|
||||
Например, пока вы набираете что-то в текстовом поле ниже -- события нет. Но как только вы уведёте фокус на другой элемент, например, нажмёте кнопку -- произойдет событие `onchange`.
|
||||
For instance, while we are typing in the text field below -- there's no event. But when we move the focus somewhere else, for instance, click on a button -- there will be a `change` event:
|
||||
|
||||
```html autorun height=40
|
||||
```html autorun height=40 run
|
||||
<input type="text" onchange="alert(this.value)">
|
||||
<input type="button" value="Кнопка">
|
||||
<input type="button" value="Button">
|
||||
```
|
||||
|
||||
Для остальных же элементов: `select`, `input type=checkbox/radio` оно срабатывает сразу при выборе значения.
|
||||
For other elements: `select`, `input type=checkbox/radio` it triggers right after the selection changes.
|
||||
|
||||
```warn header="Поздний `onchange` в IE8-"
|
||||
В IE8- `checkbox/radio` при изменении мышью не инициируют событие сразу, а ждут потери фокуса.
|
||||
## Event: input
|
||||
|
||||
Для того, чтобы видеть изменения `checkbox/radio` тут же -- в IE8- нужно повесить обработчик на событие `click` (оно произойдет и при изменении значения с клавиатуры) или воспользоваться событием `propertychange`, описанным далее.
|
||||
```
|
||||
The `input` event triggers every time a value is modified.
|
||||
|
||||
## Событие input
|
||||
For instance:
|
||||
|
||||
Событие `input` срабатывает *тут же* при изменении значения текстового элемента и поддерживается всеми браузерами, кроме IE8-.
|
||||
|
||||
В IE9 оно поддерживается частично, а именно -- *не возникает при удалении символов* (как и `onpropertychange`).
|
||||
|
||||
Пример использования (не работает в IE8-):
|
||||
|
||||
```html autorun height=40
|
||||
<input type="text"> oninput: <span id="result"></span>
|
||||
```html autorun height=40 run
|
||||
<input type="text" id="input"> oninput: <span id="result"></span>
|
||||
<script>
|
||||
var input = document.body.children[0];
|
||||
|
||||
input.oninput = function() {
|
||||
document.getElementById('result').innerHTML = input.value;
|
||||
result.innerHTML = input.value;
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
В современных браузерах `oninput` -- самое главное событие для работы с элементом формы. Именно его, а не `keydown/keypress` следует использовать.
|
||||
If we want to handle every modification of an `<input>` then this event is the best choice.
|
||||
|
||||
Если бы ещё не проблемы со старыми IE... Впрочем, их можно решить при помощи события `propertychange`.
|
||||
Unlike keyboard events it works on any value change, even those that does not involve keyboard actions: pasting with a mouse or using speech recognition to dictate the text.
|
||||
|
||||
## IE10-, событие propertychange
|
||||
```smart header="Can't prevent anything in `oninput`"
|
||||
The `input` event occurs after the value is modified.
|
||||
|
||||
Это событие происходит только в 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>
|
||||
So we can't use `event.preventDefault()` there -- it's just too late, there would be no effect.
|
||||
```
|
||||
|
||||
Это событие также срабатывает при изменении значения текстового элемента. Поэтому его можно использовать в старых IE вместо `oninput`.
|
||||
## Events: cut, copy, paste
|
||||
|
||||
К сожалению, в IE9 у него недочёт: оно не срабатывает при удалении символов. Поэтому сочетания `onpropertychange` + `oninput` недостаточно, чтобы поймать любое изменение поля в старых IE. Далее мы рассмотрим пример, как это можно сделать иначе.
|
||||
These events occur on cutting/copying/pasting a value.
|
||||
|
||||
## События cut, copy, paste
|
||||
They belong to [ClipboardEvent](https://www.w3.org/TR/clipboard-apis/#clipboard-event-interfaces) class and provide access to the data that is copied/pasted.
|
||||
|
||||
Эти события используются редко. Они происходят при вырезании/вставке/копировании значения.
|
||||
We also can use `event.preventDefault()` to abort the action.
|
||||
|
||||
К сожалению, кросс-браузерного способа получить данные, которые вставляются/копируются, не существует, поэтому их основное применение -- это отмена соответствующей операции.
|
||||
For instance, the code below prevents all such events and shows what we are trying to cut/copy/paste:
|
||||
|
||||
Например, вот так:
|
||||
|
||||
```html autorun height=40
|
||||
<input type="text" id="input"> event: <span id="result"></span>
|
||||
```html autorun height=40 run
|
||||
<input type="text" id="input">
|
||||
<script>
|
||||
input.oncut = input.oncopy = input.onpaste = function(event) {
|
||||
result.innerHTML = event.type + ' ' + input.value;
|
||||
alert(event.type + ' - ' + event.clipboardData.getData('text/plain'));
|
||||
return false;
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
## Пример: поле с контролем СМС
|
||||
Technically, we can copy/paste everything. For instance, we can copy and file in the OS file manager, and paste it.
|
||||
|
||||
Как видим, событий несколько и они взаимно дополняют друг друга.
|
||||
There's a list of methods [in the specification](https://www.w3.org/TR/clipboard-apis/#dfn-datatransfer) to work with different data types, read/write to the clipboard.
|
||||
|
||||
Посмотрим, как их использовать, на примере.
|
||||
But please note that clipboard is a "global" OS-level thing. Most browsers allow read/write access to the clipboard only in the scope of certain user actions for the safety. Also it is forbidden to create "custom" clipboard events in all browsers except Firefox.
|
||||
|
||||
Сделаем поле для СМС, рядом с которым должно показываться число символов, обновляющееся при каждом изменении поля.
|
||||
## Summary
|
||||
|
||||
Как такое реализовать?
|
||||
Data change events:
|
||||
|
||||
Событие `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`-декоратора, описанного в задаче <info: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` не работает при удалении.
|
||||
|
||||
## Итого
|
||||
|
||||
События изменения данных:
|
||||
|
||||
| Событие | Описание | Особенности |
|
||||
| Event | Description | Specials |
|
||||
|---------|----------|-------------|
|
||||
| `change`| Изменение значения любого элемента формы. Для текстовых элементов срабатывает при потере фокуса. | В IE8- на чекбоксах ждет потери фокуса, поэтому для мгновенной реакции ставят также <code>onclick</code>-обработчик или <code>onpropertychange</code>. |
|
||||
| `input` | Событие срабатывает только на текстовых элементах. Оно не ждет потери фокуса, в отличие от <code>change</code>. | В IE8- не поддерживается, в IE9 не работает при удалении символов. |
|
||||
| `propertychange` | Только для IE10-. Универсальное событие для отслеживания изменения свойств элементов. Имя изменённого свойства содержится в `event.propertyName`. Используют для мгновенной реакции на изменение значения в старых IE. | В IE9 не срабатывает при удалении символов. |
|
||||
| `cut/copy/paste` | Срабатывают при вставке/копировании/удалении текста. Если в их обработчиках отменить действие браузера, то вставки/копирования/удаления не произойдёт. | Вставляемое значение получить нельзя: на момент срабатывания события в элементе всё ещё *старое* значение, а новое недоступно. |
|
||||
|
||||
Ещё особенность: в IE8- события `change`, `propertychange`, `cut` и аналогичные не всплывают. То есть, обработчики нужно назначать на сам элемент, без делегирования.
|
||||
|
||||
| `change`| A value was changed. | For text inputs triggers on focus loss. |
|
||||
| `input` | For text inputs on every change. | Triggers immediately unlike `change`. |
|
||||
| `cut/copy/paste` | Cut/copy/paste actions. | The action can be prevented. The `event.clipbordData` property gives read/write access to the clipboard. |
|
||||
|
|
|
@ -1,8 +1,4 @@
|
|||
Модальное окно делается путём добавления к документу `DIV`, полностью перекрывающего документ и имеющего больший `z-index`.
|
||||
|
||||
В результате все клики будут доставаться этому `DIV'у`:
|
||||
|
||||
Стиль:
|
||||
A modal window can be implemented using a half-transparent `<div id="cover-div">` that covers the whole window, like this:
|
||||
|
||||
```css
|
||||
#cover-div {
|
||||
|
@ -17,5 +13,8 @@
|
|||
}
|
||||
```
|
||||
|
||||
Самой форме можно дать еще больший `z-index`, чтобы она была над `DIV'ом`. Мы не помещаем форму в контейнер, чтобы она не унаследовала полупрозрачность.
|
||||
Because the `<div>` covers everything, it gets all clicks, not the page below it.
|
||||
|
||||
Also we can prevent page scroll by setting `body.style.overflowY='hidden'`.
|
||||
|
||||
The form should be not in the `<div>`, but next to it, because we don't want it to have `opacity`.
|
||||
|
|
|
@ -3,96 +3,49 @@
|
|||
|
||||
<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>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
|
||||
<body style="height:3000px">
|
||||
|
||||
<h1>Нажмите на кнопку ниже</h1>
|
||||
<h2>Click the button below</h2>
|
||||
|
||||
<input type="button" value="Нажмите для показа формы ввода" id="show-button">
|
||||
<input type="button" value="Click to show the form" 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="Отмена">
|
||||
<input type="submit" value="Ok">
|
||||
<input type="button" name="cancel" value="Cancel">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Показать полупрозрачный DIV, затеняющий всю страницу
|
||||
// (а форма будет не в нем, а рядом с ним, чтобы не полупрозрачная)
|
||||
// Show a half-transparent DIV to "shadow" the page
|
||||
// (the form is not inside, but near it, because it shouldn't be half-transparent)
|
||||
function showCover() {
|
||||
var coverDiv = document.createElement('div');
|
||||
let coverDiv = document.createElement('div');
|
||||
coverDiv.id = 'cover-div';
|
||||
document.body.appendChild(coverDiv);
|
||||
|
||||
// make the page unscrollable while the modal form is open
|
||||
document.body.style.overflowY = 'hidden';
|
||||
|
||||
document.body.append(coverDiv);
|
||||
}
|
||||
|
||||
function hideCover() {
|
||||
document.body.removeChild(document.getElementById('cover-div'));
|
||||
document.getElementById('cover-div').remove();
|
||||
document.body.style.overflowY = '';
|
||||
}
|
||||
|
||||
function showPrompt(text, callback) {
|
||||
showCover();
|
||||
var form = document.getElementById('prompt-form');
|
||||
var container = document.getElementById('prompt-form-container');
|
||||
let form = document.getElementById('prompt-form');
|
||||
let container = document.getElementById('prompt-form-container');
|
||||
document.getElementById('prompt-message').innerHTML = text;
|
||||
form.elements.text.value = '';
|
||||
form.text.value = '';
|
||||
|
||||
function complete(value) {
|
||||
hideCover();
|
||||
|
@ -102,54 +55,51 @@
|
|||
}
|
||||
|
||||
form.onsubmit = function() {
|
||||
var value = form.elements.text.value;
|
||||
if (value == '') return false; // игнорировать пустой submit
|
||||
let value = form.text.value;
|
||||
if (value == '') return false; // ignore empty submit
|
||||
|
||||
complete(value);
|
||||
return false;
|
||||
};
|
||||
|
||||
form.elements.cancel.onclick = function() {
|
||||
form.cancel.onclick = function() {
|
||||
complete(null);
|
||||
};
|
||||
|
||||
document.onkeydown = function(e) {
|
||||
if (e.keyCode == 27) { // escape
|
||||
if (e.key == 'Escape') {
|
||||
complete(null);
|
||||
}
|
||||
};
|
||||
|
||||
var lastElem = form.elements[form.elements.length - 1];
|
||||
var firstElem = form.elements[0];
|
||||
let lastElem = form.elements[form.elements.length - 1];
|
||||
let firstElem = form.elements[0];
|
||||
|
||||
lastElem.onkeydown = function(e) {
|
||||
if (e.keyCode == 9 && !e.shiftKey) {
|
||||
if (e.key == 'Tab' && !e.shiftKey) {
|
||||
firstElem.focus();
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
firstElem.onkeydown = function(e) {
|
||||
if (e.keyCode == 9 && e.shiftKey) {
|
||||
if (e.key == 'Tab' && 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);
|
||||
showPrompt("Enter something<br>...smart :)", function(value) {
|
||||
alert("You entered: " + value);
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
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://en.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;
|
||||
}
|
|
@ -3,47 +3,7 @@
|
|||
|
||||
<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>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
@ -51,15 +11,15 @@
|
|||
|
||||
<div id="prompt-form-container">
|
||||
<form id="prompt-form">
|
||||
<div id="prompt-message">Введите, пожалуйста...
|
||||
<br>Что-то..</div>
|
||||
<div id="prompt-message">Enter something...
|
||||
<br>Please..</div>
|
||||
<input name="text" type="text">
|
||||
<input type="submit" value="Ок">
|
||||
<input type="button" name="cancel" value="Отмена">
|
||||
<input type="submit" value="Ok">
|
||||
<input type="button" name="cancel" value="Cancel">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
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://en.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;
|
||||
}
|
|
@ -2,32 +2,32 @@ importance: 5
|
|||
|
||||
---
|
||||
|
||||
# Модальное диалоговое окно
|
||||
# Modal form
|
||||
|
||||
Создайте функцию `showPrompt(text, callback)`, которая выводит форму для ввода с сообщением `text` и кнопками `ОК/Отмена`.
|
||||
Create a function `showPrompt(html, callback)` that shows a form with the message `html`, an input field and buttons `OK/CANCEL`.
|
||||
|
||||
- При отправке формы (OK/ввод в текстовом поле) -- должна вызываться функция `callback` со значением поля.
|
||||
- При нажатии на `Отмена` или на клавишу `key:Esc` -- должна вызываться функция `callback(null)`. Клавиша `key:Esc` должна закрывать форму всегда, даже если поле для ввода сообщения не в фокусе.
|
||||
- A user should type something into a text field and press `key:Enter` or the OK button, then `callback(value)` is called with the value he entered.
|
||||
- Otherwise if the user presses `key:Esc` or CANCEL, then `callback(null)` is called.
|
||||
|
||||
Особенности реализации:
|
||||
In both cases that ends the input process and removes the form.
|
||||
|
||||
- Форма должна показываться в центре окна (и оставаться в центре при изменении его размеров, а также при прокрутке окна!).
|
||||
- Текст может состоять из нескольких строк, возможен любой HTML
|
||||
- При показе формы остальные элементы страницы использовать нельзя, не работают другие кнопки и т.п, это окно -- *модальное*.
|
||||
- При показе формы -- сразу фокус на `INPUT` для ввода.
|
||||
- Нажатия `key:Tab`/`key:Shift+Tab` переключают в цикле только по полям формы, они не позволяют переключиться на другие элементы страницы.
|
||||
Requirements:
|
||||
|
||||
- The form should be in the center of the window.
|
||||
- The form is *modal*. In other words, no interaction with the rest of the page is possible until the user closes it.
|
||||
- When the form is shown, the focus should be inside the `<input>` for the user.
|
||||
- Keys `key:Tab`/`key:Shift+Tab` should shift the focus between form fields, don't allow it to leave for other page elements.
|
||||
|
||||
Пример использования:
|
||||
|
||||
```js
|
||||
showPrompt("Введите что-нибудь<br>... умное :)", function(value) {
|
||||
alert( value );
|
||||
showPrompt("Enter something<br>...smart :)", function(value) {
|
||||
alert(value);
|
||||
});
|
||||
```
|
||||
|
||||
Демо в ифрейме:
|
||||
A demo in the iframe:
|
||||
|
||||
[iframe src="solution" height=160 border=1]
|
||||
|
||||
Исходный HTML/CSS для формы с готовым fixed-позиционированием - в песочнице.
|
||||
|
||||
P.S. The source document has HTML/CSS for the form with fixed positioning, but it's up to you to make it modal.
|
||||
|
|
|
@ -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)`, которая проверяет содержимое формы по клику на кнопку "Проверить".
|
||||
|
||||
Ошибки:
|
||||
|
||||
1. Одно из полей не заполнено.
|
||||
2. Пароли не совпадают.
|
||||
|
||||
Ошибка должна сопровождаться сообщением у поля. Например:
|
||||
|
||||
[iframe height=280 src="solution"]
|
||||
|
|
@ -1,56 +1,67 @@
|
|||
# Формы: отправка, событие и метод submit
|
||||
# Form submission: event and method submit
|
||||
|
||||
Событие `submit` возникает при отправке формы. Наиболее частое его применение -- это *валидация* (проверка) формы перед отправкой.
|
||||
The `submit` event triggers when the form is submitted, it is usually used to validate the form before sending it to the server or to abort the submission and process it in Javascript.
|
||||
|
||||
Метод `submit` позволяет инициировать отправку формы из JavaScript, без участия пользователя. Далее мы рассмотрим детали их использования.
|
||||
The method `form.submit()` allows to initiate form sending from JavaScript. We can use it to dynamically create and send our own forms to server.
|
||||
|
||||
Let's see more details of them.
|
||||
|
||||
[cut]
|
||||
|
||||
## Событие submit
|
||||
## Event: submit
|
||||
|
||||
Чтобы отправить форму на сервер, у посетителя есть два способа:
|
||||
There are two main ways to submit a form:
|
||||
|
||||
1. **Первый -- это нажать кнопку `<input type="submit">` или `<input type="image">`.**
|
||||
2. **Второй -- нажать Enter, находясь на каком-нибудь поле.**
|
||||
1. The first -- to click `<input type="submit">` or `<input type="image">`.
|
||||
2. The second -- press `key:Enter` on an input field.
|
||||
|
||||
Какой бы способ ни выбрал посетитель -- будет сгенерировано событие `submit`. Обработчик в нём может проверить данные и, если они неверны, то вывести ошибку и сделать `event.preventDefault()` -- тогда форма не отправится на сервер.
|
||||
Both actions lead to `submit` event on the form. The handler can check the data, and if there are errors, show them and call `event.preventDefault()`, then the form won't be sent to the server.
|
||||
|
||||
Например, в таком HTML оба способа выведут `alert`, форма не будет отправлена:
|
||||
In the form below:
|
||||
1. Go into the text field and press `key:Enter`.
|
||||
2. Click `<input type="submit">`.
|
||||
|
||||
```html autorun height=80 no-beautify
|
||||
Both actions show `alert` and the form is not sent anywhere due to `return false`:
|
||||
|
||||
```html autorun height=60 no-beautify
|
||||
<form onsubmit="alert('submit!');return false">
|
||||
Первый: Enter в текстовом поле <input type="text" value="Текст"><br>
|
||||
Второй: Нажать на "Отправить": <input type="submit" value="Отправить">
|
||||
First: Enter in the input field <input type="text" value="text"><br>
|
||||
Second: Click "submit": <input type="submit" value="Submit">
|
||||
</form>
|
||||
```
|
||||
|
||||
Ожидаемое поведение:
|
||||
````smart header="Relation between `submit` and `click`"
|
||||
When a form is sent using `key:Enter` on an input field, a `click` event triggers on the `<input type="submit">`.
|
||||
|
||||
1. Перейдите в текстовое поле и нажмите Enter, будет событие, но форма не отправится на сервер благодаря `return false` в обработчике.
|
||||
2. То же самое произойдет при клике на `<input type="submit">`.
|
||||
That's rather funny, because there was no click at all.
|
||||
|
||||
````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">
|
||||
Here's the demo:
|
||||
```html autorun height=60
|
||||
<form onsubmit="return false">
|
||||
<input type="text" size="30" value="Focus here and press enter">
|
||||
<input type="submit" value="Submit" *!*onclick="alert('click')"*/!*>
|
||||
</form>
|
||||
```
|
||||
|
||||
````
|
||||
|
||||
```warn header="В IE8- событие `submit` не всплывает"
|
||||
В IE8- событие `submit` не всплывает. Нужно вешать обработчик `submit` на сам элемент формы, без использования делегирования.
|
||||
## Method: submit
|
||||
|
||||
To submit a form to the server manually, we can call `form.submit()`.
|
||||
|
||||
Then the `submit` event is not generated. It is assumed that if the programmer calls `form.submit()`, then the script already did all related processing.
|
||||
|
||||
Sometimes that's used to manually create and send a form, like this:
|
||||
|
||||
```js run
|
||||
let form = document.createElement('form');
|
||||
form.action = 'https://google.com/search';
|
||||
form.method = 'GET';
|
||||
|
||||
form.innerHTML = '<input name="q" value="test">';
|
||||
|
||||
// the form must be in the document to submit it
|
||||
document.body.append(form);
|
||||
|
||||
form.submit();
|
||||
```
|
||||
|
||||
## Метод submit
|
||||
|
||||
Чтобы отправить форму на сервер из JavaScript -- нужно вызвать на элементе формы метод `form.submit()`.
|
||||
|
||||
При этом само событие `submit` не генерируется. Предполагается, что если программист вызывает метод `form.submit()`, то он выполнил все проверки.
|
||||
|
||||
Это используют, в частности, для искусственной генерации и отправки формы.
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue