将HTML5:无效的伪类延迟到第一个事件之前


73

我最近发现,:invalid伪类required在页面加载后立即应用于表单元素。例如,如果您具有以下代码:

<style>
input:invalid { background-color: pink; color: white; }
input:valid { background-color: white; color: black; }
</style><input name="foo" required />

然后,您的页面将加载一个空的粉红色输入元素。将验证内置到HTML5中是很棒的,但是我认为大多数用户都不希望表单有机会输入任何值之前先进行验证。有什么方法可以延迟伪类的应用,直到影响该元素的第一个事件(表单提交,模糊,更改,适当时)?没有JavaScript,是否可以做到这一点?


我无法发表评论,因此我将此补充内容添加到@ user3284463中。makeDirty不应切换脏类,而应将其保留在将来的(蓝色,无效,有效)事件函数makeDirty(e){if(!e.target .classList.contains('dirty'))e.target.classList.add('dirty'); }
Abir Stolov '18年

理想情况下,我希望CSS允许对表单元素进行:visited来完成此任务。
马蒂

Answers:


41

http://www.alistapart.com/articles/forward-thinking-form-validation/

由于我们只想表示一个具有焦点的字段是无效的,因此我们使用focus伪类触发无效的样式。(自然地,从一开始就将所有必填字段标记为无效将是一个糟糕的设计选择。)

按照这种逻辑,您的代码将如下所示:

<style>
    input:focus:required:invalid {background-color: pink; color: white;}
    input:required:valid {background-color: white; color: black; }
<style>

在这里创建了一个小提琴:http : //jsfiddle.net/tbERP/

如您所料,从小提琴中可以看到,该技术仅在元素具有焦点时才显示验证样式。一旦移开焦点,样式就会被丢弃,无论它是否有效。无论如何都不理想。


18
“无论如何都不理想。” 确实。在元素失去焦点之后,是否可以将样式应用于有效但必需的内容?我还没有想办法做到这一点。
唐纳德·詹金斯

1
我可以想象有一个(目前不存在的)pseduo类,用于“提交”之类的表单,这样它就可以充当任何包含的输入(如)的父选择器#myform:submitted input:required:invalid。因为即使当前的情况确实不直观,但期望CSS知道在特定情况下字段无效也是不必要的(就其所知,为什么不立即执行此操作?),因此您确实想将输入样式绑定到表单的状态,而不是输入本身(例如,当必填字段为空且提交父表单时)。
安东尼

我认为,这个答案:stackoverflow.com/questions/41619497/…实际上处理得很好,即使它需要一些javascript也是如此。它只是在表单中添加了一个“已提交”的类(就像我所建议的一样!),以便样式规则可以是form.submitted input:required:invalid
Anthony

同样,要完全耗尽这一点,其他类型:valid:invalid输入类型也应存在相同的问题。如果输入为类型integer且未填写任何内容,则由于空白不是有效的数字值,因此应将其视为无效。这不是问题的唯一原因是因为在required属性之外,空值被视为有效值。
安东尼

单击提交时,一次仅突出显示1个无效字段。通常,如果突出显示了所有无效项,那么这样做很方便,因此用户可以在单击提交之前将其全部更正。是否最好从所有字段中删除所需的标记,然后为onsubmit编写自定义js函数
Aseem,

31

这些答案已过期。现在,您可以通过使用CSS检查占位符伪类来执行此操作。

input:not(:placeholder-shown):invalid {
    background-color: salmon;
}
form:invalid button {
    background-color: salmon;
    pointer-events: none;
}
<form>
    <input type="email" placeholder="me@example.com" required>
    <button type="submit">Submit</button>
</form>

它以正常背景开头,并在您输入不完整的电子邮件地址时变成粉红色。


4
值得注意的是,如果您关心IE或Edge支持,:placeholder-shown则根本不支持伪类。caniuse.com/#feat=css-placeholder-showd
Leo Napoleon

5
不幸的是,当您唯一的验证需要通过时,它不起作用。当您触摸必填字段或尝试提交包含必填字段的表单时,它应该显示错误。空的必填字段仍将显示占位符。
比约恩·坦道

我添加了一个基于表单切换提交按钮的示例。
卡尔,

哇,太好了!
Ege

如果您输入部分电子邮件,它将再次变白,表示jon @ doe有效
S. Redrum

25

这在纯CSS中是不可能的,但是可以使用JavaScript来完成。这是一个jQuery示例

// use $.fn.one here to fire the event only once.
$(':required').one('blur keydown', function() {
  console.log('touched', this);
  $(this).addClass('touched');
});
/**
 * All required inputs initially are yellow.
 */
:required {
  background-color: lightyellow;
}

/**
 * If a required input has been touched and is valid, it should be white.
 */
.touched:required:valid {
  background-color: white;
}

