Как скрыть раскрывающийся список, когда пользователь щелкает за его пределами
Ниже приведена упрощенная версия раскрывающегося списка ввода, с которым я работаю.
основная сводка того, что он делает: если вы сосредоточитесь на входе, появится выпадающее меню. Если щелкнуть один из параметров в раскрывающемся списке, параметр заполнит входные данные, и раскрывающийся список исчезнет. Это достигается с помощью onfocus
и функции, которые я назвал dropdown();
и undropdown();
.
Я нахожусь в дилемме, где я не могу заставить выпадающий список исчезнуть, когда кто-то щелкает в другом месте. если я использовать onblur
, он успешно скрывает раскрывающийся список, но если вы нажмете на опцию, она не заполняет вход, это потому, что onblur
функция запускается первой и, следовательно,input();
функция не запускается, потому что раскрывающийся список уже скрыт.
если вы ставите onclick
на теге body или другом родителе он рассматривает onfocus
как щелчок, где он работает, это
3 ответов
в этом случае On onblur
вы можете вызвать функцию, которая запускает undropdown(0);
после очень крошечного setTimeout почти мгновенно. Вот так:
function set() {
setTimeout(function(){
undropdown(0);
}, 100);
}
HTML-код
<input type="text" name="class" placeholder="Class" onfocus="dropdown(0)" onblur="set()" />
никаких других изменений не требуется.
function input(pos) {
var dropdown = document.getElementsByClassName('drop');
var li = dropdown[0].getElementsByTagName("li");
document.getElementsByTagName('input')[0].value = li[pos].innerHTML;
undropdown(0);
}
function dropdown(pos) {
document.getElementsByClassName('content')[pos].style.display= "block"
}
function undropdown(pos) {
document.getElementsByClassName('content')[pos].style.display= "none";
}
function set() {
setTimeout(function(){
undropdown(0);
}, 100);
}
.drop {
position: relative;
display: inline-block;
vertical-align:top;
overflow: visible;
}
.content {
display: none;
list-style-type: none;
border: 1px solid #000;
padding: 0;
margin: 0;
position: absolute;
z-index: 2;
width: 100%;
max-height: 190px;
overflow-y: scroll;
}
.content li {
padding: 12px 16px;
display: block;
margin: 0;
}
<div class="drop">
<input type="text" name="class" placeholder="Class" onfocus="dropdown(0)" onblur="set()" />
<ul class="content">
<li onclick="input(0)">Option 1</li>
<li onclick="input(1)">Option 2</li>
<li onclick="input(2)">Option 3</li>
<li onclick="input(3)">Option 4</li>
</ul>
</div>
вы можете сделать выпадающий список фокусируемым с tabindex
, а на входе blur
прослушиватель событий только скрыть раскрывающийся список, если фокус не пошел в раскрывающийся список (см. когда onblur происходит, как я могу узнать, какой элемент фокус пошел to?)
<ul class="content" tabindex="-1"></ul>
input.addEventListener('blur', function(e) {
if(!e.relatedTarget || !e.relatedTarget.classList.contains('content')) {
undropdown(0);
}
});
function input(e) {
var dropdown = document.getElementsByClassName('drop');
var li = dropdown[0].getElementsByTagName("li");
document.getElementsByTagName('input')[0].value = e.target.textContent;
undropdown(0);
}
[].forEach.call(document.getElementsByTagName('li'), function(el) {
el.addEventListener('click', input);
});
function dropdown(pos) {
document.getElementsByClassName('content')[pos].style.display = "block"
}
function undropdown(pos) {
document.getElementsByClassName('content')[pos].style.display = "none";
}
var input = document.getElementsByTagName('input')[0];
input.addEventListener('focus', function(e) {
dropdown(0);
});
input.addEventListener('blur', function(e) {
if(!e.relatedTarget || !e.relatedTarget.classList.contains('content')) {
undropdown(0);
}
});
.drop {
position: relative;
display: inline-block;
vertical-align: top;
overflow: visible;
}
.content {
display: none;
list-style-type: none;
border: 1px solid #000;
padding: 0;
margin: 0;
position: absolute;
z-index: 2;
width: 100%;
max-height: 190px;
overflow-y: scroll;
outline: none;
}
.content li {
padding: 12px 16px;
display: block;
margin: 0;
}
<div class="drop">
<input type="text" name="class" placeholder="Class" />
<ul class="content" tabindex="-1">
<li>Option 1</li>
<li>Option 2</li>
<li>Option 3</li>
<li>Option 4</li>
</ul>
</div>
принятый ответ-наивный и ненадежный подход. У меня была труднодоступная ошибка в сложном приложении, потому что я использовал setTimeout
чтобы дать ~ 200ms задержку, чтобы браузер мог обрабатывать выпадающий щелчок, прежде чем произойдет событие размытия. Хотя он отлично работал на каждой установке, с которой я его тестировал, у некоторых пользователей были проблемы, в частности у пользователей с более медленными машинами.
правильный способ-проверить relatedTarget
on focusout
событие:
input.addEventListener('focusout', function(event) {
if(!isDropdownElement(event.relatedTarget)) {
// hide dropdown
}
});
relatedTarget
на focusout
содержит ссылка на элемент, который получает фокус. Это надежно работает в каждом браузере, который я тестировал до сих пор (я не тестировал IE10 и ниже, только IE11 и Edge).