CSS 自动填充和验证

CSS autofill and validation

我正在设计一些用户输入的样式,但我遇到了一个绊脚石。

我不反对用户使用自动完成(我个人讨厌在某些网站上关闭它),因为我认为它真的很方便。我知道如何用 -webkit-autofill 设置样式。但是,自动完成将始终导致字段获得 :valid 状态,即使它们不应该如此。

例如,我将一个字段设置为至少 5 个字符长:

<input type="text" required class=namefileds id=first_name name=Name[] placeholder="Enter your first name" minlength="5">

我已经为 valid/invalid 个案例设置了 CSS 样式:

#checkout_form input:invalid {
    padding-right: calc(1.5em + .75rem);
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");
    background-repeat: no-repeat;
    background-position: center right calc(.375em + .1875rem);
    background-size: calc(.75em + .375rem) calc(.75em + .375rem);
}

    #checkout_form input:valid {
    padding-right: calc(1.5em + 0.75rem);
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%23ffcc00' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");
    background-repeat: no-repeat;
    background-position: right calc(0.375em + 0.1875rem) center;
    background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
}

这很好用。为了使自动填充样式很好地工作(在所有 webkit 浏览器上),我添加了以下 CSS 规则:

@-webkit-keyframes autofillvalid {
     0%,100% {
         color: #ffcc00;
         background:none;
         padding-right: calc(1.5em + 0.75rem);
         background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%23ffcc00' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");
         background-repeat: no-repeat;
         background-position: right calc(0.375em + 0.1875rem) center;
         background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
    }
}

 @-webkit-keyframes autofillinvalid {
    0%,100% {
        background:none;
        padding-right: calc(1.5em + .75rem);
        background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");
        background-repeat: no-repeat;
        background-position: center right calc(.375em + .1875rem);
        background-size: calc(.75em + .375rem) calc(.75em + .375rem);
    }
}

input:-webkit-autofill:valid {
     -webkit-animation-delay: 1s; /* Safari support - any positive time runs instantly */
     -webkit-animation-name: autofillvalid;
     -webkit-animation-fill-mode: both;
 }

 input:-webkit-autofill:invalid {
     -webkit-animation-delay: 1s; /* Safari support - any positive time runs instantly */
     -webkit-animation-name: autofillinvalid;
     -webkit-animation-fill-mode: both;
 }

我的问题是,如果自动填充的名称只有四个字符长,它应该得到 :invalid 状态,但自动填充总是生成 :valid 状态。其他状态选择器看起来工作正常,例如::focus

我看过 this 问题,但我暂时不想触发额外的 java 脚本,现阶段一切都是关于视觉的,验证将在稍后进行用户也与其他事物交互。这就是问题所在,如果自动填充给人一种错误的感觉,即数据是有效的,那么在稍后阶段显示为无效可能会非常令人沮丧……

知道如何仅从 CSS 设置样式吗?

如果我一定要用JS: 另一个问题是,在未手动更改值之前,样式不会在字段上更新,触发字段上的 jQuery .change() 事件无济于事

在写这个问题时,我决定做更多的挖掘,更糟糕的是,自动填充使元素 document.getElementById("first_name").checkValidity() return 为真,而它不应该为真。

因此,虽然我从一个 CSS 问题开始,但现在我更倾向于问这种行为是否被视为错误?

编辑 1: 由于 checkValidity() return 是真的,我尝试提交表单(通常不应该提交,因为不满足验证条件)并且它没有任何问题地提交了。当然,黄金法则永远不会信任用户数据,服务器端会拒绝它,但我认为这不应该发生,简单的自动填充不应该自动意味着数据有效。

希望对您有所帮助:

使用 "pattern" 属性代替 "minlength"。

#checkout_form input:invalid{
    padding-right: calc(1.5em + .75rem);
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");
    background-repeat: no-repeat;
    background-position: center right calc(.375em + .1875rem);
    background-size: calc(.75em + .375rem) calc(.75em + .375rem);
    }
    #checkout_form input:valid{
    padding-right: calc(1.5em + 0.75rem);
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%23ffcc00' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");
    background-repeat: no-repeat;
    background-position: right calc(0.375em + 0.1875rem) center;
    background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
    }
    
    @-webkit-keyframes autofillvalid {
    0%,100% {
        color: #ffcc00;
        background:none;
         padding-right: calc(1.5em + 0.75rem);
        background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%23ffcc00' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");
        background-repeat: no-repeat;
        background-position: right calc(0.375em + 0.1875rem) center;
        background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
    }
}
    @-webkit-keyframes autofillinvalid {
    0%,100% {
        background:none;
        padding-right: calc(1.5em + .75rem);
        background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");
        background-repeat: no-repeat;
        background-position: center right calc(.375em + .1875rem);
        background-size: calc(.75em + .375rem) calc(.75em + .375rem);
    }
}

    input:-webkit-autofill:valid {
        -webkit-animation-delay: 1s; /* Safari support - any positive time runs instantly */
        -webkit-animation-name: autofillvalid;
        -webkit-animation-fill-mode: both;
    }
    input:-webkit-autofill:invalid {
        -webkit-animation-delay: 1s; /* Safari support - any positive time runs instantly */
        -webkit-animation-name: autofillinvalid;
        -webkit-animation-fill-mode: both;
    }
