Ниже приведена упрощенная версия раскрывающегося списка ввода, с которым я работаю.

Основная сводка того, что он делает: если вы сосредоточитесь на вводе, появится выпадающий список. Если щелкнуть один из параметров в раскрывающемся списке, этот параметр заполняет ввод, и раскрывающийся список исчезает. Это достигается с помощью onfocus и функций, которые я назвал dropdown(); и undropdown();.

У меня дилемма, когда я не могу заставить раскрывающийся список исчезнуть, когда кто-то щелкает в другом месте. Если я использую onblur, он успешно скрывает раскрывающийся список, но если вы нажимаете на опцию, он не заполняет ввод, это потому, что сначала запускается функция onblur и, следовательно, {{ X2}} функция не запускается, потому что раскрывающийся список уже скрыт.

Если вы поместите onclick в тег body или другого родителя, он будет рассматривать onfocus как щелчок, где он запускает функцию dropdown();, а затем функцию undropdown(); сразу раскрывающийся список никогда не появляется, поскольку функции перекрываются.

Я был бы признателен за помощь в выяснении, как упорядочить функции так, чтобы они выполнялись в правильном порядке, не перекрывая друг друга.

JSFiddle доступен здесь.

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";
}
.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)"/>
  <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>

PS: В дополнение к вышеупомянутой проблеме, я был бы признателен за внесение изменений, чтобы получить лучший заголовок для этого вопроса, чтобы кто-нибудь, столкнувшийся с подобной проблемой, мог найти его более легко.

7
user7267879 8 Дек 2016 в 16:58

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>
4
Nikhil Nanjappa 8 Дек 2016 в 14:38

Принятый ответ - наивный и ненадежный подход. У меня была сложная ошибка в сложном приложении, потому что я использовал setTimeout для задержки ~ 200 мс, чтобы браузер мог обработать выпадающий щелчок, прежде чем произойдет событие размытия. Хотя он отлично работал на каждой установке, с которой я тестировал, некоторые пользователи имели проблемы, в частности, пользователи с более медленными машинами.

Правильный способ - проверить relatedTarget на событии focusout:

input.addEventListener('focusout', function(event) {
  if(!isDropdownElement(event.relatedTarget)) {
    // hide dropdown
  }
});

relatedTarget для focusout содержит элемент элемент ссылка, которая получает фокус. Это надежно работает во всех протестированных мною браузерах (я не тестировал IE10 и ниже, только IE11 и Edge).

0
Dmitriy 17 Сен 2018 в 09:44

Вы можете сделать раскрывающийся список фокусируемым с помощью tabindex, а в обработчике событий blur входного параметра скрыть раскрывающийся список, только если фокус не перешел на раскрывающийся список (см. Когда происходит onblur, как я могу узнать, какой фокус элемента перешел на ? )

<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>
3
Community 23 Май 2017 в 11:52