/**
 * If a required input has been touched and is invalid, it should be pink.
 */
.touched:required:invalid {
  background-color: pink;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>
  <label>
    Name:
    <input type="text" required> *required
  </label>
</p>
<p>
  <label>Age:
    <input type="text">
  </label>
</p>


那是一个简单的解决方案;)
fegemo '16

1
这是我更喜欢的解决方案,但您不再需要jQuery。这可以用简单的Javascript完成。
米迦

3

我在代码库中创建了一个小垫片来处理此问题。我只是从<form/>具有novalidate属性和data-validate-on="blur"属性的元素开始。这将监视该类型的第一个事件。这样,您仍然可以将本机:invalidcss选择器用于表单样式。

$(function () {
    $('[data-validate-on]').each(function () {
        var $form = $(this);
        var event_name = $form.data('validate-on');

        $form.one(event_name, ':input', function (event) {
            $form.removeAttr('novalidate');
        });
    });
});

这是我的解决方案。
抢劫

3

Mozilla使用自己的:-moz-ui-invalid伪类来处理此问题,该伪类仅在与表单进行交互之后才适用。由于缺乏支持,MDN不建议使用此功能。但是,您可以为Firefox对其进行修改。

即将出现的:user-valid规范有4级规范,它将提供类似的行为。

抱歉,如果这样做没有帮助,但我希望它提供了一些上下文。


2

在使用HTML5表单验证时,请尝试使用浏览器检测无效的提交/字段,而不是重新发明轮子。

监听invalid事件以将“无效”类添加到您的表单中。添加“无效”类后,您可以使用CSS3:pseudo选择器来样式化表单。

例如:

// where myformid is the ID of your form
var myForm = document.forms.myformid;

var checkCustomValidity = function(field, msg) {
    if('setCustomValidity' in field) {
        field.setCustomValidity(msg);
    } else {
        field.validationMessage = msg;
    }
};

var validateForm = function() {

    // here, we're testing the field with an ID of 'name'
    checkCustomValidity(myForm.name, '');

    if(myForm.name.value.length < 4) {
        checkCustomValidity(
            // alerts fields error message response
            myForm.name, 'Please enter a valid Full Name, here.'
        );
    }
};

/* here, we are handling your question above, by adding an invalid
   class to the form if it returns invalid.  Below, you'll notice
   our attached listener for a form state of invalid */
var styleInvalidForm = function() {
    myForm.className = myForm.className += ' invalid';
}

myForm.addEventListener('input', validateForm, false);
myForm.addEventListener('keyup', validateForm, false);
myForm.addEventListener('invalid', styleInvalidForm, true);

现在,根据我们附加的“无效”类,简单地为您认为合适的样式设置表单。

例如:

form.invalid input:invalid,
form.invalid textarea:invalid {
    background: rgba(255, 0, 0, .05);
    border-color: #ff6d6d;
    -webkit-box-shadow: 0 0 6px rgba(255, 0, 0, .35);
    box-shadow: 0 0 6px rgba(255, 0, 0, .35);
}

5
呃,这不是在重塑轮子吗?
Ry-

2

对于每个未通过checkValidity的invalid元素,在submit事件发生之前都会在表单元素上触发html5事件。您可以使用此事件将一个类应用于周围的表单,并仅在此事件发生后显示:invalid样式。

 $("form input, form select, form textarea").on("invalid", function() {
     $(this).closest('form').addClass('invalid');
 });

然后,您的CSS将如下所示:

