Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

+ add another resource

JavaScript

Babel includes JSX processing.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                <div class="wrap">
  <form class="c-form" id="js-form">
    <div class="c-form__error-message u-mb40" data-js-form-error-message style="display: none;">入力内容に不備があります<br>お手数ですが再度入力内容をご確認ください</div>
    <div class="c-form-inputs">
      <div class="c-form-inputs__group">
        <label class="c-form-inputs__label"><span class="__required">必須</span>氏名</label>
        <div class="c-form-inputs__input">
          <input type="text" name="name" id="name" required placeholder="例)氏名 太郎" autocomplete="off">
          <div class="c-form-inputs__error-message" data-js-error-message="name"></div>
        </div>
      </div>
      <div class="c-form-inputs__group">
        <label class="c-form-inputs__label"><span class="__required">必須</span>メールアドレス</label>
        <div class="c-form-inputs__input">
          <input type="email" name="email" id="email" required pattern="^[a-zA-Z0-9-_.]+@[a-zA-Z0-9-_.]+$" title="メールアドレスの形式で入力してください" placeholder="例)example@ex.com" autocomplete="off">
          <div class="c-form-inputs__error-message" data-js-error-message="email"></div>
        </div>
      </div>
      <div class="c-form-inputs__group">
        <label class="c-form-inputs__label"><span class="__required">必須</span>電話番号</label>
        <div class="c-form-inputs__input">
          <input type="tel" name="tel" id="tel" required pattern="^0[0-9]{9,10}$" title="電話番号の形式で入力してください" placeholder="例)09012345678" autocomplete="off">
          <div class="c-form-inputs__error-message" data-js-error-message="tel"></div>
          <div class="c-form-inputs__caption"><small>ハイフン無しで入力してください</small></div>
        </div>
      </div>
      <div class="c-form-inputs__group">
        <label class="c-form-inputs__label"><span class="__required">必須</span>年齢</label>
        <div class="c-form-inputs__input">
          <select name="selectbox" id="selectbox" required data-required-error="いずれかを選択してください">
            <option value="">選択してください</option>
            <option value="1">10代</option>
            <option value="2">20代</option>
            <option value="3">30代</option>
            <option value="4">40代</option>
            <option value="5">50代</option>
            <option value="6">60歳以上</option>
          </select>
          <div class="c-form-inputs__error-message" data-js-error-message="selectbox"></div>
        </div>
      </div>
      <div class="c-form-inputs__group--checkbox">
        <label class="c-form-inputs__label"><span class="__required">必須</span>雇用形態</label>
        <div class="c-form-inputs__input">
          <div class="l-flex">
            <div>
              <input type="checkbox" name="job" id="job-1" required data-required-error="1つ以上選択してください">
              <label for="job-1">会社員</label>
            </div>
            <div>
              <input type="checkbox" name="job" id="job-2" required data-required-error="1つ以上選択してください">
              <label for="job-2">公務員</label>
            </div>
            <div>
              <input type="checkbox" name="job" id="job-3" required data-required-error="1つ以上選択してください">
              <label for="job-3">パート・アルバイト</label>
            </div>
            <div>
              <input type="checkbox" name="job" id="job-4" required data-required-error="1つ以上選択してください">
              <label for="job-4">自営業(経営者)</label>
            </div>
            <div>
              <input type="checkbox" name="job" id="job-5" required data-required-error="1つ以上選択してください">
              <label for="job-5">自営業(その他)</label>
            </div>
            <div>
              <input type="checkbox" name="job" id="job-6" required data-required-error="1つ以上選択してください">
              <label for="job-6">その他</label>
            </div>
          </div>
          <div class="c-form-inputs__error-message" data-js-error-message="job"></div>
        </div>
      </div>
      <div class="c-form-inputs__group--radio">
        <label class="c-form-inputs__label"><span class="__required">必須</span>住居</label>
        <div class="c-form-inputs__input">
          <div class="l-flex">
            <div>
              <input type="radio" name="house" id="house-1" required data-required-error="1つ以上選択してください">
              <label for="house-1">持ち家</label>
            </div>
            <div>
              <input type="radio" name="house" id="house-2" required data-required-error="1つ以上選択してください">
              <label for="house-2">賃貸</label>
            </div>
            <div>
              <input type="radio" name="house" id="house-3" required data-required-error="1つ以上選択してください">
              <label for="house-3">その他</label>
            </div>
          </div>
          <div class="c-form-inputs__error-message" data-js-error-message="house"></div>
        </div>
      </div>
      <div class="c-form-inputs__group">
        <label class="c-form-inputs__label"><span class="__any">任意</span>任意項目</label>
        <div class="c-form-inputs__input">
          <textarea name="any"></textarea>
        </div>
      </div>
      <div class="c-form-inputs__group">
        <div class="u-w100p">
          <div class="l-flex __col-center">
            <input type="checkbox" name="privacy-policy" id="privacy-policy" required data-required-error="プライバシーポリシーに同意される場合、チェックを入れてください">
            <label for="privacy-policy"><a class="u-link" href="#" target="_blank" rel="noreferrer noopener">プライバシーポリシー</a>に同意する</label>
          </div>
          <div class="l-flex __col-center">
            <div class="c-form-inputs__error-message" data-js-error-message="privacy-policy"></div>
          </div>
        </div>
      </div>
    </div>
    <div class="c-form-submit">
      <button class="c-btn" type="submit">送信する</button>
    </div>
    <div class="c-form__error-message u-mt40" data-js-form-error-message style="display: none;">入力内容に不備があります<br>お手数ですが再度入力内容をご確認ください</div>
  </form>
