英文:
Chrome Autofill causes textbox collision for Textfield Label and Value
问题
在React中,自动完成Chrome值不会立即触发onChange事件。因此,在页面初始加载期间会导致MUI TextField标签和实际值发生冲突。如何解决这个问题?
尝试了多种方法,但值变小时InputLabelProps收缩也不起作用。https://stackoverflow.com/a/65070465/15435022
<StyledTextField
fullWidth
id="email"
label="Email"
size="medium"
value={emailAddress}
onChange={(e) => setEmailAddress(e.target.value)}
error={emailError}
InputLabelProps={{
shrink: emailAddress != null && emailAddress !== "" ? true : false,
}}
helperText={emailError ? "Please enter a valid email" : "Required"}
/>
尝试此解决方案也会出现问题:在执行const theme = createTheme({
时。
Github资源:
https://github.com/mui/material-ui/issues/14427#issuecomment-466054906
英文:
In React, Autocomplete Chrome values don't trigger onChange event immediately. Thus it causes a collision of MUI TextField Label and actual values, during initial loading of page.
How can I resolve this issue ?
Trying numerous methods, InputLabelProps shrink on Value does not work either. https://stackoverflow.com/a/65070465/15435022
<StyledTextField
fullWidth
id="email"
label="Email"
size="medium"
value={emailAddress}
onChange={(e) => setEmailAddress(e.target.value)}
error={emailError}
InputLabelProps={{
shrink: emailAddress != null && emailAddress != "" ? true : false,
}}
helperText={emailError ? "Please enter a valid email" : "Required"}
/>
Trying this solution also gives issues: when doing const theme = createTheme({
Github resource:
https://github.com/mui/material-ui/issues/14427#issuecomment-466054906
答案1
得分: 2
个人而言,我认为相对于禁用收缩或自动完成登录,这并不值得一试,但没有人征求我的意见,所以...
据我了解,之所以比我们想象中更令人痛苦的原因是因为它最初是作为一种安全功能来防止通过自动完成来窃取密码的,但一旦Chrome(等等)删除了限制,React的去重机制接管了。有一些比我即将提出的稍微“hack-y”的解决方法,但您可以决定哪种方法适合您。
为了避开这个问题,您可以为每个“input”的“onAnimationStart”事件添加一个处理程序,检查“animationName”是否为“mui-auto-fill”,然后检查“input”是否具有“-webkit-autofill”伪类,以查看浏览器是否自动填充了字段。您还需要处理场景中的“mui-auto-fill-cancel”情况,其中表单被自动填充并且用户清除了值(以重置“shrink”)。
例如:
const [passwordHasValue, setPasswordHasValue] = React.useState(false);
// 为了可重用性,我将其作为一个接受useState设置函数的函数来制作,
// 并返回一个每个输入使用的处理程序,因为您至少有两个
// 要处理的TextField。
const makeAnimationStartHandler = (stateSetter) => (e) => {
const autofilled = !!e.target?.matches("*:-webkit-autofill");
if (e.animationName === "mui-auto-fill") {
stateSetter(autofilled);
}
if (e.animationName === "mui-auto-fill-cancel") {
stateSetter(autofilled);
}
};
...
<TextField
type="password"
id="password"
inputProps={{
onAnimationStart: makeAnimationStartHandler(setPasswordHasValue)
}}
InputLabelProps={{
shrink: passwordHasValue
}}
label="Password"
value={password}
onChange={(e) => {
setPassword(e.target.value);
...
}}
/>
加载时的结果应如下所示:
带取消的更新 - 允许用户在自动填充后清除表单字段,并重置标签:
FYI:我将我的makeAnimationStartHandler
制作成一个接受React.useState()
设置函数作为参数并返回执行实际工作的处理程序,因为您的示例有两个字段,我希望 1)减少代码和2)如果需要的话仍然允许您单独处理每个字段的手动输入用例。
工作示例: https://z3vxm7.csb.app/
工作CodeSandbox: https://codesandbox.io/s/autofill-and-mui-label-shrink-z3vxm7?file=/Demo.tsx
英文:
Personally, I don't think the juice is worth the squeeze for this vs just disabling shrink or auto-complete for the log-in, but no one asked my opinion, so...
From what I understand, the reason this is more painful than we'd like is because it was originally intended as a security feature to prevent password theft via auto-complete, but once Chrome (et al.) removed the restrictions, React's de-duping mechanism took over. There are some slightly more hack-y work-arounds than what I'm going to suggest, but you can decide which you like.
To side-step this, you can add a handler to each input
's onAnimationStart
event, check if the animationName
is "mui-auto-fill"
, and then check if the input
has a -webkit-autofill
pseudo class, to see if the browser has auto-filled the field. You'll also want to handle the "mui-auto-fill-cancel"
case for scenarios where the form is auto-filled and the user clears the values (to reset shrink
.)
For example:
const [passwordHasValue, setPasswordHasValue] = React.useState(false);
// For re-usability, I made this a function that accepts a useState set function
// and returns a handler for each input to use, since you have at least two
// TextFields to deal with.
const makeAnimationStartHandler = (stateSetter) => (e) => {
const autofilled = !!e.target?.matches("*:-webkit-autofill");
if (e.animationName === "mui-auto-fill") {
stateSetter(autofilled);
}
if (e.animationName === "mui-auto-fill-cancel") {
stateSetter(autofilled);
}
};
...
<TextField
type="password"
id="password"
inputProps={{
onAnimationStart: makeAnimationStartHandler(setPasswordHasValue)
}}
InputLabelProps={{
shrink: passwordHasValue
}}
label="Password"
value={password}
onChange={(e) => {
setPassword(e.target.value);
...
}}
/>
The result on load should appear as:
Update with cancel -- allows user to clear out the form field, after load with auto-fill, and the label is reset:
FYI: I made my makeAnimationStartHandler
a function that accepts a React.useState()
setter as a param and returns a handler that actually does the work because your example has two fields and I wanted to 1) reduce code and 2) allow you to still handle the manual entry use-case for each field separately, if desired.
Working Example: https://z3vxm7.csb.app/
Working CodeSandbox: https://codesandbox.io/s/autofill-and-mui-label-shrink-z3vxm7?file=/Demo.tsx
答案2
得分: 1
感谢你,Steve。效果很好。我根据你的答案为我的项目编写了一个组件:
import { TextField } from "@mui/material";
import { useCallback, useState } from "react";
const AutoFillAwareTextField = ({ onChange, inputProps, InputLabelProps, ...rest }) => {
const [fieldHasValue, setFieldHasValue] = useState(false)
const makeAnimationStartHandler = (stateSetter) => (e) => {
const autofilled = !!e.target?.matches("*:-webkit-autofill");
if (e.animationName === "mui-auto-fill") {
stateSetter(autofilled);
}
if (e.animationName === "mui-auto-fill-cancel") {
stateSetter(autofilled);
}
}
const _onChange = useCallback((e) => {
onChange(e.target.value);
setFieldHasValue(e.target.value !== "");
}, [onChange])
return <TextField
inputProps={{
onAnimationStart: makeAnimationStartHandler(setFieldHasValue),
...inputProps
}}
InputLabelProps={{
shrink: fieldHasValue,
...InputLabelProps
}}
onChange={_onChange}
{...rest}
/>
}
export default AutoFillAwareTextField
请注意,以上是你提供的代码的翻译。
英文:
Thanks for this Steve. Works great. I wrote a component for my project based on your answer:
import {TextField} from "@mui/material";
import {useCallback, useState} from "react";
const AutoFillAwareTextField = ({onChange, inputProps, InputLabelProps, ...rest}) => {
const [fieldHasValue, setFieldHasValue] = useState(false)
const makeAnimationStartHandler = (stateSetter) => (e) => {
const autofilled = !!e.target?.matches("*:-webkit-autofill");
if (e.animationName === "mui-auto-fill") {
stateSetter(autofilled);
}
if (e.animationName === "mui-auto-fill-cancel") {
stateSetter(autofilled);
}
}
const _onChange = useCallback((e) => {
onChange(e.target.value);
setFieldHasValue(e.target.value !== "")
}, [onChange])
return <TextField
inputProps={{
onAnimationStart: makeAnimationStartHandler(setFieldHasValue),
...inputProps
}}
InputLabelProps={{
shrink: fieldHasValue,
...InputLabelProps
}}
onChange={_onChange}
{...rest}
/>
}
export default AutoFillAwareTextField
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论