インタラクティブフォームの構築


オリジナルのフォームとの主な挑戦は、対話を扱うことでした.任意のJavaScriptなしで、我々はコードを読むのが困難で組織化されなかったハックとトリックに頼らなければなりませんでした.
JSを持つことは、我々の人生を単純化するつもりです.我々がしたことのいくつかが標準でなくて、彼らがWebKitブラウザーの外で働くつもりでなかったので、それはかなり我々のブラウザー支持を広げます.

Note: I am not going to use any JS framework or transpiler, and stick with vanilla JavaScript for this form. It makes the snippet lighter, cleaner (that's my opinion), and framework-agnostic. If you are using a framework/transpiler, you can easily adapt the code to it.


クラスの使用


ユーザーアクションによって引き起こされるイベントの多くは、色、位置、またはサイズの変更で構成されています.関数内で直接変更を適用する代わりに、我々がすることができるのは、クラスにクラスを適用し、CSSで必要な変更を処理することです.
これにはいくつかの利点があります.
  • クリーナーJavaScript :コードは、イベントが発生したときにクラスを追加/削除することで構成されます.
  • 操作の分離:JavaScriptはロジックを担当します(変更が引き起こされたときに決定)、CSSはスタイリングを担当します.
  • 簡単に維持し、変更:乱雑なJSコードを持っていない後の変更とメンテナンスを簡素化します.
  • しかし、小さな変化から始めましょう.クラス変更を必要としない唯一のもの

    パスワードを隠す


    元のフォームでは、我々は非標準との表示/非表示のパスワード機能を扱う -webkit-text-security . それはコードが自然な順序でない理由の1つであり、非標準的な機能として、それは私たちのフォームの範囲を制限しました.
    現時点では、JavaScriptを使用してその機能を処理し、幸運に私たちのため、それはかなり簡単なことです:我々は、チェックボックスにイベントリスナーを添付し、変更するたびに、我々は更新されますtype パスワードの入力text を返します.password を返します.
    // show password interactivity
    document.querySelector("#show-password").addEventListener("input", function() {
      document.querySelector("#password").type = this.checked ? "text" : "password";
    });
    
    私たちはif...else 構造体ですが、三項演算子はコードをかなり簡素化します.

    入力スタイルの拡張


    チュートリアルの次の(および最後の)手順では、要素イベント(ホバー、フォーカスなど)のいくつかのスタイルを追加しますが、いくつかのケースはCSSで完全にカバーできません.
    主なものは、入力がフォーカスを持つときにラベルテキストをスタイリングすることです.私たちは :focus-within ラベルに適用されますがnot supported by all browsers . 他のフォームでは、それは“簡単”でした:我々は入力とテキストの順序を交換し、兄弟セレクタを使用することができました.
    入力が-または紛失したかどうかチェックする必要がありますlabel :
    const labels = document.querySelectorAll("#login-form label input");
    for (let x = 0; x < labels.length; x++) {
        labels[x].addEventListener("focus", function() {
        this.parentNode.classList.add("state-focus");
      });
    
      labels[x].addEventListener("blur", function() {
        this.parentNode.classList.remove("state-focus");
      });
    }
    
    それから、そのクラスの特別なケースがあります.ラベルテキストを作成し、入力をメイン色のライターバージョンに渡します.
    label.state-focus span {
      color: hsl(var(--fgColorH), calc(var(--fgColorS) * 2), calc(var(--fgColorL) * 1.15));
    }
    
    label input:focus,
    label.state-focus input,
    label.state-focus span.checkbox-label::before {
      border-color: hsl(var(--fgColorH), calc(var(--fgColorS) * 2), calc(var(--fgColorL) * 1.15));
    }
    
    label.state-focus input[type="checkbox"]:checked + span.checkbox-label::after {
      background: hsl(var(--fgColorH), calc(var(--fgColorS) * 2), calc(var(--fgColorL) * 1.15));
    }
    
    次のステップでは、使用します:focus-within このJavaScriptのスニペットに“フォールバック”オプションとして.

    フォームの検証


    理想的には、検証はフロントエンドとバックエンドの両方で起こります.バックエンド側の検証は、このチュートリアルの範囲外ですので、私はフロントエンドに焦点を当てます.ではなく、
    ブラウザでフロントエンドの検証を委任していますが、HTMLの検証をサポートしていないブラウザを見つける場合、JavaScriptのロジックを追加することもできます.私たちのいくつかの基本的な検証ではなく、複雑なだけで、最小の長さをチェックする minlength しかし、テスト用には、以下を追加しません):
    // form submission = validation
    document.querySelector(".login-form").addEventListener("submit", function(e) {
      let errorMessage = "";
      const error = document.querySelector("#error-message");
      const username = document.querySelector("#username");
      const password = document.querySelector("#password");
    
      error.textContent = "";
    
      // check password length
      if (password.value.length < 8) {
        errorMessage = "Password is mandatory";
        password.parentNode.classList.add("state-error");
        password.focus();
      // check email length
      } else if (username.value.length < 5) {
        errorMessage = "Username is too short (min 5 chars)";
        username.parentNode.classList.add("state-error");
        username.focus();
      } 
    
      if (errorMessage != "") {
        error.textContent = errorMessage;
        e.preventDefault();
      }
    });
    
    // remove state error on field change
    function removeError(e) {
        e.target.parentNode.classList.remove("state-error");
      document.querySelector("#error-message").textContent = "";
    }
    document.querySelector("#password").addEventListener("input", removeError);
    document.querySelector("#username").addEventListener("input", removeError);
    
    エラーが発生した場合、フォームは送信されません(event.preventDefault() では、エラーメッセージを表示します(フォームに追加しました).

    検証の一部として、我々はまた、error-state フィールドのラベルへのクラス.そのように、我々は我々が欲しい(しかし、我々のケースでは、赤い境界線とテキストで)間違ったフィールドを整えることもできますこのエラー状態は、入力が変更されるたびに削除されます.

    キャラクターアニメーション


    最後に-最後に!本当の楽しい部分.私たちのフォームをユニークにするもの:前のセクションで作成したCSS文字のアニメーション.
    文字で多くのことができます.
  • フィールドが正しいときに笑顔を大きくする.
  • 「表示パスワード」ボックスがチェックされるとき、目を閉じてください.
  • “表示パスワード”ボックスをチェックするときに(ダーリンSenneffは彼の有名なイエティフォームで行うように)目をカバーします.
  • アニメーションエラー/正しいフィールドに応じて眉.
  • 目のテキストのパスに従ってsomething similar in a previous form .)
  • テキスト入力(darin senneffのものがこれを持っている)の後、全体の頭を動かしてください
  • エラーが発生した場合は口で異なる式を持ってください.
  • 入力が集中しているが、何もX秒後に書かれていない場合は、文字が気が遠くなる.
  • すべてのフィールドが有効な場合は紙吹雪/パーティの帽子が落ちる.
  • ...あなたが想像できる何でも.
  • それは単に提案のショートリストです.それらのいくつかはリストからいくつかの他とは互換性がありませんが、それらの多くを結合することができます.あなたが追加したいものを選ぶあなたの呼び出しです.このデモでは、いくつかを追加します.
  • フィールドが正しいときに笑顔を大きくする.
  • すべてのフィールドが正しいときにも笑顔を作る.
  • 「表示パスワード」ボックスがチェックされるとき、目を閉じてください.
  • 正しいアニメーション


    スマイルを調整するのは無効なフィールドの数に依存し、我々は口のサイズと位置を変更することによってそれを達成するdiv . JavaScriptコードはクラスを使用して簡単です(再び!)そして、我々が欲しいものを得るまで、我々は少しの高さ/幅/トップ位置で少し遊びなければなりません:
    const invalidFields = document.querySelectorAll("input:invalid").length;
    document.querySelector(".mouth").className = `mouth errors-${invalidFields}`;
    
    /* already existing default state: 2 errors */
    figure .head .mouth {
      border: 0.125rem solid transparent;
      border-bottom: 0.125rem solid var(--borderDarker);
      width: 25%;
      border-radius: 50%;
      transition: all 0.333s;
      top: 75%;
      left: 50%;
      height: 10%;
    }
    
    /* only one input is invalid: bigger smile */
    figure .head .mouth.errors-1 {
      top: 61%;
      width: 35%;
      height: 40%;
    }
    
    /* no inputs are invalid: the biggest smile */
    figure .head .mouth.errors-0 {
      top: 53%;
      width: 45%;
      height: 55%;
    }
    
    これはCSS + JSのうちの25行(口の中にはすでに11が存在する)です.

    表示/非表示のパスワードアニメーション


    目を閉じるには、「表示パスワード」チェックボックス変更イベント(余分なCSSを追加)を処理するために、既存のコードに1行のコードを追加する必要があります.
    document.querySelector(".eyes").className = `eyes ${this.checked && " closed"}`;
    
    figure .head .eyes.closed::before,
    figure .head .eyes.closed::after {
      height: 0.125rem;
      animation: none;
    }
    
    それはそんなに悪くなかった.では、なぜ何か余分な追加?ユーザー名フィールドとして目の動きを追加しましょう!

    テキストを書く


    特にこのフォントは少しトリッキーなので、私が選んだフォントはモノスペースではありません.これは、異なる文字が異なる幅(例えば、私が同じスペースで合うだけである間、私がテキストボックスに収まるように)を持っていることを意味します、そして、我々は目がどれくらいの目を動かす必要があるかについて、目玉をする必要があります.
    すべてのユーザーがすべてのIまたはMのすべてのユーザー名を持つというわけではありません、そして、一般に、他の手紙の幅は、それらの2つの間の何かであるでしょう.それで、通常、25 - 30文字がテキストフィールドに合うふりをしましょう.27?
    それから私たちの論理は次のようになります.
  • ユーザ名の入力がフォーカスを取得した場合、
  • ユーザー名入力がフォーカスを失うならば、彼らの垂直位置に目を返してください.
  • 27文字の最大を考慮すると、左に“値の長さ- 14”ピクセル目を移動します.
  • コードはこのように見えます( CSSの変更は必要ありません).
    // move eyes following username input 
    function moveEyes(e) {
      const eyes = document.querySelector(".eyes");
      const length = e.target.value.length;
      // this is a bit trickier because the eyes already have a translation!
      eyes.style.transform = `translate(calc(-50% + ${Math.min(length/2 - 7, 7)}px), calc(-50% + 0.25rem))`;
    }
    document.querySelector("#username").addEventListener("focus", moveEyes);
    document.querySelector("#username").addEventListener("input", moveEyes);
    document.querySelector("#username").addEventListener("blur", function() {
      document.querySelector(".eyes").style.transform = "translate(-50%, -50%)";
    });
    

    Note: the transform calculation is a bit messy because the eyes' div already has a transformation applied to it. I tried using margins, but then the transition is not as smooth and nice.


    これは今のままであるJSFiddle :
    つの最後のステップ:いくつかのスタイルを調整するので、JavaScriptに依存関係がなく、いくつかの追加のものをきれいにします.