</div>
              
            
!

CSS

              
                // Variables
// ##############################
$breackpoint_pc: 1080px
$breackpoint_tb: 750px
$breackpoint_sp: 375px

$c--primary: #014c86
$c--accent: #f5df4d

$c--black: #333
$c--white: #fff

$c--gray-s: #f5f5f5
$c--gray-m: #d5d5d5
$c--gray-l: #939597

$c--text: $c--black
$c--link: $c--primary

$c--safe: #1fda50
$c--warning: #e63444



// Mixin
// ##############################
=max-screen($breakPoint)
  @media screen and (max-width: $breakPoint)
    @content
    
    

// Reset
// ##############################
*
  box-sizing: border-box

/* webkit specific styles */

input[type="color"]::-webkit-color-swatch
  border: none

input[type="color"]::-webkit-color-swatch-wrapper
  padding: 0

// html5doctor.com Reset Stylesheet
// v1.6.1
// Last Updated: 2010-09-17
// Author: Richard Clark - http://richclarkdesign.com
// Twitter: @rich_clark

html,
body,
div,
span,
object,
iframe,
h1,
h2,
h3,
h4,
h5,
h6,
p,
blockquote,
pre,
abbr,
address,
cite,
code,
del,
dfn,
em,
img,
ins,
kbd,
q,
samp,
small,
strong,
sub,
sup,
var,
b,
i,
dl,
dt,
dd,
ol,
ul,
li,
fieldset,
form,
label,
legend,
table,
caption,
tbody,
tfoot,
thead,
tr,
th,
td,
article,
aside,
canvas,
details,
figcaption,
figure,
footer,
header,
hgroup,
menu,
nav,
section,
summary,
time,
mark,
audio,
video
  margin: 0
  padding: 0
  border: 0
  outline: 0
  font-size: 100%
  vertical-align: baseline
  background: transparent
  font-weight: inherit

body
  line-height: 1

article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
menu,
nav,
section
  display: block

nav ul
  list-style: none

blockquote,
q
  quotes: none

blockquote:before,
blockquote:after,
q:before,
q:after
  content: ''
  content: none

a
  margin: 0
  padding: 0
  font-size: 100%
  vertical-align: baseline
  background: transparent

/* change colours to suit your needs */

mark
  background-color: #ff9
  color: #000
  font-style: italic
  font-weight: bold

del
  text-decoration: line-through

abbr[title],
dfn[title]
  border-bottom: 1px dotted
  cursor: help

table
  border-collapse: collapse
  border-spacing: 0

/* change border colour to suit your needs */

hr
  display: block
  height: 1px
  border: 0
  border-top: 1px solid #cccccc
  margin: 1em 0
  padding: 0

input,
select
  vertical-align: middle

input:focus
  outline: none

