React.js - 自动完成:如何在单击其他区域时隐藏建议列表
React.js - Autocomplete: How To Hide Suggest Lst While Clicking On Other Areas
我读了这个 tutorial 并尝试制作一个自动完成的文本框。
部分代码如下:
import React, {useState} from 'react';
import {SearchInput, SearchUL, SearchLI} from './index.style';
// based on this: https://programmingwithmosh.com/react/simple-react-autocomplete-component/
const Autocomplete = ({suggestions = []}) => {
const [activeSuggestion, setActiveSuggestion] = useState(0);
const [filteredSuggestions, setFilteredSuggestions] = useState([]);
const [showSuggestions, setShowSuggestions] = useState(false);
const [userInput, setUserInput] = useState('');
const [isTyping, setIsTyping] = useState(false);
// timer id
let typingTimer;
//
let doneTypingInterval = 1000;
// on change
const onChange = e => {
// input
const userInput = e.currentTarget.value;
// match
const filteredSuggestions = suggestions.filter(
suggestion =>
suggestion.toLowerCase().indexOf(userInput.toLowerCase()) > -1
);
// set state
setActiveSuggestion(0);
setFilteredSuggestions(filteredSuggestions);
setShowSuggestions(true);
setUserInput(e.currentTarget.value);
};
// onclick
const onClick = e => {
// set state
setActiveSuggestion(0);
setFilteredSuggestions([]);
setShowSuggestions(false);
setUserInput(e.currentTarget.innerText); //?
console.log('on click');
};
// done
const doneTyping = () => {
console.log('done type');
setIsTyping(false);
};
// key down
const onKeyDown = e => {
if (e.keyCode === 13) {
// 1. enter key
// state
setActiveSuggestion(0);
setShowSuggestions(false);
setUserInput(filteredSuggestions[activeSuggestion]);
} else if (e.keyCode === 38) {
// 2. up arrow key
// no active, out
if (activeSuggestion === 0) {
return;
}
// go up, -1
setActiveSuggestion(activeSuggestion - 1);
} else if (e.keyCode === 40) {
// 3. down arr
if (activeSuggestion - 1 === filteredSuggestions.length) {
return;
}
// go down +1
setActiveSuggestion(activeSuggestion + 1);
} else {
}
};
// key up
const onKeyUp = e => {
// clear old timer
clearTimeout(typingTimer);
// has val, then clean up
if (e.currentTarget.value) {
setIsTyping(true);
typingTimer = setTimeout(doneTyping, doneTypingInterval);
}
};
// tmp: sugList
let suggestionsListComponent;
// state: show and u-input
if (showSuggestions && userInput) {
// state: filterSug.len
if (filteredSuggestions.length) {
// ul + li
suggestionsListComponent = (
<SearchUL>
{filteredSuggestions.map((suggestion, index) => {
return (
<SearchLI key={suggestion} onClick={onClick}>
{suggestion}
</SearchLI>
);
})}
</SearchUL>
);
} else {
// no
suggestionsListComponent = (
<div>
<em>No suggestions!</em>
</div>
);
}
}
// --------
// NOTE: this makes the drop down list disappear, but not able to click the list
// --------
const onBlur = () => {
setShowSuggestions(false);
};
return (
<>
<SearchInput
type="text"
onChange={onChange}
onKeyDown={onKeyDown}
onKeyUp={onKeyUp}
value={userInput}
isTyping={isTyping}
//onBlur={onBlur}
/>
{suggestionsListComponent}
</>
);
};
export default Autocomplete;
抱歉,代理不允许我上传图片。总之,当你开始输入时,如果匹配,就会有一个下拉列表。单击项目中的 1 个,搜索框将被填满。
现在图片打到一半,然后鼠标移动到其他区域,所以像onBlur
,下拉列表无法消失。
我试着做了一个onBlur
函数
const onBlur = () => {
setShowSuggestions(false);
};
并有这样的东西:
return (
<>
<SearchInput
type="text"
onChange={onChange}
onKeyDown={onKeyDown}
onKeyUp={onKeyUp}
value={userInput}
isTyping={isTyping}
//onBlur={onBlur}
/>
{suggestionsListComponent}
</>
);
这一次,如果我点击其他区域,下拉列表可以隐藏,但是当我正常输入和select下拉item时,selected 项目无法进入搜索框。
完整代码here
我已经为您创建了一个沙盒 link。检查这个:https://codesandbox.io/s/testformik-xgl3w
在此,我用 div
包装了您的组件并传递了 ref
。在 componentDidMount
上,我正在收听单击 document
并调用该函数,如果单击的目标与自动完成组件(输入和建议)不同,我将关闭建议框。
这是新代码:
import React, { useState, useEffect } from "react";
import { SearchInput, SearchUL, SearchLI } from "./index.style";
const inputRef = React.createRef();
// based on this: https://programmingwithmosh.com/react/simple-react-autocomplete-component/
const Autocomplete = ({ suggestions = [] }) => {
const [activeSuggestion, setActiveSuggestion] = useState(0);
const [filteredSuggestions, setFilteredSuggestions] = useState([]);
const [showSuggestions, setShowSuggestions] = useState(false);
const [userInput, setUserInput] = useState("");
const [isTyping, setIsTyping] = useState(false);
const handleOuterClick = e => {
if (!inputRef.current.contains(e.target)) {
setShowSuggestions(false);
}
};
useEffect(() => {
document.addEventListener("click", handleOuterClick);
}, []);
// timer id
let typingTimer;
//
let doneTypingInterval = 1000;
// on change
const onChange = e => {
// input
const userInput = e.currentTarget.value;
// match
const filteredSuggestions = suggestions.filter(
suggestion =>
suggestion.toLowerCase().indexOf(userInput.toLowerCase()) > -1
);
// set state
setActiveSuggestion(0);
setFilteredSuggestions(filteredSuggestions);
setShowSuggestions(true);
setUserInput(e.currentTarget.value);
};
// onclick
const onClick = e => {
// set state
setActiveSuggestion(0);
setFilteredSuggestions([]);
setShowSuggestions(false);
setUserInput(e.currentTarget.innerText); //?
console.log("on click");
};
// done
const doneTyping = () => {
console.log("done type");
setIsTyping(false);
};
// key down
const onKeyDown = e => {
if (e.keyCode === 13) {
// 1. enter key
// state
setActiveSuggestion(0);
setShowSuggestions(false);
setUserInput(filteredSuggestions[activeSuggestion]);
} else if (e.keyCode === 38) {
// 2. up arrow key
// no active, out
if (activeSuggestion === 0) {
return;
}
// go up, -1
setActiveSuggestion(activeSuggestion - 1);
} else if (e.keyCode === 40) {
// 3. down arr
if (activeSuggestion - 1 === filteredSuggestions.length) {
return;
}
// go down +1
setActiveSuggestion(activeSuggestion + 1);
} else {
}
};
// key up
const onKeyUp = e => {
// clear old timer
clearTimeout(typingTimer);
// has val, then clean up
if (e.currentTarget.value) {
setIsTyping(true);
typingTimer = setTimeout(doneTyping, doneTypingInterval);
}
};
// tmp: sugList
let suggestionsListComponent;
// state: show and u-input
if (showSuggestions && userInput) {
// state: filterSug.len
if (filteredSuggestions.length) {
// ul + li
suggestionsListComponent = (
<SearchUL>
{filteredSuggestions.map((suggestion, index) => {
return (
<SearchLI key={suggestion} onClick={onClick}>
{suggestion}
</SearchLI>
);
})}
</SearchUL>
);
} else {
// no
suggestionsListComponent = (
<div>
<em>No suggestions!</em>
</div>
);
}
}
return (
<div ref={inputRef}>
<SearchInput
type="text"
onChange={onChange}
onKeyDown={onKeyDown}
onKeyUp={onKeyUp}
value={userInput}
isTyping={isTyping}
/>
{suggestionsListComponent}
</div>
);
};
export default Autocomplete;
希望对您有所帮助!
使用计时器更新您的 onBlur
函数,如下所示:
const onBlurr = () => {
setTimeout(() => setShowSuggestions(false), 500);
};
我读了这个 tutorial 并尝试制作一个自动完成的文本框。
部分代码如下:
import React, {useState} from 'react';
import {SearchInput, SearchUL, SearchLI} from './index.style';
// based on this: https://programmingwithmosh.com/react/simple-react-autocomplete-component/
const Autocomplete = ({suggestions = []}) => {
const [activeSuggestion, setActiveSuggestion] = useState(0);
const [filteredSuggestions, setFilteredSuggestions] = useState([]);
const [showSuggestions, setShowSuggestions] = useState(false);
const [userInput, setUserInput] = useState('');
const [isTyping, setIsTyping] = useState(false);
// timer id
let typingTimer;
//
let doneTypingInterval = 1000;
// on change
const onChange = e => {
// input
const userInput = e.currentTarget.value;
// match
const filteredSuggestions = suggestions.filter(
suggestion =>
suggestion.toLowerCase().indexOf(userInput.toLowerCase()) > -1
);
// set state
setActiveSuggestion(0);
setFilteredSuggestions(filteredSuggestions);
setShowSuggestions(true);
setUserInput(e.currentTarget.value);
};
// onclick
const onClick = e => {
// set state
setActiveSuggestion(0);
setFilteredSuggestions([]);
setShowSuggestions(false);
setUserInput(e.currentTarget.innerText); //?
console.log('on click');
};
// done
const doneTyping = () => {
console.log('done type');
setIsTyping(false);
};
// key down
const onKeyDown = e => {
if (e.keyCode === 13) {
// 1. enter key
// state
setActiveSuggestion(0);
setShowSuggestions(false);
setUserInput(filteredSuggestions[activeSuggestion]);
} else if (e.keyCode === 38) {
// 2. up arrow key
// no active, out
if (activeSuggestion === 0) {
return;
}
// go up, -1
setActiveSuggestion(activeSuggestion - 1);
} else if (e.keyCode === 40) {
// 3. down arr
if (activeSuggestion - 1 === filteredSuggestions.length) {
return;
}
// go down +1
setActiveSuggestion(activeSuggestion + 1);
} else {
}
};
// key up
const onKeyUp = e => {
// clear old timer
clearTimeout(typingTimer);
// has val, then clean up
if (e.currentTarget.value) {
setIsTyping(true);
typingTimer = setTimeout(doneTyping, doneTypingInterval);
}
};
// tmp: sugList
let suggestionsListComponent;
// state: show and u-input
if (showSuggestions && userInput) {
// state: filterSug.len
if (filteredSuggestions.length) {
// ul + li
suggestionsListComponent = (
<SearchUL>
{filteredSuggestions.map((suggestion, index) => {
return (
<SearchLI key={suggestion} onClick={onClick}>
{suggestion}
</SearchLI>
);
})}
</SearchUL>
);
} else {
// no
suggestionsListComponent = (
<div>
<em>No suggestions!</em>
</div>
);
}
}
// --------
// NOTE: this makes the drop down list disappear, but not able to click the list
// --------
const onBlur = () => {
setShowSuggestions(false);
};
return (
<>
<SearchInput
type="text"
onChange={onChange}
onKeyDown={onKeyDown}
onKeyUp={onKeyUp}
value={userInput}
isTyping={isTyping}
//onBlur={onBlur}
/>
{suggestionsListComponent}
</>
);
};
export default Autocomplete;
抱歉,代理不允许我上传图片。总之,当你开始输入时,如果匹配,就会有一个下拉列表。单击项目中的 1 个,搜索框将被填满。
现在图片打到一半,然后鼠标移动到其他区域,所以像onBlur
,下拉列表无法消失。
我试着做了一个onBlur
函数
const onBlur = () => {
setShowSuggestions(false);
};
并有这样的东西:
return (
<>
<SearchInput
type="text"
onChange={onChange}
onKeyDown={onKeyDown}
onKeyUp={onKeyUp}
value={userInput}
isTyping={isTyping}
//onBlur={onBlur}
/>
{suggestionsListComponent}
</>
);
这一次,如果我点击其他区域,下拉列表可以隐藏,但是当我正常输入和select下拉item时,selected 项目无法进入搜索框。
完整代码here
我已经为您创建了一个沙盒 link。检查这个:https://codesandbox.io/s/testformik-xgl3w
在此,我用 div
包装了您的组件并传递了 ref
。在 componentDidMount
上,我正在收听单击 document
并调用该函数,如果单击的目标与自动完成组件(输入和建议)不同,我将关闭建议框。
这是新代码:
import React, { useState, useEffect } from "react";
import { SearchInput, SearchUL, SearchLI } from "./index.style";
const inputRef = React.createRef();
// based on this: https://programmingwithmosh.com/react/simple-react-autocomplete-component/
const Autocomplete = ({ suggestions = [] }) => {
const [activeSuggestion, setActiveSuggestion] = useState(0);
const [filteredSuggestions, setFilteredSuggestions] = useState([]);
const [showSuggestions, setShowSuggestions] = useState(false);
const [userInput, setUserInput] = useState("");
const [isTyping, setIsTyping] = useState(false);
const handleOuterClick = e => {
if (!inputRef.current.contains(e.target)) {
setShowSuggestions(false);
}
};
useEffect(() => {
document.addEventListener("click", handleOuterClick);
}, []);
// timer id
let typingTimer;
//
let doneTypingInterval = 1000;
// on change
const onChange = e => {
// input
const userInput = e.currentTarget.value;
// match
const filteredSuggestions = suggestions.filter(
suggestion =>
suggestion.toLowerCase().indexOf(userInput.toLowerCase()) > -1
);
// set state
setActiveSuggestion(0);
setFilteredSuggestions(filteredSuggestions);
setShowSuggestions(true);
setUserInput(e.currentTarget.value);
};
// onclick
const onClick = e => {
// set state
setActiveSuggestion(0);
setFilteredSuggestions([]);
setShowSuggestions(false);
setUserInput(e.currentTarget.innerText); //?
console.log("on click");
};
// done
const doneTyping = () => {
console.log("done type");
setIsTyping(false);
};
// key down
const onKeyDown = e => {
if (e.keyCode === 13) {
// 1. enter key
// state
setActiveSuggestion(0);
setShowSuggestions(false);
setUserInput(filteredSuggestions[activeSuggestion]);
} else if (e.keyCode === 38) {
// 2. up arrow key
// no active, out
if (activeSuggestion === 0) {
return;
}
// go up, -1
setActiveSuggestion(activeSuggestion - 1);
} else if (e.keyCode === 40) {
// 3. down arr
if (activeSuggestion - 1 === filteredSuggestions.length) {
return;
}
// go down +1
setActiveSuggestion(activeSuggestion + 1);
} else {
}
};
// key up
const onKeyUp = e => {
// clear old timer
clearTimeout(typingTimer);
// has val, then clean up
if (e.currentTarget.value) {
setIsTyping(true);
typingTimer = setTimeout(doneTyping, doneTypingInterval);
}
};
// tmp: sugList
let suggestionsListComponent;
// state: show and u-input
if (showSuggestions && userInput) {
// state: filterSug.len
if (filteredSuggestions.length) {
// ul + li
suggestionsListComponent = (
<SearchUL>
{filteredSuggestions.map((suggestion, index) => {
return (
<SearchLI key={suggestion} onClick={onClick}>
{suggestion}
</SearchLI>
);
})}
</SearchUL>
);
} else {
// no
suggestionsListComponent = (
<div>
<em>No suggestions!</em>
</div>
);
}
}
return (
<div ref={inputRef}>
<SearchInput
type="text"
onChange={onChange}
onKeyDown={onKeyDown}
onKeyUp={onKeyUp}
value={userInput}
isTyping={isTyping}
/>
{suggestionsListComponent}
</div>
);
};
export default Autocomplete;
希望对您有所帮助!
使用计时器更新您的 onBlur
函数,如下所示:
const onBlurr = () => {
setTimeout(() => setShowSuggestions(false), 500);
};