:invalid { box-shadow: none; }
.invalid input:invalid,
.invalid textarea:invalid,
.invalid select:invalid { border: 1px solid #A90909 !important; background-color: #EEC2C2; }

第一行删除了默认样式,因此表单元素在页面加载时看起来是中性的。一旦无效事件触发(当用户尝试提交表单时),这些元素就会明显变为无效。


2

这是kzh答案的VanillaJS(没有jQuery)版本

{
  let f = function() {
    this.classList.add('touched')
  }
  document
    .querySelectorAll('input')
    .forEach((e) => {
      e.addEventListener('blur', f, false)
      e.addEventListener('keydown', f, false)
    })
}
/**
 * All required inputs initially are yellow.
 */
:required {
  background-color: lightyellow;
}

/**
 * If a required input has been touched and is valid, it should be white.
 */
.touched:required:valid {
  background-color: white;
}

/**
 * If a required input has been touched and is invalid, it should be pink.
 */
.touched:required:invalid {
  background-color: pink;
}
<p><label>
  Name:
  <input type="text" required> *required
</label></p>
<p><label>Age:
  <input type="text">
</label></p>


1
虽然这是一个很好的解决方案,你可能要掉querySelectorAllgetElementsByTagName,因为它是约17000倍(!)我的机器上更快。自己测试一下:jsperf.com/queryselectorall-vs-getelementsbytagname
Boltgolt

1

您可以这样做,以便只有在其上具有特定类必需的元素才是粉红色的。向每个必需的元素添加一个事件处理程序,以便在离开该元素时添加该类。

就像是:

<style>
  input.touched:invalid { background-color: pink; color: white; }
  input.touched:valid { background-color: white; color: black; }
</style>
<script>
  document.addEventListener('DOMContentLoaded', function() {
    var required = document.querySelectorAll('input:required');
    for (var i = 0; i < required.length; ++i) {
      (function(elem) {
        function removeClass(name) {
          if (elem.classList) elem.classList.remove(name);
          else
            elem.className = elem.className.replace(
              RegExp('(^|\\s)\\s*' + name + '(?:\\s+|$)'),
              function (match, leading) {return leading;}
          );
        }

        function addClass(name) {
          removeClass(name);
          if (elem.classList) elem.classList.add(name);
          else elem.className += ' ' + name;
        }

        // If you require a class, and you use JS to add it, you end up
        // not showing pink at all if JS is disabled.
        // One workaround is to have the class on all your elements anyway,
        // and remove it when you set up proper validation.
        // The main problem with that is that without JS, you see what you're
        // already seeing, and stuff looks hideous.
        // Unfortunately, you kinda have to pick one or the other.


        // Let non-blank elements stay "touched", if they are already,
        // so other stuff can make the element :invalid if need be
        if (elem.value == '') addClass('touched');

        elem.addEventListener('blur', function() {
          addClass('touched');
        });

        // Oh, and when the form submits, they need to know about everything
        if (elem.form) {
          elem.form.addEventListener('submit', function() {
            addClass('touched');
          });
        };
      })(required[i]);
    }
  });
</script>

当然,它不会像IE8或更低版本那样工作,因为(a)DOMContentLoaded相对较新并且在IE8推出时不是标准,(b)IE8使用的attachEvent不是DOM标准addEventListener,并且(c)IE8:required无论如何都不会在乎,因为它在技术上不支持HTML 5。


1

一个好的方法是先:invalid, :valid使用CSS类进行抽象,然后再使用一些JavaScript来检查输入字段是否集中。

CSS:

input.dirty:invalid{ color: red; }
input.dirty:valid{ color: green; }

JS:

// Function to add class to target element
function makeDirty(e){
  e.target.classList.toggle('dirty');
}

// get form inputs
var inputs = document.forms[0].elements;

// bind events to all inputs
for(let input of inputs){
  input.addEventListener('invalid', makeDirty);
  input.addEventListener('blur', makeDirty);
  input.addEventListener('valid', makeDirty);
}

演示


1

遵循agouseh想法,您可以使用一些JavaScript来告知何时将提交按钮聚焦,并在那时显示验证。

submit-focussed当焦点位于或单击提交按钮时,javascript将在表单字段中添加一个类(例如),然后允许CSS为无效的输入设置样式。

这遵循在用户填写完字段后显示验证反馈的最佳做法,因为根据研究,在此过程中显示验证反馈没有其他好处。

document
  .querySelector('input[type=submit]')
  .onfocus = function() {
    this
      .closest('form')
      .classList
      .add('submit-focussed');
  };
form.submit-focussed input:invalid {
  border: thin solid red;
}
<form>
  <label>Email <input type="email" required="" /></label>
  <input type="submit" />
</form>

jQuery替代

(function($) {
  $('input[type=submit]').on('focus', function() {
    $(this)
      .parent('form')
      .addClass('submit-focussed');
  });
})(jQuery); /* WordPress compatible */

0

这是我的方法,可以避免将所有未聚焦输入的默认样式设置为无效,您只需添加一个简单的js命令onFocus即可让网页识别focusedunfocused输入,因此所有输入一开始都不会以无效样式显示。

<style>
input.focused:required:invalid { background-color: pink; color: white; }
input:valid { background-color: white; color: black; }
</style><input name="foo" class="notfocused" onFocus="document.activeElement.className='focused';" required />

请在下面自己尝试:

input.focused:required:invalid {
  background-color: pink;
  color: white;
}

input:required:valid {
  background-color: darkseagreen;
  color: black;
}
<label>At least 1 charater:</label><br />
<input type="text" name="foo" class="notfocused" onFocus="document.activeElement.className='focused';" required />


0

我不能发表评论,但可以使用@Carl关于使用:not(:placeholder-shown)的非常有用的答案。如前所述,如果您没有占位符(某些表单设计要求),它将仍然显示无效状态。

要解决这个问题,只需添加一个空的占位符,就像这样

<input type="text" name="username" placeholder=" " required>

然后是您的CSS,类似

:not(:placeholder-shown):invalid{ background-color: #ff000038; }

为我工作!

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.