ul,
ol
  list-style-type: none

// Add

body
  -ms-text-size-adjust: 100%
  -webkit-text-size-adjust: 100%

main
  display: block

li
  list-style: none

h1,
h2,
h3,
h4,
h5,
h6
  font-size: 16px

a
  color: inherit
  width: 100%
  text-decoration: none
  &:hover
    color: inherit

img
  vertical-align: middle
  max-width: 100%
  height: auto

address
  font-style: normal

sup
  vertical-align: super
  font-size: smaller

input,
button,
textarea,
select
  display: block
  color: $c--text
  font-size: 16px
  background: none
  border: none
  border-radius: 0
  outline: none
  width: 100%
  margin: 0
  padding: 0
  -webkit-appearance: none
  -moz-appearance: none
  appearance: none
  -moz-appearance: textfield

  // オートフィルで入力した際の文字色と入力欄の背景色
  &:-webkit-autofill
    color: $c--text
    -webkit-box-shadow: 0 0 0px 1000px $c--white inset

  // プレースホルダーの文字色
  &::placeholder
    color: #b5b5b6

  // input[type="number"]の増減ボタン非表示
  &::-webkit-outer-spin-button,
  &::-webkit-inner-spin-button
    -webkit-appearance: none
    margin: 0

input,
textarea,
select
  line-height: 1.5
  background-color: $c--white
  border: solid 1px $c--gray-m
  border-radius: 3px
  padding: 10px 12px
  &[readonly]
    color: rgba($c--black, .7)
    border: none
    border-radius: 0

textarea
  min-height: 100px

select
  // IEの矢印を非表示
  &::-ms-expand
    display: none

button
  cursor: pointer

%__input_radio_check_hide
  // `display: none`で消すのはアクセシビリティ的によろしくない
  // @see https://github.com/mike-engel/a11y-css-reset
  position: absolute
  white-space: nowrap
  width: 1px
  height: 1px
  border: 0
  margin: -1px
  padding: 0
  overflow: hidden
  clip: rect(0 0 0 0)
  clip-path: inset(50%)

input[type="radio"]
  $parent_radio: &
  @extend %__input_radio_check_hide
  + label
    display: block
    font-size: 14px
    margin-right: 20px
    margin-bottom: 4px
    padding-left: 28px
    position: relative
    cursor: pointer
    &::before
      content: ""
      display: block
      width: 20px
      height: 20px
      background-color: $c--gray-s
      border: solid 1px $c--gray-m
      border-radius: 50%
      position: absolute
      top: 0
      left: 0
      @at-root #{$parent_radio}:checked + label::before
        border: solid 1px $c--primary
    &::after
      content: ""
      display: none
      // codepenだと何故か中心からズレるから小数点に...
      width: 13.5px
      height: 13.5px
      background-color: $c--primary
      border-radius: 50%
      position: absolute
      top: 4px
      left: 4px
      @at-root #{$parent_radio}:checked + label::after
        display: block

input[type="checkbox"]
  $parent_checkbox: &
  @extend %__input_radio_check_hide
  + label
    display: block
    font-size: 14px
    margin-right: 20px
    margin-bottom: 4px
    padding-left: 28px
    position: relative
    cursor: pointer
    &::before
      content: ""
      display: block
      width: 20px
      height: 20px
      background-color: $c--gray-s
      border: solid 1px $c--gray-m
      border-radius: 2px
      position: absolute
      top: 0
      left: 0
      @at-root #{$parent_checkbox}:checked + label::before
        background-color: $c--primary
        border-color: $c--primary
    &::after
      content: ""
      display: none
      width: 6px
      height: 10px
      border: solid $c--white
      border-width: 0 2px 2px 0
      position: absolute
      top: 3px
      left: 7px
      transform: rotate(45deg)
      @at-root #{$parent_checkbox}:checked + label::after
        display: block

select
  cursor: pointer
  padding-right: 32px
  background-repeat: no-repeat
  background-position: right 8px center
  background-size: 10px 10px
  background-image: url(data:image/png;base64,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)

		

