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

延迟HTML5:在第一个事件之前无效的伪类,html,validation,usability,Html,Validation,Usability,我最近发现,:invalid伪类在页面加载后立即应用于必需的表单元素。例如,如果您有以下代码: <style> input:invalid { background-color: pink; color: white; } input:valid { background-color: white; color: black; } </style> … <input name="foo" required /> 输入:无效{背景颜色:粉红色;颜色:白色;}

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

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

输入:无效{背景颜色:粉红色;颜色:白色;}
输入:有效{背景色:白色;颜色:黑色;}
…
然后,页面将加载一个空的粉红色输入元素。将验证内置到HTML5中是很好的,但我认为大多数用户在有机会输入任何值之前并不希望表单进行验证。是否有任何方法可以延迟伪类的应用,直到影响该元素的第一个事件(表单提交、模糊、更改,任何适当的情况)出现?没有JavaScript是否可以做到这一点?

因为我们只想指出一个字段一旦存在,它就是无效的 focus,我们使用focus伪类触发无效样式。 (当然,从一开始就将所有必填字段标记为无效。) 这将是一个糟糕的设计选择。)

按照这个逻辑,你的代码看起来像这样

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

输入:焦点:必需:无效{背景颜色:粉红色;颜色:白色;}
输入:必需:有效{背景颜色:白色;颜色:黑色;}
在这里创建了一个小提琴:


正如您所猜测的,正如您将从小提琴中看到的,此技术仅在元素具有焦点时显示验证样式。一旦移开焦点,样式将被删除,无论其是否有效。无论如何都不理想。

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

比如:

<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>

input.touched:无效{背景颜色:粉红色;颜色:白色;}
input.toucted:有效{背景色:白色;颜色:黑色;}
document.addEventListener('DOMContentLoaded',function(){
var required=document.querySelectorAll('input:required');
对于(变量i=0;i

当然,它不会像IE8或更低版本那样工作,因为(a)
DOMContentLoaded
相对较新,在IE8问世时不是标准的,(b)IE8使用
attachEvent
而不是DOM标准
addEventListener
,并且(c)IE8不会关心
:required
,因为它在技术上不支持HTML5。

这在纯CSS中是不可能的,但可以通过JavaScript来实现。这是一个:

//在此处使用$.fn.one仅触发一次事件。
$(':required').one('blur keydown',function(){
console.log('toucted',this);
$(this.addClass('toucted');
});
/**
*所有必需的输入最初均为黄色。
*/
:必需{
背景颜色:浅黄色;
}
/**
*如果触摸到所需输入且该输入有效,则该输入应为白色。
*/
.触摸:必需:有效{
背景色:白色;
}
/**
*如果所需输入已被触摸且无效,则应为粉红色。
*/
.触摸:必需:无效{
背景颜色:粉红色;
}


姓名:
*必需的

年龄:

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

侦听
invalid
事件,将“invalid”类添加到表单中。添加了“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);
}

在每个未通过checkValidity检查的元素发生
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; }

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

我在代码库中创建了一个小垫片来处理这个问题。我只是从我的
元素开始,该元素具有
novalidate
属性以及
data validate on=“blur”
属性。这将监视该类型的第一个事件。这样,您仍然可以使用本机
:无效的
css选择器
$(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');
        });
    });
});
input.dirty:invalid{ color: red; }
input.dirty:valid{ color: green; }
// 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);
}
<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 type="text" name="username" placeholder=" " required>
:not(:placeholder-shown):invalid{ background-color: #ff000038; }