Сколько времени это займет, пока не активируется событие клика? - PullRequest
1 голос
/ 27 марта 2019

Я пытаюсь сделать поле выбора, записи которого открываются после щелчка в поле ввода. После выбора одного из пунктов, раскрывающийся список должен быть снова закрыт. Я хочу, чтобы открыть / закрыть часть раскрывающегося списка без использования 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 для назначения событий щелчка выпадающим значениям:

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 мс, у меня снова та же проблема. Похоже, что событие нажатия срабатывает «очень» поздно.

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

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

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

Ответы [ 2 ]

1 голос
/ 27 марта 2019

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

Я дал каждому result item табличный индекс "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 устанавливается до того, как событие click будет запущено в элементе результата. Порядок событий должен быть (исходя из моих наблюдений):

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

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

1 голос
/ 27 марта 2019

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

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

Рабочая демоверсия >> ЗДЕСЬ << </a>.

Шаг 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 и 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 s внутри оболочки #results), мы должны следить за щелчком на всей странице (document элемент) и ответьте на действие следующим образом:

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...