web-dev-qa-db-ja.com

HTML5の遅延:最初のイベントまで無効な擬似クラス

最近、:invalid擬似クラスは、ページが読み込まれるとすぐにrequiredフォーム要素に適用されます。たとえば、次のコードがある場合:

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

次に、空のピンク色の入力要素がページにロードされます。 HTML5に検証が組み込まれているのは素晴らしいことですが、ほとんどのユーザーは、値を入力する前にフォームが検証されることを期待していないと思います。その要素に影響する最初のイベント(フォームの送信、ぼかし、変更、適切なもの)まで、擬似クラスの適用を遅らせる方法はありますか? JavaScriptなしでこれを行うことは可能ですか?

59
kojiro

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

フォーカスを取得するとフィールドが無効であることのみを示すため、フォーカス疑似クラスを使用して無効なスタイルをトリガーします。 (当然、最初からすべての必須フィールドに無効のフラグを付けることは、設計上の選択として不適切です。)

このロジックに従うと、コードは次のようになります...

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

ここでフィドルを作成しました: http://jsfiddle.net/tbERP/

ご想像のとおり、フィドルからわかるように、この手法は、要素にフォーカスがある場合にのみ検証スタイルを表示します。フォーカスをオフにするとすぐに、スタイリングは有効かどうかに関係なく削除されます。決して理想的ではありません。

35
Bart

これは純粋な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>
21
kzh

これらの答えは時代遅れです。これで、CSSを使用してプレースホルダー擬似クラスを確認することでこれを行うことができます。

input:not(:placeholder-shown):invalid {
    background-color: salmon;
}
form:invalid button {
    background-color: salmon;
    pointer-events: none;
}
<form>
    <input type="email" placeholder="[email protected]" required>
    <button type="submit">Submit</button>
</form>

通常の背景で始まり、不完全なメールアドレスを入力するとピンク色に変わります。

18
Carl

これをコードベースで処理する小さなシムを作成しました。 <form/>属性とともにnovalidateプロパティを持つdata-validate-on="blur"要素から始めます。これは、そのタイプの最初のイベントを監視します。この方法では、フォームのスタイル設定にネイティブ:invalid 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');
        });
    });
});
3
aghouseh

Mozillaは、独自の :-moz-ui-invalid 擬似クラスを使用してこれを処理します。擬似クラスは、やり取りされた後のフォームにのみ適用されます。サポートが不足しているため、MDNはこれの使用を推奨しません。ただし、Firefox用に変更できます。

同様の動作を提供する地平線上の :user-invalid 仕様にはレベル4仕様があります。

これが役に立たない場合は申し訳ありませんが、コンテキストが提供されることを願っています。

3
Aaron Benjamin

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);
}
2
Jonathan Calvin

CheckValidityを渡さない要素ごとにinvalidイベントが発生する前に、フォーム要素で発生するhtml5 submitイベントがあります。このイベントを使用して、たとえば周囲のフォームにクラスを適用し、このイベントが発生した後にのみ: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
Liquinaut

これは kzhanswer の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>
2
yunzen

特定のクラスandが必要な要素のみがピンクになるように作成できます。要素を離れるときにそのクラスを追加する各必須要素にイベントハンドラを追加します。

何かのようなもの:

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

もちろん、(a)DOMContentLoadedは比較的新しく、IE8がリリースされたときは標準ではなかったため、(b)IE8はattachEventを使用しているため、IE8以下では動作しませんDOM標準のaddEventListenerではなく、(c)IE8は:requiredとにかく、HTML 5を技術的にサポートしていないため。

1
cHao

良い方法は、CSSクラスで:invalid, :validを抽象化してから、入力フィールドがフォーカスされているかどうかをチェックする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);
}

[〜#〜] demo [〜#〜]

1
Rust

コメントすることはできませんが、:not(:placeholder-shown)の使用に関する@Carlの非常に有用な回答に進みます。別のコメントで述べたように、プレースホルダーがない場合は、無効な状態が表示されます(一部のフォームデザインでは必要です)。

これを解決するには、次のように空のプレースホルダーを追加するだけです

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

次に、CSS、次のようなもの

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

私のために働いた!

0
Marcus Frasier

agousehidea に続いて、送信ボタンがフォーカスされたことを通知するJavaScriptを少し持つことができ、その時点で検証が表示されます。

Javascriptはクラスを追加します(例:submit-focussed)送信ボタンがフォーカスまたはクリックされたときにフォームフィールドに追加されます。これにより、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
robrecord

これは、フォーカスされていない入力の無効なデフォルト設定を回避する私の方法です。単純なjsコマンドonFocusを追加するだけで、Webページで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
timo