sanManjiro
@sanManjiro

Как предотвратить работу onBlur?

Функция getAddress

import FetchSuggestions from "@config/daData/dadataConfig";

export const getAddress = async (
  query: string,
  setAddress: (address: string) => void,
  param: {
    slice: number;
  },
  htmlElements?: {
    idInput: string;
    idUlList: string;
    classUl: string;
    classLi: string;
  }
): Promise<void> => {
  if (query.length < 3) {
    return;
  }
  try {
    const suggestionsList = await FetchSuggestions("address", query);
    const suggestions = suggestionsList.slice(0, param.slice);
    const inputElement = document.getElementById(`${htmlElements?.idInput}`);

    if (inputElement) {
      console.log(suggestions);

      // NOTE :Проверяем, существует ли уже элемент <ul>, если <ul> не существует, создаем его
      let ul = document.getElementById(
        htmlElements?.idUlList ?? ""
      ) as HTMLUListElement;
      if (!ul) {
        ul = document.createElement("ul");
        ul.id = htmlElements?.idUlList ?? "";
        ul.className = htmlElements?.classUl ?? "";
        inputElement.parentNode?.insertBefore(ul, inputElement.nextSibling);
      } else {
        ul.innerHTML = "";
      }

      // NOTE: Добавляем список адресов в <li>
      let statusClick = false;
      if (suggestions.length > 0) {
        suggestions.forEach((suggestion: { value: string }) => {
          const li = document.createElement("li");
          li.textContent = suggestion.value;
          li.addEventListener("click", () => {
            // NOTE: Обработчик события клика
            statusClick = true;
            setAddress(suggestion.value);
            ul.innerHTML = "";
            ul.remove();
          });

          ul.appendChild(li);
        });
      }

      // NOTE: Добавляем обработку нажатий клавиш
      let currentIndex = -1;
      inputElement.addEventListener("keydown", (event) => {
        const items = ul.querySelectorAll("li");
        if (event.key === "ArrowDown") {
          currentIndex = (currentIndex + 1) % items.length;
          highlightItem(items, currentIndex, htmlElements?.classLi ?? "");
          event.preventDefault();
        } else if (event.key === "ArrowUp") {
          currentIndex = (currentIndex - 1 + items.length) % items.length;
          highlightItem(items, currentIndex, htmlElements?.classLi ?? "");
          event.preventDefault();
        } else if (event.key === "Enter") {
          if (currentIndex >= 0 && currentIndex < items.length) {
            setAddress(items[currentIndex].textContent || "");
            ul.innerHTML = ""; // Очищаем список после выбора
            currentIndex = -1;
            ul.remove(); // Удаляем <ul> из DOM после выбора
          }
        }
      });

      // NOTE: Очистка списка при потере фокуса
      inputElement.addEventListener("blur", () => {
        setTimeout(() => {
          console.log("before", statusClick);
          if (!statusClick && suggestions.length > 0) {
            setAddress(suggestions[0].value);
          }
          statusClick = false;
          console.log("after", statusClick);
          ul.innerHTML = "";
          ul.remove();
        }, 100);
      });

      // NOTE: Удаление ul, если значение <= 3 символов или массив данных пуст
      if (query.length <= 3 || suggestions.length === 0) {
        const ul = document.getElementById(htmlElements?.idUlList ?? "");
        if (ul) {
          ul.remove();
        }
        return;
      }
    }
  } catch (error) {
    console.error("Error fetching address suggestions:", error);
    return;
  }
};

// NOTE: Функция для выделения текущего элемента в списке
function highlightItem(
  items: NodeListOf<HTMLElement>,
  index: number,
  highlight: string
) {
  items.forEach((item, i) => {
    item.classList.remove(highlight);
    if (i === index) {
      item.classList.add(highlight);
    }
  });
}


Проблема в том, что statusClick срабатывает только один раз, и если попробовать второй раз вызвать форму и нажать по элемент в списке, то визуально можно увидеть что он значение резко выбирает, а потом заменяет его на setAddress(suggestions[0].value).

Функция вызывается в 2-х местах:
FormInputText
<div className={`${style["pass-details__form-address"]}`}>
        <FormInputText<IDataFormPassport>
          controller={{
            control: control,
            name: "legalAddress",
          }}
          customClassName="pass-details__form-address-input"
          value={dataFormPassport.legalAddress}
          inputId="legalAddress"
          placeholder=" Адрес регистрации (Область / Город/ Улица / Дом)"
          minLength={2}
          maxLength={100}
          pattern={/^[^A-Za-z]+$/gi}
          patternValid={/^[^A-Za-z]+$/gi}
          error={errors}
          onChange={(val) => handleLegalAddressChange(val)}
          onClick={() =>
            getAddress(
              dataFormPassport.legalAddress,
              (address) => updateDataForm({ legalAddress: address }),
              { slice: 10 },
              {
                idInput: "legalAddress",
                idUlList: "legalAddressList",
                classUl: style["pass-details__form-address-list"],
                classLi: style["pass-details__form-address-item"],
              }
            )
          }
        />
      </div>

handleLegalAddressChange
const handleLegalAddressChange = (value: string) => {
    updateDataForm({ legalAddress: value });
    getAddress(
      value,
      (address) => updateDataForm({ legalAddress: address }),
      { slice: 10 },
      {
        idInput: "legalAddress",
        idUlList: "legalAddressList",
        classUl: style["pass-details__form-address-list"],
        classLi: style["pass-details__form-address-item"],
      }
    );
  };


Ещё я заметил что при повторном вызове функции он начинает все больше раз вызывать getAddress:
673d0a1846da1300016988.jpeg
  • Вопрос задан
  • 68 просмотров
Пригласить эксперта
Ответы на вопрос 1
neuotq
@neuotq
Прокрастинация
Функция конечно перегружена, я бы её немного декомпозировал(но ладно, отдельная проблема).
Как минимум я вижу что не убираются слушатели событий, то точно нужны(перед добавлением)
те, возможно баг как раз в этом:
inputElement.removeEventListener("keydown", ...);
 inputElement.removeEventListener("blur", ...);

И там лучше передать созданные функции типа handleKeydown/handleBlur

И еще нормально выделить гарды на проверку данных, те что
if (query.length < 3) {
    return;
  }
и другие
например с инпутом сразу:
if (!inputElement) return;
Короче пройтись, чтобы легче читалось и понимать что происходит.
Точнее сказать сразу сложно, без живой отладки. Если выгрузите где-то, будет понятнее.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы