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