// Base
// ##############################
body,
input,
button,
textarea,
select
  color: $c--text
  font-size: 16px
  font-family: "游ゴシック Medium", "游ゴシック体", "Yu Gothic Medium", YuGothic, "ヒラギノ角ゴ ProN", "Hiragino Kaku Gothic ProN", "メイリオ", Meiryo, "MS Pゴシック", "MS PGothic", sans-serif

.wrap
  line-height: 1.7
  padding: 40px 20px 80px
  
  

// Layout
// ##############################
.l-flex
  display: flex
  flex-wrap: wrap
  &.__col-center
    justify-content: center
  &.__col-between
    justify-content: space-between
  &.__col-start
    justify-content: flex-start
  &.__col-end
    justify-content: flex-end

	

// Component
// ##############################
.c-form
  max-width: 600px
  margin: auto
  &__error-message
    color: $c--warning
    font-size: 13px
    text-align: center
    background-color: rgba($c--warning, .1)
    border: solid 1px $c--warning
    border-radius: 2px
    padding: 12px 12px
  &-inputs
    &__group
      $parent_inputs_group: &
      display: flex
      align-items: flex-start
      &:not(:first-of-type)
        margin-top: 40px
      +max-screen($breackpoint_tb)
        display: block
      &--checkbox
        @extend #{$parent_inputs_group}
      &--radio
        @extend #{$parent_inputs_group}
    &__label
      flex: 0 0 auto
      display: flex
      align-items: center
      font-size: 15px
      width: 100%
      max-width: 200px
      +max-screen($breackpoint_tb)
        max-width: 100%
      .__required,
      .__any
        display: inline-block
        font-size: 12px
        border-radius: 2px
        margin-right: 8px
        padding: 2px 6px
      .__required
        color: $c--white
        background-color: $c--warning
      .__any
        background-color: $c--gray-s
    &__input
      width: 100%
      padding-left: 40px
      +max-screen($breackpoint_tb)
        margin-top: 10px
        padding-left: 0
      input,
      textarea,
      select
        &.__error
          background-color: rgba($c--warning, .1)
          border: solid 1px $c--warning
        &.__secure
          background-color: rgba($c--safe, .05)
          border: solid 1px $c--safe
    &__error-message
      color: $c--warning
      font-size: 12px
      margin-top: 4px
    &__caption
      color: $c--gray-l
      font-size: 12px
      margin-top: 4px
  &-submit
    max-width: 200px
    margin: 64px auto 0
    
.c-btn
  color: $c--white
  background-color: $c--primary
  border-radius: 2px
  padding: 16px 20px
  transition: opacity .2s
  &:hover
    opacity: 0.7
    transition: opacity .2s



// Utility
// ##############################
.u
  &-link
    color: $c--link
    text-decoration: underline
    &:hover
      text-decoration: none
  &-w100p
    width: 100% !important

$marginType: (top: ("mt", "margin-top"), left: ("ml", "margin-left"), right: ("mr", "margin-right"), bottom: ("mb", "margin-bottom"))
@each $key, $value in $marginType
  @for $i from 0 through 20
    .u-#{nth($value, 1)}#{$i * 4}
      #{nth($value, 2)}: #{$i * 4}px !important
              
            
!

JS

              
                /**
 * フォームのバリデーションを実行するクラス
 * `elmForm`, `elmTargetInputs`, `elmSubmitBtn`は必須
 *
 * 場合によっては、以下ライブラリでバリデーションするでも良いかも
 * 公式: https://imbrn.github.io/v8n/#what-s-v8n
 * 日本語解説記事: https://co.bsnws.net/article/182
 */