<form id="checkout_form" >
  <input type="text" required class=namefileds id=first_name name=Name[] placeholder="Enter your first name" pattern=".{5,}">
  <button type="submit">submit</button>
</form>

这是一个非常有趣的问题。不过,我对一件事很好奇。 Chrome 只会在提交表单时保存 autocomplete 个值(如前所述 here)。从逻辑上讲,如果输入的 minlength 为 5,而您输入了长度为 4 的内容,那么您将无法首先提交表单 。因此,不应该保存无效的自动完成值。您是如何第一次遇到这种情况的?我只能通过 not 设置 minlength、提交,然后设置 minlength 并尝试自动填充来重现它。

为了回答您的问题,此行为不是错误。 minlength 不起作用,因为它的设计无法验证 autofill 插入的值。在下面阅读更多内容。

为什么 minlength 不起作用?

当值的长度小于指定的 minlength 时,为什么 minlength 不使 autocomplete 给出的值无效?在HTML Standard by WHATWG中提到,minlength有这个约束验证:

If an element has a minimum allowed value length, its dirty value flag is true, its value was last changed by a user edit (as opposed to a change made by a script), its value is not the empty string, and the JavaScript string length of the element's API value is less than the element's minimum allowed value length, then the element is suffering from being too short.

很明显,当值被 user edit 而不是脚本更改时,该值只会被 minlength 验证。进一步阅读,我们可以推断 autofill 的值是由脚本插入的(阅读 here)。我们可以测试 minlength 在输入值被脚本更改时不验证输入值,方法是执行以下操作(请在输入中插入单词 "Testing"):

const input = document.querySelector('input')
input.addEventListener('keyup', e => {
  if (input.value === "Testing") input.value = "Test"
})
input:invalid {
  outline: 1px solid red;
}
<input type="text" minlength="5">

您可以看到,当 input 的值被脚本更改为 "Test" 时,它不会费心验证 input

使用pattern

另一方面,

pattern 没有说明值必须由用户编辑更改的约束验证。您可以阅读它的 constraint validation here。因此,您仍然可以使用脚本更改 input 的值,它仍然有效。尝试再次输入 "Testing" 到 input:

const input = document.querySelector('input')
input.addEventListener('keyup', e => {
  if (input.value === "Testing") input.value = "Test"
})
input:invalid {
  outline: 1px solid red;
}
<input type="text" pattern="[0-9a-zA-Z]{5,}">

当它变为 "Test" 时,它会被 pattern 无效并导致 CSS 触发。这意味着您可以使用 pattern 来验证 autofill 给出的值。 (还是很好奇这个错误的 autofill 值一开始是怎么保存的。)

这是一个使用 pattern 来验证 autofill 给出的 input 并进行一些调整的解决方案:

* {
  box-sizing: border-box;
  margin: 0;
  font-family: Helvetica;
}

body {
  padding: 20px;
}

#checkout_form * {
  display: block;
  margin: 1em;
}

#first_name {
  padding: 10px;
  border-radius: 5px;
  width: 400px;
  border: 1px solid #00000044;
  outline: none;
}

#first_name::placeholder {
  color: #00000040;
}

#submit_form {
  padding: 10px;
  border-radius: 5px;
  border: none;
  background: #7F00FF;
  color: #ffffffee;
  width: 200px;
}

#checkout_form input:invalid {
  padding-right: calc(1.5em + .75rem);
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");
  background-repeat: no-repeat;
  background-position: center right calc(.375em + .1875rem);
  background-size: calc(.75em + .375rem) calc(.75em + .375rem);
  
  border: 1px solid #CC0000ee;
}

#checkout_form input:valid {
  padding-right: calc(1.5em + 0.75rem);
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%23ffcc00' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");
  background-repeat: no-repeat;
  background-position: right calc(0.375em + 0.1875rem) center;
  background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
  
  border: 1px solid #00cc00ee;
}

@-webkit-keyframes autofillvalid {
  0%,
  100% {
    color: #ffcc00;
    background: none;
    padding-right: calc(1.5em + 0.75rem);
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%23ffcc00' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");
    background-repeat: no-repeat;
    background-position: right calc(0.375em + 0.1875rem) center;
    background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
  }
}

@-webkit-keyframes autofillinvalid {
  0%,
  100% {
    background: none;
    padding-right: calc(1.5em + .75rem);
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");
    background-repeat: no-repeat;
    background-position: center right calc(.375em + .1875rem);
    background-size: calc(.75em + .375rem) calc(.75em + .375rem);
  }
}

input:-webkit-autofill:valid {
  -webkit-animation-name: autofillvalid;
  -webkit-animation-fill-mode: both;
}

input:-webkit-autofill:invalid {
  -webkit-animation-name: autofillinvalid;
  -webkit-animation-fill-mode: both;
}
<form id="checkout_form">
  <input type="text" required id="first_name" name="fname" placeholder="Enter your first name" pattern="[0-9a-zA-Z]{5,}" autocomplete="given-name" title="First Name should be at least 5 characters and must consist only of alphabets and/or numbers">
  <button id="submit_form" type="submit">Submit</button>
</form>

此外,我高度建议阅读更多关于autofill的内容以及如何更好地利用它here