Chrome Autofill导致文本框标签和数值发生碰撞。

huangapple go评论123阅读模式
英文:

Chrome Autofill causes textbox collision for Textfield Label and Value

问题

在React中,自动完成Chrome值不会立即触发onChange事件。因此,在页面初始加载期间会导致MUI TextField标签和实际值发生冲突。如何解决这个问题?

尝试了多种方法,但值变小时InputLabelProps收缩也不起作用。https://stackoverflow.com/a/65070465/15435022

  1. <StyledTextField
  2. fullWidth
  3. id="email"
  4. label="Email"
  5. size="medium"
  6. value={emailAddress}
  7. onChange={(e) => setEmailAddress(e.target.value)}
  8. error={emailError}
  9. InputLabelProps={{
  10. shrink: emailAddress != null && emailAddress !== "" ? true : false,
  11. }}
  12. helperText={emailError ? "Please enter a valid email" : "Required"}
  13. />

尝试此解决方案也会出现问题:在执行const theme = createTheme({时。

Github资源:
https://github.com/mui/material-ui/issues/14427#issuecomment-466054906

Chrome Autofill导致文本框标签和数值发生碰撞。
Chrome Autofill导致文本框标签和数值发生碰撞。

英文:

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 ?

Chrome Autofill导致文本框标签和数值发生碰撞。

Trying numerous methods, InputLabelProps shrink on Value does not work either. https://stackoverflow.com/a/65070465/15435022

  1. &lt;StyledTextField
  2. fullWidth
  3. id=&quot;email&quot;
  4. label=&quot;Email&quot;
  5. size=&quot;medium&quot;
  6. value={emailAddress}
  7. onChange={(e) =&gt; setEmailAddress(e.target.value)}
  8. error={emailError}
  9. InputLabelProps={{
  10. shrink: emailAddress != null &amp;&amp; emailAddress != &quot;&quot; ? true : false,
  11. }}
  12. helperText={emailError ? &quot;Please enter a valid email&quot; : &quot;Required&quot;}
  13. /&gt;

Trying this solution also gives issues: when doing const theme = createTheme({

Github resource:
https://github.com/mui/material-ui/issues/14427#issuecomment-466054906

Chrome Autofill导致文本框标签和数值发生碰撞。

答案1

得分: 2

个人而言,我认为相对于禁用收缩或自动完成登录,这并不值得一试,但没有人征求我的意见,所以...

据我了解,之所以比我们想象中更令人痛苦的原因是因为它最初是作为一种安全功能来防止通过自动完成来窃取密码的,但一旦Chrome(等等)删除了限制,React的去重机制接管了。有一些比我即将提出的稍微“hack-y”的解决方法,但您可以决定哪种方法适合您。

为了避开这个问题,您可以为每个“input”的“onAnimationStart”事件添加一个处理程序,检查“animationName”是否为“mui-auto-fill”,然后检查“input”是否具有“-webkit-autofill”伪类,以查看浏览器是否自动填充了字段。您还需要处理场景中的“mui-auto-fill-cancel”情况,其中表单被自动填充并且用户清除了值(以重置“shrink”)。

例如:

  1. const [passwordHasValue, setPasswordHasValue] = React.useState(false);
  2. // 为了可重用性,我将其作为一个接受useState设置函数的函数来制作,
  3. // 并返回一个每个输入使用的处理程序,因为您至少有两个
  4. // 要处理的TextField。
  5. const makeAnimationStartHandler = (stateSetter) => (e) => {
  6. const autofilled = !!e.target?.matches("*:-webkit-autofill");
  7. if (e.animationName === "mui-auto-fill") {
  8. stateSetter(autofilled);
  9. }
  10. if (e.animationName === "mui-auto-fill-cancel") {
  11. stateSetter(autofilled);
  12. }
  13. };
  14. ...
  15. <TextField
  16. type="password"
  17. id="password"
  18. inputProps={{
  19. onAnimationStart: makeAnimationStartHandler(setPasswordHasValue)
  20. }}
  21. InputLabelProps={{
  22. shrink: passwordHasValue
  23. }}
  24. label="Password"
  25. value={password}
  26. onChange={(e) => {
  27. setPassword(e.target.value);
  28. ...
  29. }}
  30. />

加载时的结果应如下所示:

Chrome Autofill导致文本框标签和数值发生碰撞。

带取消的更新 - 允许用户在自动填充后清除表单字段,并重置标签:

Chrome Autofill导致文本框标签和数值发生碰撞。

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 &quot;mui-auto-fill&quot;, 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 &quot;mui-auto-fill-cancel&quot; case for scenarios where the form is auto-filled and the user clears the values (to reset shrink.)

For example:

  1. const [passwordHasValue, setPasswordHasValue] = React.useState(false);
  2. // For re-usability, I made this a function that accepts a useState set function
  3. // and returns a handler for each input to use, since you have at least two
  4. // TextFields to deal with.
  5. const makeAnimationStartHandler = (stateSetter) =&gt; (e) =&gt; {
  6. const autofilled = !!e.target?.matches(&quot;*:-webkit-autofill&quot;);
  7. if (e.animationName === &quot;mui-auto-fill&quot;) {
  8. stateSetter(autofilled);
  9. }
  10. if (e.animationName === &quot;mui-auto-fill-cancel&quot;) {
  11. stateSetter(autofilled);
  12. }
  13. };
  14. ...
  15. &lt;TextField
  16. type=&quot;password&quot;
  17. id=&quot;password&quot;
  18. inputProps={{
  19. onAnimationStart: makeAnimationStartHandler(setPasswordHasValue)
  20. }}
  21. InputLabelProps={{
  22. shrink: passwordHasValue
  23. }}
  24. label=&quot;Password&quot;
  25. value={password}
  26. onChange={(e) =&gt; {
  27. setPassword(e.target.value);
  28. ...
  29. }}
  30. /&gt;

The result on load should appear as:

Chrome Autofill导致文本框标签和数值发生碰撞。

Update with cancel -- allows user to clear out the form field, after load with auto-fill, and the label is reset:

Chrome Autofill导致文本框标签和数值发生碰撞。

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。效果很好。我根据你的答案为我的项目编写了一个组件:

  1. import { TextField } from "@mui/material";
  2. import { useCallback, useState } from "react";
  3. const AutoFillAwareTextField = ({ onChange, inputProps, InputLabelProps, ...rest }) => {
  4. const [fieldHasValue, setFieldHasValue] = useState(false)
  5. const makeAnimationStartHandler = (stateSetter) => (e) => {
  6. const autofilled = !!e.target?.matches("*:-webkit-autofill");
  7. if (e.animationName === "mui-auto-fill") {
  8. stateSetter(autofilled);
  9. }
  10. if (e.animationName === "mui-auto-fill-cancel") {
  11. stateSetter(autofilled);
  12. }
  13. }
  14. const _onChange = useCallback((e) => {
  15. onChange(e.target.value);
  16. setFieldHasValue(e.target.value !== "");
  17. }, [onChange])
  18. return <TextField
  19. inputProps={{
  20. onAnimationStart: makeAnimationStartHandler(setFieldHasValue),
  21. ...inputProps
  22. }}
  23. InputLabelProps={{
  24. shrink: fieldHasValue,
  25. ...InputLabelProps
  26. }}
  27. onChange={_onChange}
  28. {...rest}
  29. />
  30. }
  31. export default AutoFillAwareTextField

请注意,以上是你提供的代码的翻译。

英文:

Thanks for this Steve. Works great. I wrote a component for my project based on your answer:

  1. import {TextField} from &quot;@mui/material&quot;;
  2. import {useCallback, useState} from &quot;react&quot;;
  3. const AutoFillAwareTextField = ({onChange, inputProps, InputLabelProps, ...rest}) =&gt; {
  4. const [fieldHasValue, setFieldHasValue] = useState(false)
  5. const makeAnimationStartHandler = (stateSetter) =&gt; (e) =&gt; {
  6. const autofilled = !!e.target?.matches(&quot;*:-webkit-autofill&quot;);
  7. if (e.animationName === &quot;mui-auto-fill&quot;) {
  8. stateSetter(autofilled);
  9. }
  10. if (e.animationName === &quot;mui-auto-fill-cancel&quot;) {
  11. stateSetter(autofilled);
  12. }
  13. }
  14. const _onChange = useCallback((e) =&gt; {
  15. onChange(e.target.value);
  16. setFieldHasValue(e.target.value !== &quot;&quot;)
  17. }, [onChange])
  18. return &lt;TextField
  19. inputProps={{
  20. onAnimationStart: makeAnimationStartHandler(setFieldHasValue),
  21. ...inputProps
  22. }}
  23. InputLabelProps={{
  24. shrink: fieldHasValue,
  25. ...InputLabelProps
  26. }}
  27. onChange={_onChange}
  28. {...rest}
  29. /&gt;
  30. }
  31. export default AutoFillAwareTextField

huangapple
  • 本文由 发表于 2023年8月4日 02:26:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/76830737.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定