class FormValidator {
  /**
   * @property {Object} elmForm 【必須】form要素
   * @property {Array} elmTargetInputs 【必須】バリデーション対象となるinput要素の配列
   * @property {Object} elmSubmitBtn 【必須】送信ボタンの要素
   * @property {Array} elmFormErrorMessages フォーム全体のエラーメッセージ要素の配列
   * @property {String} classErrorInput エラーの場合、input要素に付与されるclass
   * @property {String} classSecureInput エラーが無い場合、input要素に付与されるclass
   * @property {String} attrElmErrorMessage エラーメッセージ要素をinput要素と紐付けるための属性名
   * @property {String} attrRequiredErrorMessage `required`のエラーメッセージの文言を変更するための属性名
   * @property {String} defaultErrorMessage デフォルトのエラーメッセージ
   * @property {Object} inputStatuses バリデーション対象となる全てのinput要素のオブジェクト. エラーの状態などのプロパティを持つ
   * @property {Boolean} isFirstSubmit 1回でも送信ボタンをクリックしたら`true`
   */
  constructor(_parm) {
    this.elmForm = document.querySelector(_parm.form) || false;
    this.elmTargetInputs = [
      ...this.elmForm.querySelectorAll(_parm.targetInputs)
    ];
    this.elmSubmitBtn = this.elmForm.querySelector(_parm.submitBtn);
    this.elmFormErrorMessages = [
      ...this.elmForm.querySelectorAll("[data-js-form-error-message]")
    ];
    this.classErrorInput = _parm.classErrorInput || "__error";
    this.classSecureInput = _parm.classSecureInput || "__secure";
    this.attrElmErrorMessage =
      _parm.attrElmErrorMessage || "data-js-error-message";
    this.attrRequiredErrorMessage =
      _parm.attrRequiredErrorMessage || "data-required-error";
    this.defaultErrorMessage =
      _parm.defaultErrorMessage || "必須項目を入力してください";
    const createInputStatuses = this.elmTargetInputs.map((_item) => {
      let result = [];
      result["name"] = _item.getAttribute("name");
      result["isError"] = true;
      return result;
    });
    this.inputStatuses = createInputStatuses.filter(
      (_item, _index, _self) =>
        _self.findIndex((_ev) => _ev.name === _item.name) === _index
    );
    this.isFirstSubmit = false;
  }

  /**
   * input要素が一つでもバリデーションエラーなら`true`を返す
   * @return {Boolean}
   */
  getIsFormError() {
    return this.inputStatuses.every((_item) => _item["isError"] === false)
      ? false
      : true;
  }

  /**
   * バリデーション対象となるinput要素の種類を返す
   * @param {Object} _elmInput バリデーション対象のinput要素
   * @return {String} 'checkOrRadio', 'select', 'input'
   */
  getInputType(_elmInput) {
    if (_elmInput.tagName === "SELECT") return "select";
    if (_elmInput.getAttribute("type").match(/checkbox|radio/))
      return "checkOrRadio";
    return "input";
  }

  /**
   * input要素に付与されている`required`と`pattern`でバリデーションチェックを行う
   * エラーなら`true`を返す
   * @param {Object} _elmInput バリデーション対象のinput要素
   * @returns {Boolean}
   */
  errorCheck(_elmInput) {
    const patternValidate = _elmInput.getAttribute("pattern") || false;
    const inputType = this.getInputType(_elmInput);
    // セレクトボックスの場合
    if (inputType === "select")
      return !_elmInput.validity.patternMismatch && _elmInput.value.length
        ? false
        : true;
    // チェックボックスかラジオボタンの場合
    if (inputType === "checkOrRadio") return _elmInput.checked ? false : true;
    // input(パターン有り)の場合
    if (patternValidate)
      return !_elmInput.validity.patternMismatch && _elmInput.value.length
        ? false
        : true;
    // input(パターン無し)の場合
    return _elmInput.validity.valueMissing;
  }

  /**
   * input要素に紐づくエラーメッセージ要素のテキストを描画
   * @param {Object} _elmInput バリデーション対象のinput要素
   */
  createInputErrorMessage(_elmInput) {
    const value = _elmInput.value;
    const name = _elmInput.getAttribute("name");
    const elmErrorMessage = this.elmForm.querySelector(
      `[${this.attrElmErrorMessage}="${name}"]`
    );
    const patternErrorMessage = _elmInput.getAttribute("title") || false;
    const requiredErrorMessage =
      _elmInput.getAttribute(this.attrRequiredErrorMessage) ||
      this.defaultErrorMessage;
    let errorMessage = "";
    if (patternErrorMessage) {
      errorMessage = value.length ? patternErrorMessage : requiredErrorMessage;
    } else {
      errorMessage = requiredErrorMessage;
    }
    elmErrorMessage.textContent = errorMessage;
    return;
  }

