Сколько времени требуется для запуска события click?

#javascript #html #css

#javascript #HTML #css

Вопрос:

Я пытаюсь создать поле выбора, записи в котором открываются после нажатия на поле ввода. После выбора одного из элементов выпадающий список должен быть снова закрыт. Я хочу открыть / закрыть часть выпадающего списка без использования javascript.

HTML выглядит следующим образом:

 <div id="outer">
  <input type="text" id="input">
  <div id="results">
    <div>Test 1 </div>
    <div>Test 2 </div>
    <div>Test 3 </div>
    <div>Test 4 </div>    
  </div>
</div>
<div id="label">
</div>
  

После нажатия на элемент выбранное значение должно появиться под #outer div (просто для демонстрации).
Javascript для присвоения событий click значениям выпадающего списка:

 document.querySelectorAll("#results div").forEach(setClick);
function setClick(node) {
     node.addEventListener("click", setText.bind(null, node.innerHTML))
}
function setText(t) {
    document.getElementById("label").innerHTML = t;
}
  

Теперь я покажу вам свой первый черновик css-кода:

 #outer {
  width: 200px;
  position: relative;
}
#input {
  width: 100%;
}
#results {
    position: absolute;
    width: 100%;
    display: block;
    visibility: hidden;
    background-color: white;
}
#results > div:hover {
  background-color: lightblue;
  cursor: pointer;
}
#outer:focus-within #results, #results:hover {
  visibility: visible;
}
  

Это работает как шарм, но в какой-то момент дает сбой:
После нажатия на элемент выпадающий список не закрывается. Это из-за #results:hover селектора, который необходим для сохранения выпадающего списка открытым после нажатия на элемент. Щелчок выводит фокус из поля ввода, таким образом, focus-within селектор больше не применяется. Поскольку фокус удаляется из входных данных до того, как произойдет щелчок, выпадающий список скрывается, когда в документе появляется последний щелчок (это мое понимание проблемы).
Таким образом, я использую hover селектор, который заставляет div оставаться открытым до тех пор, пока мышь находится над div.

Вы можете протестировать это здесь: https://jsfiddle.net/hcetz1og/3 /

Моим решением для этого был переход, который скрывает выпадающий список после того, как фокус был убран:

 #outer:not(:focus-within) #results:hover {
  visibility: hidden;
  transition-property: visibility;
  /*use 10 ms and the clicked value in the drop down won't be shown */
  transition-delay: 100ms;
  transition-timing-function: step-end;
}
  

Это работает на моем компьютере, когда я использую 100 мс в качестве задержки. Если я использую 10 мс, у меня снова возникает та же проблема. Кажется, что событие click запускается «очень» поздно.

Не стесняйтесь протестировать его здесь: https://jsfiddle.net/hcetz1og/2

Вопрос: Сколько времени потребуется, пока событие click не поступит в документ? Есть ли фиксированный промежуток времени, который я должен ждать, или задержка может зависеть от каждой машины? Если это так, я вынужден не использовать обычный CSS, но должен использовать javascript для этого, я думаю.

Редактировать: Не стесняйтесь публиковать альтернативное решение, используя обычный css. Но, пожалуйста, имейте в виду, что я в основном хочу сосредоточиться на получении ответа на этот вопрос, а не на альтернативных решениях.

Комментарии:

1. я не думаю, что это задержка после щелчка, скорее вам нужно вывести курсор мыши после щелчка??? или мне так кажется

2. @PaddyHallihan Да, мне нужно щелкнуть мышью, чтобы заставить hover селектор больше не применяться. Для этого я представил второе решение. Используя переход с step-end , я гарантирую, что выпадающий список «перемещается» из положения мыши пользователя, заставляя hover быть отключенным. Но когда это происходит слишком рано, щелчок, похоже, не затрагивает элементы результата, в противном случае значение элемента должно отображаться под outer div. Я не уверен в точном процессе здесь. Это всего лишь мое предположение. Может быть, кто-то знает подробно, что здесь происходит.

3. Время для запуска события click должно быть действительно коротким. Невозможно точно сказать, насколько короткое, потому что это зависит от вычислительной мощности, браузера и других факторов. Поэтому дизайн, который зависит от таймингов, подобных этому, на мой взгляд, плох, и его следует избегать, если это возможно. Это похоже на запрос о проблемах. Решение на javascript, основанное на обработчиках событий, было бы намного надежнее и проще в реализации. Не уверен, что это можно выполнить с помощью css.

Ответ №1:

Как сказал @Mark Baijens в комментариях, использование тайм-аутов — плохая практика, поэтому вот довольно чистое решение. Я использовал JavaScript для отображения выпадающего списка, а не CSS, потому что ваша проблема возникает из-за CSS.

Я не знаю, почему вы хотите установить innerHTML , а не какое-либо другое свойство, как style.visibility , например. Для меня это просто не имеет смысла, поэтому, имея это в виду, давайте возьмемся за это 🙂

Рабочая демонстрация >> ЗДЕСЬ <<.

