Javascript focus()在移动浏览器中不适用于刚从顶部进入窗口的元素,即React功能组件

Javascript focus()在移动浏览器中不适用于刚从顶部进入窗口的元素,即React功能组件,javascript,css,reactjs,mobile,focus,Javascript,Css,Reactjs,Mobile,Focus,我在React中创建了一个搜索栏下拉列表,作为导航栏的一部分。搜索栏的起始位置在窗口上方,顶部:-112px。单击导航上的搜索按钮时,搜索栏动画开始,下拉列表从顶部:-112px到顶部:0。动画完成且“Showed”类应用于搜索栏容器后,监视displayClass值的useEffect将触发并聚焦于搜索栏输入字段 这在桌面Chrome和Safari上运行良好,但在移动设备上,两种浏览器的输入字段都不会在视图中聚焦一次。我试过在focus通话中设置各种长度的超时,但都没有用。任何帮助都将不胜感激

我在React中创建了一个搜索栏下拉列表,作为导航栏的一部分。搜索栏的起始位置在窗口上方,顶部:-112px。单击导航上的搜索按钮时,搜索栏动画开始,下拉列表从顶部:-112px到顶部:0。动画完成且“Showed”类应用于搜索栏容器后,监视displayClass值的useEffect将触发并聚焦于搜索栏输入字段

这在桌面Chrome和Safari上运行良好,但在移动设备上,两种浏览器的输入字段都不会在视图中聚焦一次。我试过在focus通话中设置各种长度的超时,但都没有用。任何帮助都将不胜感激

搜索栏组件:

export const SearchBar = ({
  analyticsSearchBarSearchInput,
  display,
  history,
  onClose,
}) => {
  const [displayClass, setDisplayClass] = useState("");
  const [searchActive, setSearchActive] = useState(false);

  const prevDisplayRef = useRef();
  useEffect(() => {
    prevDisplayRef.current = display;
  });
  const prevDisplay = prevDisplayRef.current;

  useEffect(() => {
    if (!prevDisplay && display) {
      onSearchOpen();
    } else if (prevDisplay && !display) {
      onSearchClose();
    }
  }, [display, prevDisplay]);

  useEffect(() => {
    if (displayClass === "shown") {
      document.getElementById("search-bar-input").focus({
        preventScroll: true,
      });
    }
  }, [displayClass]);

  return (
    <ConditionalWrapper
      condition={displayClass === "shown"}
      wrapper={(children) => (
        <OutsideClick onClick={onClose}>{children}</OutsideClick>
      )}
    >
      <div className={`search-bar__container ${displayClass}`}>
        <div className="search-bar__left">
          <div className={`search-bar__search-icon${getSearchIconClass()}`} />
          <div className="search-bar__input">
            <SearchForm
              onSubmit={onSearchSubmit}
              searchActive={searchActive}
              setSearchActive={setSearchActive}
            />
          </div>
        </div>
        <div className="search-bar__right">
          <div
            aria-label="Close search"
            className="search-bar__close-icon"
            onClick={onClose}
            onKeyDown={onClose}
            role="button"
            tabIndex={0}
          />
        </div>
      </div>
    </ConditionalWrapper>
  );

  function getSearchIconClass() {
    return searchActive ? " active" : "";
  }

  function onSearchClose() {
    setDisplayClass("hide");
    setTimeout(() => {
      setDisplayClass("");
    }, 300);

    enableBodyScroll();
    document.body.removeEventListener("touchmove", touchMoveCallback, {
      passive: false,
    });
  }

  function onSearchOpen() {
    setDisplayClass("show");
    setTimeout(() => {
      setDisplayClass("shown");
    }, 200);

    disableBodyScroll();
    document.body.addEventListener("touchmove", touchMoveCallback, {
      passive: false,
    });
  }

  function onSearchSubmit(usersInput) {
    const hostUrl = window.location.host;
    const pageNumber = 1;
    const searchUrl = `${hostUrl}/search?query=${usersInput}&page=${pageNumber}`;
    if (displayClass === "shown" && usersInput !== "") {
      Promise.resolve(
        analyticsSearchBarSearchInput(hostUrl, usersInput, searchUrl)
      ).then(() => {
        onClose();
        history.push(`/search?query=${usersInput}&page=${pageNumber}`);
      });
    }
  }
};

const ConditionalWrapper = ({ condition, wrapper, children }) =>
  condition ? wrapper(children) : children;