  /**
   * フォーム全体のエラーメッセージの表示を切り替える
   * @param {Boolean} _show `true`: 表示, `false`: 非表示
   */
  toggleFormErrorMessage(_show) {
    this.elmFormErrorMessages.forEach((_elmFormErrorMessage) => {
      _show
        ? (_elmFormErrorMessage.style.display = "block")
        : (_elmFormErrorMessage.style.display = "none");
    });
  }

  /**
   * input要素のエラーをリセット
   * @param {Object} _elmInput バリデーション対象のinput要素
   */
  errorReset(_elmInput) {
    const name = _elmInput.getAttribute("name");
    const elmErrorMessage = this.elmForm.querySelector(
      `[${this.attrElmErrorMessage}="${name}"]`
    );
    _elmInput.classList.remove(this.classErrorInput);
    _elmInput.classList.remove(this.classSecureInput);
    elmErrorMessage.textContent = "";
    return;
  }

  /**
   * input要素に対してバリデーションチェックやエラーメッセージの描画など、バリデーションに関する関数を全て実行する
   * @param {Object} _elmInput バリデーション対象のinput要素
   */
  validate(_elmInput) {
    // エラーリセット
    this.errorReset(_elmInput);

    // 必要な変数定義
    const isError = this.errorCheck(_elmInput);
    const targetInputStatus = this.inputStatuses.find((_item) => {
      return _item.name === _elmInput.getAttribute("name");
    });
    const changeInputStatusArray = (_isErrorValue) => {
      targetInputStatus["isError"] = _isErrorValue;
    };

    // チェックボックスかラジオボタンの場合、いずれかがチェックされていればエラーにしない
    if (this.getInputType(_elmInput) === "checkOrRadio") {
      const ElmsCheckOrRadio = [
        ...document.querySelectorAll(
          `input[name="${targetInputStatus["name"]}"]`
        )
      ];
      const getIsAnyChecked = () => {
        return ElmsCheckOrRadio.some((_elm) => {
          return _elm.checked;
        });
      };
      const toggleAllCheckOrRadioRequired = (_value) => {
        ElmsCheckOrRadio.forEach((_elm) => {
          _elm.required = _value;
        });
      };
      if (getIsAnyChecked()) {
        toggleAllCheckOrRadioRequired(false);
        changeInputStatusArray(false);
      } else {
        toggleAllCheckOrRadioRequired(true);
        changeInputStatusArray(true);
        this.createInputErrorMessage(_elmInput);
      }
      return;
    }

    // バリデーションチェックやエラーメッセージの描画などを実行
    if (isError) {
      _elmInput.classList.add(this.classErrorInput);
      changeInputStatusArray(true);
      this.createInputErrorMessage(_elmInput);
    } else {
      changeInputStatusArray(false);
      _elmInput.classList.add(this.classSecureInput);
    }
    return;
  }

  addEvent() {
    /**
     * Input
     */
    this.elmTargetInputs.forEach((_elmTargetInput) => {
      // 入力時
      _elmTargetInput.addEventListener("change", (_ev) => {
        this.validate(_elmTargetInput);
        this.isFirstSubmit
          ? this.getIsFormError()
            ? this.toggleFormErrorMessage(true)
            : this.toggleFormErrorMessage(false)
          : false;
      });
      // エラー時
      _elmTargetInput.addEventListener("invalid", (_ev) => {
        this.validate(_elmTargetInput);
      });
    });

    /**
     * SubmitBtn
     */
    this.elmSubmitBtn.addEventListener("click", (_ev) => {
      this.isFirstSubmit = true;
      this.getIsFormError()
        ? this.toggleFormErrorMessage(true)
        : this.toggleFormErrorMessage(false);
    });

    /**
     * Form
     */
    this.elmForm.addEventListener("submit", (_ev) => {
      _ev.preventDefault();
      if (!this.getIsFormError()) {
        alert("Validate OK!");
        // this.elmForm.submit();
      }
    });
  }

  init() {
    if (!this.elmForm) return;
    this.addEvent();
  }
}

window.addEventListener("DOMContentLoaded", () => {
  const formValidator = new FormValidator({
    form: "#js-form",
    targetInputs: "input[required], select[required]",
    submitBtn: 'button[type="submit"]'
  });
  formValidator.init();
});

              
            
!
999px

Console