Шаг 1 — удалите #outer...:hover части CSS

Итак, вы остаетесь с этим:

 #outer {
  width: 200px;
  position: relative;
}
#input {
  width: 100%;
}
#results {
    position: absolute;
    width: 100%;
    display: block;
    visibility: hidden;
    background-color: white;
}
#results > div:hover {
  background-color: lightblue;
  cursor: pointer;
}
  

Шаг 2 — добавьте onfocus событие в input поле

Просто назначьте вызов функции onfocus атрибуту ввода. Все остальное в HTML остается неизменным.

 <div id="outer">
  <input type="text" id="input" onfocus="showElements()">
  <div id="results">
    <div>Test 1 </div>
    <div>Test 2 </div>
    <div>Test 3 </div>
    <div>Test 4 </div>    
  </div>
</div>
<div id="label">
</div>
  

Шаг 3 — создайте функцию showElements and hideElements :

 function showElements() {
  document.getElementById("results").style.visibility = 'visible';
}
function hideElements() {
  document.getElementById("results").style.visibility = 'hidden';
}
  

Шаг 4 — вызываем hideElements() при нажатии вне input элемента

Существует два случая щелчка вне input элемента:

  • Случай 1 — мы нажали на один из div файлов внутри #results оболочки
  • Случай 2 — щелчок вне поля ввода, но не на одном из div полей внутри #results оболочки

В первом случае мы изменим назначение onclick обработчика следующим образом:

 document.querySelectorAll("#results div").forEach(setClick);
function setClick(node) {
  node.addEventListener("click", setTextAndHideElements.bind(null, node.innerHTML));
}
  

Итак, setText функция теперь становится setTextAndHideElements и выглядит следующим образом:

 function setTextAndHideElements(t) {
  document.getElementById("label").innerHTML = t;
  hideElements();
}
  

Во втором случае (щелчок за пределами поля ввода, но не на одном из div элементов внутри #results оболочки) мы должны следить за щелчком на всей странице ( document элементе) и реагировать на действие следующим образом:

 document.onclick = function(e) {
  if (e.target.id !== 'input'){
    hideElements();
  }
}
  

Примечание: это переопределит все ранее назначенные onclick события, назначенные document элементу.

Как упоминалось в начале, рабочая демонстрация находится >> ЗДЕСЬ (codepen.io ) <<.

Комментарии:

1. Как уже упоминалось, это был лишь минимальный рабочий пример. Замените innerHTML на что-нибудь, что вам больше нравится. Спасибо за ваше расследование :-). Конечно, я знаю некоторые решения Javascript (но я не думал об использовании onfocus event для этого. Кажется разумным предпочесть это чистому решению на основе onclick). На мой взгляд, решение CSS было бы более чистым. Это было причиной для попытки этого. К сожалению, порядок событий вынуждает меня использовать этот вид таймаута.

2. Использование onfocus or :focus-within в CSS имеет дело с focus элементом. Разница заключается в удобочитаемости и качестве решения. Если вы в конечном итоге используете мой код (или большую его часть), пожалуйста, примите этот ответ. 🙂

Ответ №2:

Я попробовал другое решение, которое не требует настройки дополнительных событий JS. Смотрите: https://jsfiddle.net/hcetz1og/4 /

Я присвоил каждому result item tabindex значение «0», чтобы убедиться, что эти элементы могут быть сфокусированы. Затем я удалил #outer:not() часть из css и заменил hover селектор этим: #results:focus-within . Дополнительные, которые я вызывал node.blur() на узле после нажатия на них.

Резюме: Изменение в HTML:

 <div tabindex="0">Test 1 </div>
  

Изменения в JS:

 function setText(t, node) {
    document.getElementById("label").innerHTML = t;
  node.blur();
}
  

Изменение в CSS:

 #outer:focus-within #results, #results:focus-within {
  visibility: visible;
}
  

Что вы думаете об этом? Должно быть стабильным, я думаю, потому что фокус на #results div устанавливается до того, как click событие запускается для элемента результата.
Порядок событий должен быть (на основе моего наблюдения):

input focus -> input blur -> item focus -> item click

Не уверен, что переход между размытием и фокусировкой может привести к видимой проблеме. Теоретически, div результатов должен быть скрыт и показан снова через очень небольшой промежуток времени. Но я исследовал это с помощью временной шкалы производительности Chrome и не распознал новый рендеринг между обоими событиями. Можно видеть, что результирующий элемент сфокусирован (на нем установлен контур), а затем он исчезает, как и ожидалось.

Комментарии:

1. Это сработает, но, я думаю, это намного сложнее для понимания. Не следует писать код, который трудно читать и понимать, вот почему я думаю, что решение JS подходит лучше. CSS должен обрабатывать оформление элементов, но логика того, как что-то работает, должна обрабатываться JS. Это всего лишь мое мнение.

2. @Casper это хороший момент! Я думаю, вы правы. Если другой разработчик заглянет в мой код, будет непонятно, как это работает. Спасибо, что указали на это.

3. Я рад, что помог 🙂