init
This commit is contained in:
parent
06f61d8ce8
commit
f301cb744d
2271 changed files with 103162 additions and 0 deletions
|
@ -0,0 +1,9 @@
|
|||
В этом решении для закрытия селекта по клику вне него используется отслеживание произвольных кликов вне документа.
|
||||
|
||||
Альтернатива -- события `focusin/focusout`, т.е. считаем, что пока фокус в селекте -- он открыт. С одной стороны, это более мощный способ, он позволяет перемещаться по элементам управления при помощи [key Tab] и корректно обрабатывать уход при помощи клавиатуры.
|
||||
|
||||
С другой стороны, это решение не универсально: если выводится `alert`, то фокус "прыгает" в него, уходя с элемента, а потом возвращается обратно. При этом JavaScript зарегистрирует уход фокуса `focusout` и возвращение `focusin`, хотя по смыслу фокус с элемента никуда не уходил, просто был `alert`.
|
||||
|
||||
Побочный эффект -- к закрытию и раскрытию (лишнему) элемента управления при таких "ненамеренных" потерях фокуса. Поэтому был выбран `onclick`.
|
||||
|
||||
Решение: [edit src="solution"]Открыть в песочнице[/edit]
|
1
02-ui/05-widgets/05-custom-events/03-custom-select/solution/.plnkr
Executable file
1
02-ui/05-widgets/05-custom-events/03-custom-select/solution/.plnkr
Executable file
|
@ -0,0 +1 @@
|
|||
{"name":"customselect","plunk":"NSMq0hilrebQaa5zU0w6"}
|
47
02-ui/05-widgets/05-custom-events/03-custom-select/solution/customselect.css
Executable file
47
02-ui/05-widgets/05-custom-events/03-custom-select/solution/customselect.css
Executable file
|
@ -0,0 +1,47 @@
|
|||
.customselect {
|
||||
width: 200px;
|
||||
font-size: 14px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.customselect-title {
|
||||
height: 17px;
|
||||
border: 1px solid #ADD8E6;
|
||||
background-position: right;
|
||||
background-image: url(http://js.cx/clipart/select-button.gif);
|
||||
background-repeat: no-repeat;
|
||||
padding: 2px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.customselect-options li {
|
||||
padding: 2px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.customselect-options li:nth-child(even) {
|
||||
background-color: #f0f8ff;
|
||||
}
|
||||
|
||||
.customselect-options li:hover {
|
||||
background-color: #7fffd4;
|
||||
}
|
||||
|
||||
.customselect-options {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: none;
|
||||
|
||||
position: absolute;
|
||||
z-index: 1000;
|
||||
background: white;
|
||||
width: 200px;
|
||||
border-bottom: 1px solid #add8e6;
|
||||
border-left: 1px solid #add8e6;
|
||||
border-right: 1px solid #add8e6;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.customselect-open .customselect-options {
|
||||
display: block;
|
||||
}
|
61
02-ui/05-widgets/05-custom-events/03-custom-select/solution/customselect.js
Executable file
61
02-ui/05-widgets/05-custom-events/03-custom-select/solution/customselect.js
Executable file
|
@ -0,0 +1,61 @@
|
|||
function CustomSelect(options) {
|
||||
var self = this;
|
||||
|
||||
var elem = options.elem;
|
||||
|
||||
elem.on('click', '.customselect-title', onTitleClick);
|
||||
elem.on('click', 'li', onOptionClick);
|
||||
|
||||
var isOpen = false;
|
||||
|
||||
// ------ обработчики ------
|
||||
|
||||
function onTitleClick(event) {
|
||||
toggle();
|
||||
}
|
||||
|
||||
// закрыть селект, если клик вне его
|
||||
function onDocumentClick(event) {
|
||||
var isInside = $(event.target).closest(elem).length;
|
||||
if (!isInside) close();
|
||||
}
|
||||
|
||||
function onOptionClick(event) {
|
||||
close();
|
||||
|
||||
var name = $(event.target).html();
|
||||
var value = $(event.target).data('value');
|
||||
|
||||
setValue(name, value);
|
||||
}
|
||||
|
||||
// ------------------------
|
||||
|
||||
function setValue(name, value) {
|
||||
elem.find('.customselect-title').html(name);
|
||||
|
||||
$(self).triggerHandler({
|
||||
type: 'select',
|
||||
name: name,
|
||||
value: value
|
||||
});
|
||||
}
|
||||
|
||||
function toggle() {
|
||||
if (isOpen) close()
|
||||
else open();
|
||||
}
|
||||
|
||||
function open() {
|
||||
elem.addClass('customselect-open');
|
||||
$(document).on('click', onDocumentClick);
|
||||
isOpen = true;
|
||||
}
|
||||
|
||||
function close() {
|
||||
elem.removeClass('customselect-open');
|
||||
$(document).off('click', onDocumentClick);
|
||||
isOpen = false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
{"name":"customselect","plunk":"NSMq0hilrebQaa5zU0w6"}
|
|
@ -0,0 +1,47 @@
|
|||
.customselect {
|
||||
width: 200px;
|
||||
font-size: 14px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.customselect-title {
|
||||
height: 17px;
|
||||
border: 1px solid #ADD8E6;
|
||||
background-position: right;
|
||||
background-image: url(http://js.cx/clipart/select-button.gif);
|
||||
background-repeat: no-repeat;
|
||||
padding: 2px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.customselect-options li {
|
||||
padding: 2px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.customselect-options li:nth-child(even) {
|
||||
background-color: #f0f8ff;
|
||||
}
|
||||
|
||||
.customselect-options li:hover {
|
||||
background-color: #7fffd4;
|
||||
}
|
||||
|
||||
.customselect-options {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: none;
|
||||
|
||||
position: absolute;
|
||||
z-index: 1000;
|
||||
background: white;
|
||||
width: 200px;
|
||||
border-bottom: 1px solid #add8e6;
|
||||
border-left: 1px solid #add8e6;
|
||||
border-right: 1px solid #add8e6;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.customselect-open .customselect-options {
|
||||
display: block;
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
function CustomSelect(options) {
|
||||
var self = this;
|
||||
|
||||
var elem = options.elem;
|
||||
|
||||
elem.on('click', '.customselect-title', onTitleClick);
|
||||
elem.on('click', 'li', onOptionClick);
|
||||
|
||||
var isOpen = false;
|
||||
|
||||
// ------ обработчики ------
|
||||
|
||||
function onTitleClick(event) {
|
||||
toggle();
|
||||
}
|
||||
|
||||
// закрыть селект, если клик вне его
|
||||
function onDocumentClick(event) {
|
||||
var isInside = $(event.target).closest(elem).length;
|
||||
if (!isInside) close();
|
||||
}
|
||||
|
||||
function onOptionClick(event) {
|
||||
close();
|
||||
|
||||
var name = $(event.target).html();
|
||||
var value = $(event.target).data('value');
|
||||
|
||||
setValue(name, value);
|
||||
}
|
||||
|
||||
// ------------------------
|
||||
|
||||
function setValue(name, value) {
|
||||
elem.find('.customselect-title').html(name);
|
||||
|
||||
$(self).triggerHandler({
|
||||
type: 'select',
|
||||
name: name,
|
||||
value: value
|
||||
});
|
||||
}
|
||||
|
||||
function toggle() {
|
||||
if (isOpen) close()
|
||||
else open();
|
||||
}
|
||||
|
||||
function open() {
|
||||
elem.addClass('customselect-open');
|
||||
$(document).on('click', onDocumentClick);
|
||||
isOpen = true;
|
||||
}
|
||||
|
||||
function close() {
|
||||
elem.removeClass('customselect-open');
|
||||
$(document).off('click', onDocumentClick);
|
||||
isOpen = false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Селект</title>
|
||||
<script src='http://code.jquery.com/jquery.min.js'></script>
|
||||
<link rel="stylesheet" href="customselect.css"/>
|
||||
<script src="customselect.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div>Последний результат: <span id="result">...</span></div>
|
||||
|
||||
<div id="animal-select" class="customselect">
|
||||
<div class="customselect-title">Выберите</div>
|
||||
<ol class="customselect-options">
|
||||
<!-- значение хранится в свойстве data-value -->
|
||||
<li data-value="bird">Птицы</li>
|
||||
<li data-value="fish">Рыбы</li>
|
||||
<li data-value="animal">Звери</li>
|
||||
<li data-value="dino">Динозавры</li>
|
||||
<li data-value="simplest">Одноклеточные</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<div id="food-select" class="customselect">
|
||||
<div class="customselect-title">Выберите</div>
|
||||
<ol class="customselect-options">
|
||||
<li data-value="carnivore">Плотоядные</li>
|
||||
<li data-value="herbivore">Травоядные</li>
|
||||
<li data-value="omnivore">Всеядные</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
var animalSelect = new CustomSelect({
|
||||
elem: $('#animal-select')
|
||||
});
|
||||
|
||||
var foodSelect = new CustomSelect({
|
||||
elem: $('#food-select')
|
||||
});
|
||||
|
||||
$([animalSelect, foodSelect]).on('select', function (event) {
|
||||
$('#result').html(event.value)
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
51
02-ui/05-widgets/05-custom-events/03-custom-select/solution/index.html
Executable file
51
02-ui/05-widgets/05-custom-events/03-custom-select/solution/index.html
Executable file
|
@ -0,0 +1,51 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Селект</title>
|
||||
<script src='http://code.jquery.com/jquery.min.js'></script>
|
||||
<link rel="stylesheet" href="customselect.css"/>
|
||||
<script src="customselect.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div>Последний результат: <span id="result">...</span></div>
|
||||
|
||||
<div id="animal-select" class="customselect">
|
||||
<div class="customselect-title">Выберите</div>
|
||||
<ol class="customselect-options">
|
||||
<!-- значение хранится в свойстве data-value -->
|
||||
<li data-value="bird">Птицы</li>
|
||||
<li data-value="fish">Рыбы</li>
|
||||
<li data-value="animal">Звери</li>
|
||||
<li data-value="dino">Динозавры</li>
|
||||
<li data-value="simplest">Одноклеточные</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<div id="food-select" class="customselect">
|
||||
<div class="customselect-title">Выберите</div>
|
||||
<ol class="customselect-options">
|
||||
<li data-value="carnivore">Плотоядные</li>
|
||||
<li data-value="herbivore">Травоядные</li>
|
||||
<li data-value="omnivore">Всеядные</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
var animalSelect = new CustomSelect({
|
||||
elem: $('#animal-select')
|
||||
});
|
||||
|
||||
var foodSelect = new CustomSelect({
|
||||
elem: $('#food-select')
|
||||
});
|
||||
|
||||
$([animalSelect, foodSelect]).on('select', function (event) {
|
||||
$('#result').html(event.value)
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
20
02-ui/05-widgets/05-custom-events/03-custom-select/task.md
Normal file
20
02-ui/05-widgets/05-custom-events/03-custom-select/task.md
Normal file
|
@ -0,0 +1,20 @@
|
|||
# Свой селект
|
||||
|
||||
[importance 5]
|
||||
|
||||
Напишите свой, самостоятельно оформленный, селект.
|
||||
|
||||
Требования:
|
||||
<ul>
|
||||
<li>Открытие и закрытие по клику на заголовок.</li>
|
||||
<li>Закрытие селекта происходит при выборе или клике на любое другое место документа, в том числе на другой аналогичный селект.</li>
|
||||
<li>Событие `"select"` при выборе опции.</li>
|
||||
<li>Значение опции хранится в атрибуте `data-value` (HTML-структура есть в исходном документе).
|
||||
</ul>
|
||||
Например:
|
||||
|
||||
[iframe src="solution" border="1" height="200" newwin]
|
||||
|
||||
В примере выше два селекта, чтобы можно было проверить процесс открытия-закрытия.
|
||||
|
||||
[edit src="task" task/]
|
1
02-ui/05-widgets/05-custom-events/03-custom-select/task/.plnkr
Executable file
1
02-ui/05-widgets/05-custom-events/03-custom-select/task/.plnkr
Executable file
|
@ -0,0 +1 @@
|
|||
{"name":"customselect-src","plunk":"cCX1mrKxjKiSibESyEaZ"}
|
55
02-ui/05-widgets/05-custom-events/03-custom-select/task/index.html
Executable file
55
02-ui/05-widgets/05-custom-events/03-custom-select/task/index.html
Executable file
|
@ -0,0 +1,55 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Селект</title>
|
||||
<script src='http://code.jquery.com/jquery.min.js'></script>
|
||||
<link rel="stylesheet" href="customselect.css"/>
|
||||
<script src="customselect.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div>Последний результат: <span id="result">...</span></div>
|
||||
|
||||
<img src="http://js.cx/clipart/select-button.gif">
|
||||
|
||||
<div id="animal-select" class="customselect">
|
||||
<div class="customselect-title">Выберите</div>
|
||||
<ol class="customselect-options">
|
||||
<!-- значение хранится в свойстве data-value -->
|
||||
<li data-value="bird">Птицы</li>
|
||||
<li data-value="fish">Рыбы</li>
|
||||
<li data-value="animal">Звери</li>
|
||||
<li data-value="dino">Динозавры</li>
|
||||
<li data-value="simplest">Одноклеточные</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<div id="food-select" class="customselect">
|
||||
<div class="customselect-title">Выберите</div>
|
||||
<ol class="customselect-options">
|
||||
<li data-value="carnivore">Плотоядные</li>
|
||||
<li data-value="herbivore">Травоядные</li>
|
||||
<li data-value="omnivore">Всеядные</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
// создаём два селекта
|
||||
var animalSelect = new CustomSelect({
|
||||
elem: $('#animal-select')
|
||||
});
|
||||
|
||||
var foodSelect = new CustomSelect({
|
||||
elem: $('#food-select')
|
||||
});
|
||||
|
||||
// выводим выбранное значение
|
||||
$([animalSelect, foodSelect]).on('select', function (event) {
|
||||
$('#result').html(event.value)
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
Loading…
Add table
Add a link
Reference in a new issue