const touchMoveCallback = (e) => {
  e.preventDefault();
};
export const SearchForm = ({ onSubmit, searchActive, setSearchActive }) => {
  const [search, setSearch] = useState("");

  useEffect(() => {
    search !== "" ? setSearchActive(true) : setSearchActive(false);
  }, [search, setSearchActive]);

  return (
    <div className="search-bar__form-container">
      <form action="#" onSubmit={onSearchSubmit} className="search-bar__form">
        <input
          autoComplete="off"
          name="search"
          className="search-bar__input"
          id="search-bar-input"
          onChange={handleChange}
          placeholder="Search"
          type="search"
          value={search}
          onKeyDown={onKeyDown}
        />
      </form>
      {searchActive && (
        <div
          aria-label="Search submit"
          className="search-bar__submit-button"
          onClick={onSearchSubmit}
          onKeyDown={onKeyDown}
          role="button"
          tabIndex={0}
        >
          SEARCH&gt;
        </div>
      )}
    </div>
  );

  function handleChange(event) {
    setSearch(event.target.value);
  }

  function onSearchSubmit() {
    onSubmit(search);
    setSearch("");
    document.activeElement.blur();
  }

  function onKeyDown(event) {
    if (event.keyCode === 13) {
      event.preventDefault();
      onSearchSubmit();
    }
  }
};
.search-bar {
  &__close-icon {
    background: url("/images/icons/close.png") no-repeat;
    background-size: contain;
    cursor: pointer;
    height: 26px;
    transition: all 80ms ease-out;
    width: 26px;

    &:hover {
      background: url("/images/icons/close-hover.png") no-repeat;
      background-size: contain;
    }
  }

  &__container {
    align-items: center;
    background-color: #ffffff;
    border-bottom: 1px solid #d8d8d8;
    color: #c5c5c5;
    display: flex;
    height: 112px;
    justify-content: space-between;
    left: 0;
    position: fixed;
    top: -112px;
    width: 100%;
    z-index: 1020;

    &.show {
      animation: searchFadeIn 200ms ease-out;
    }

    &.shown {
      top: 0;
      box-shadow: 0 0 0 100vmax rgba(51, 51, 51, 0.4);
    }

    &.hide {
      animation: searchFadeOut 300ms ease-in;
    }
  }

  @keyframes searchFadeIn {
    0% {
      box-shadow: 0 0 0 100vmax rgba(51, 51, 51, 0);
    }

    50% {
      box-shadow: 0 0 0 100vmax rgba(51, 51, 51, 0.2);
    }

    100% {
      box-shadow: 0 0 0 100vmax rgba(51, 51, 51, 0.4);
      top: 0px;
    }
  }

  @keyframes searchFadeOut {
    0% {
      box-shadow: 0 0 0 100vmax rgba(51, 51, 51, 0.4);
      top: 0;
    }

    50% {
      box-shadow: 0 0 0 100vmax rgba(51, 51, 51, 0.2);
    }

    100% {
      box-shadow: 0 0 0 100vmax rgba(51, 51, 51, 0);
      top: -75px;
    }
  }

  &__icon {
    background: url("/images/icons/search.png") no-repeat;
    background-size: contain;
    cursor: pointer;
    height: 24px;
    padding-right: 20px;
    width: 24px;
  }

  &__input {
    background: none;
    border: none;
    color: #333333;
    flex: 1;
    font-family: Setimo;
    font-size: 42px;
    line-height: 1.24;
    letter-spacing: 2px;
    max-width: 321px;
    padding-right: 10px;
  }

  &__form {
    margin: 0 20px 0 20px;
  }

  &__form-container {
    display: flex;
  }

  &__left {
    align-items: center;
    display: flex;
    padding-left: 40px;
  }

  &__right {
    color: inherit;
    font-size: 30px;
    padding-right: 40px;
  }

  &__search-icon {
    background: url("/images/icons/search-grey.png") no-repeat;
    background-size: contain;
    cursor: pointer;
    height: 24px;
    padding-right: 20px;
    width: 24px;

    &.active {
      background: url("/images/icons/search.png") no-repeat;
      background-size: contain;
    }
  }

  &__submit-button {
    align-self: flex-end;
    color: #9a9a9a;
    cursor: pointer;
    flex: 1;
    font-family: Setimo;
    font-size: 11px;
    font-weight: bold;
    line-height: 1.64;
    letter-spacing: 3.5px;
    margin-bottom: 7px;
    transition: all 80ms ease-out;
    width: 81px;

    &:hover {
      color: #333333;
    }
  }
}

.search-icon {
  &__container {
    align-items: center;
    cursor: pointer;
    display: flex;
    justify-content: space-between;
    transition: all 80ms ease-out;
    width: 97px;

    &:hover .search-icon__text {
      color: #7d7be4;
    }

    &:hover .search-icon__icon {
      background: url("/images/icons/search-hover.png") no-repeat;
      background-size: contain;
    }
  }

  &__icon {
    background: url("/images/icons/search.png") no-repeat;
    background-size: contain;
    cursor: pointer;
    height: 20px;
    width: 20px;
  }

  &__text {
    color: #333333;
    font-family: Setimo;
    font-size: 11px;
    font-weight: bold;
    letter-spacing: 3.5px;
    line-height: 1.64;
  }
}

::placeholder {
  color: #c5c5c5;
}

[type="search"] {
  -webkit-appearance: textfield;
}

input[type="text"]::-ms-clear {
  display: none;
  width: 0;
  height: 0;
}

input[type="text"]::-ms-reveal {
  display: none;
  width: 0;
  height: 0;
}

input[type="search"]::-webkit-search-decoration,
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-results-button,
input[type="search"]::-webkit-search-results-decoration {
  display: none;
}

@media (max-width: 959px) {
  .search-bar {
    &__container {
      height: 80px;
      top: -80px;
    }

    &__form {
      margin: 0;
    }

    &__input {
      font-size: 32px;
      max-width: 225px;
    }

    &__left {
      padding-left: 30px;
    }

    &__right {
      padding-right: 30px;
    }

    &__search-icon {
      padding-right: 14px;
    }
  }
}

@media (max-width: 480px) {
  .search-bar {
    &__close-icon {
      height: 20px;
      width: 20px;
    }

    &__container {
      height: 64px;
      top: -64px;
    }

    &__form {
      margin: 0;
    }

    &__input {
      font-size: 24px;
      letter-spacing: normal;
      max-width: 175px;
    }

    &__left {
      padding-left: 16px;
    }

    &__right {
      padding-right: 16px;
    }

    &__search-icon {
      padding-right: 10px;
    }

    &__submit-button {
      display: none;
    }
  }
}