javascript中級者になりたいデザイナーが、リアルタイムvalidate自作してみた

フォームをバリデーションするのにオススメのjQueryプラグイン 9選

「ブレイクスルーJavaScript フロントエンドエンジニアとして越えるべき5つの壁―オブジェクト指向からシングルページアプリケーションまで」を読みながらというかほぼ参考にさせてもらいながら勉強のためにvalidateを作ってみました。勉強のために作ったものなので中上級者のかたには、たいしたことの無いサンプルかもしれないんですが・・・

こんなvalidateを作ってみたい

本に書かれているvalidateもいい感じだったんですけど、若干実用性を考えて

・エラー文言はjsに文言だけ追記すればOK
・validateする範囲をしぼりたい
・submit時にも必須項目のvalidateをしたい

っていうのがカスタマイズの基本方針でした。

まずは土台となるHTML

クラスjs-validateと書かれているところだけ、validateが動くようにしました。あとはそれぞれvalidateの種類によってそれぞれクラスを付与します。あとは、submit時にもvalidateが動くようにカスタマイズもしてあります。

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8">
    <title>realtime-validation</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css">
    <link rel="stylesheet" href="styles/style.css">
  </head>
  <body>
    <div class="container">
      <div class="page-header">
        <h1>オブジェクト指向</h1>
      </div>
      <form class="js-form" method="post" action="">
          <div class="row">
            <div class="col">
              <label for="">必須項目</label>
            </div>
            <div class="col">
              <input type="text" class="js-validate js-validate-required">
              <p class="err"></p>
            </div>
          </div>

          <div class="row">
            <div class="col">
              <label for="">4文字以上8文字以下</label>
            </div>
            <div class="col">
              <input type="text" class="js-validate js-validate-maxlength js-validate-minlength" placeholder="4文字以上8文字以内で入力してください">
              <p class="err"></p>
            </div>
          </div>

          <div class="row">
            <div class="col">
              <label for="">数字のみ</label>
            </div>
            <div class="col">
              <input type="text" class="js-validate js-validate-number" placeholder="数字のみ">
              <p class="err"></p>
            </div>
          </div>

          <div class="row">
            <div class="col">
              <label for="">url</label>
            </div>
            <div class="col">
              <input type="text" class="js-validate js-validate-url" placeholder="url入力">
              <p class="err"></p>
            </div>
          </div>

          <div class="row">
            <div class="col">
              <label for="">メールアドレス</label>
            </div>
            <div class="col">
              <input type="text" class="js-validate js-validate-mail" placeholder="メールアドレス入力">
              <p class="err"></p>
            </div>
          </div>

          <div class="row">
              <button type="button" class="js-submit btn btn-default">送信</button>
          </div>
      </form>
      <script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
      <script src="scripts/common.js"></script>
    </div>
  </body>
</html>

まずは土台となるHTML

一応cssも。基本的にbootstrapでやるんですけど。ちょっとした微調整とエラーのスタイルを書き加えています。あとはis-hiddenで表示、非表示を切り替える感じですね。

ul {
  margin: 0;
  padding: 0;
  list-style: none;
}
#container {
  padding: 40px;
}
h1 {
  font-size: 16px;
  font-weight: bold;
}
.row {
  margin-top: 2em;
  display: table;
  width: 100%;
}
.row .col {
  display: table-cell;
}
.row .col:first-child {
  width: 200px;
}
.row .col label {
  font-weight: bold;
}
.row .col input {
  font-size: 100%;
  outline: none;
  border: none;
  border-bottom: 1px solid #9e9e9e;
  padding: 0.5em 0;
  min-width: 400px;
}
.row .col input.error {
  border-color: #e51c23;
}
.row .col ul {
  margin-top: 0.5em;
}
.row .col li {
  display: none;
  color: #e51c23;
  background: url("../images/error.png") no-repeat left center;
  -webkit-background-size: 12px 12px;
  -moz-background-size: 12px 12px;
  background-size: 12px 12px;
  padding-left: 20px;
  margin-top: 0.5em;
}
.row .col li:first-child {
  margin-top: 0;
}

.errText{
    color: #e51c23;
    background: url("../images/error.png") no-repeat left center;
    -webkit-background-size: 12px 12px;
    -moz-background-size: 12px 12px;
    background-size: 12px 12px;
    padding-left: 20px;
    margin-top: 0.5em;
}

.errText.is-hidden{
    display:none;
}

本題のjavascript

基本書籍を参考にしたので、気になる方は読んでみるのが一番理解しやすいと思います。が、一応カスタマイズもしてあるので。
メールアドレスやURLなどvalidateしたいものの正規表現、エラー文言を追加できるようにしました。あとはsubmit時に必須項目が抜けている場合にエラーがでるようにした感じです。

function AppModel(attrs, elem){
    this.val = '';
    this.elem = elem;

    this.listeners = {
        valid: [],
        invalid: []
    };

    this.attrs = {
        required: '',
        maxlength: 8,
        minlength: 4,
        number: /^[-]?[0-9]+(¥.[0-9]+)?$/,
        url: /^(https?|ftp)(:\/\/[-_.!~*¥'()a-zA-Z0-9;¥/?:¥@&=+¥$,%#]+)$/,
        mail: /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/
    };

    this.message = {
        required: '必須項目です',
        maxlength: this.attrs.maxlength + '文字以下にしてください',
        minlength: this.attrs.minlength + '文字以上入力してください',
        number: '半角数字で入力してください',
        url: 'URLが正しくありません',
        mail: 'メールアドレスが正しくありません'
    };

}

AppModel.prototype.set = function(val){
    this.val = val;
    this.validate();
};

AppModel.prototype.validate = function(){
    var val;

    // バリデーションでエラーが出たものを保存しておく配列を用意
    this.errors = [];

    for(var key in this.attrs){
        val = this.attrs[key];
        if(this[key](val)) return;
    }

    this.trigger(!this.errors.length ? 'valid' : 'invalid');
};

AppModel.prototype.required = function(){
    var key = 'required';

    if(this.elem.hasClass('js-validate-' + key)){
        if(this.val ===''){
            this.errors.push(key);
        }
    }
};

AppModel.prototype.maxlength = function(rule){
    var key = 'maxlength';

    if(this.val === '') return;

    if(this.elem.hasClass('js-validate-' + key)){

        if(rule < this.val.length){
            this.errors.push(key);
        }
    }
};

AppModel.prototype.minlength = function(rule){
    var key = 'minlength';

    if(this.val === '') return;

    if(this.elem.hasClass('js-validate-' + key)){

        if(rule >= this.val.length){
            this.errors.push(key);
        }
    }
};

AppModel.prototype.number = function(rule){
    var key = 'number';

    if(this.val === '') return;

    if(this.elem.hasClass('js-validate-' + key)){

        if(!this.val.match(rule)){
            this.errors.push(key);
        }
    }
};

AppModel.prototype.url = function(rule){
    var key = 'url';

    if(this.val === '') return;

    if(this.elem.hasClass('js-validate-' + key)){

        if(!this.val.match(rule)){
            this.errors.push(key);
        }
    }
};

AppModel.prototype.mail = function(rule){
    var key = 'mail';

    if(this.val === '') return;

    if(this.elem.hasClass('js-validate-' + key)){

        if(!this.val.match(rule)){
            this.errors.push(key);
        }
    }
};

AppModel.prototype.on = function(event, func) {
    this.listeners[event].push(func);
};

AppModel.prototype.trigger = function(event){
    $.each(this.listeners[event], function(){
        this();
    });
};

function AppView (el){

    // 初期化
    this.initialize(el);

    // イベント
    this.handleEvents();
}

AppView.prototype.initialize = function(el){
    this.$el = $(el);
    this.$list = this.$el.next();
    this.$submit = $('.js-submit');
    this.$form = $('.js-form');
    var obj = this.$el.data();

    if(this.$el.hasClass('required')){
        obj["required"] = "";
    }


    this.model = new AppModel(obj, this.$el);

};

AppView.prototype.handleEvents = function(){
    var self = this;
    this.$el.on('keyup', function(e){
        self.onKeyup(e);
        console.log(self);
    });

    this.$submit.on('click', function(){
        self.onClick();
    });

    this.model.on('valid', function(){
        self.onValid();
    });

    this.model.on('invalid', function(){
        self.onInvalid();
    });
};

AppView.prototype.onKeyup = function(e){
    var $target = $(e.currentTarget);
    this.model.set($target.val());
};

AppView.prototype.onClick = function(){
    var $target = this.$el;

    if($target.hasClass('js-validate-required')){
        this.model.set($target.val());
    }

};

AppView.prototype.onValid = function(){
    var $targetForm = this.$form;

    this.$el.removeClass('error');
    this.$list.find('.errText').remove();

    this.$submit.click(function(){
        $targetForm.submit();
    });
};

AppView.prototype.onInvalid = function(){

    var self = this;
    self.message = this.model.message;

    this.$el.addClass('error');
    this.$list.find('.errText').remove();

    $.each(this.model.errors, function(index, val){
        var elem = self.message[val];
        self.$list.append('<p class="errText">' + elem + '</p>');
    });
};

$('.js-validate').each(function(){
    new AppView(this);
});

作ってみたけど、もうちょっと改良できそうだなー。今後、勉強してもっといいリアルタイムvalidateにしたいですね。

参考にした書籍


ブレイクスルーJavaScript フロントエンドエンジニアとして越えるべき5つの壁―オブジェクト指向からシングルページアプリケーションまで

この本、実用的なサンプルが多くて非常に勉強になりました。しかも、保守しやすい拡張性があるコードを目指そうというのが前提に進められているので、今回のvalidateのカスタマイズもやりやすかったです。

jQueryである程度動きを作れたり、基本的なjavascriptの理解は最低限必要ではないかなと思います。もっと言うとjQueryでいろいろ出来るようになったけど、ステップアップとして何から手を付けたらいいのかわからないっていうかたにおすすめかも。

自分もそんな感じの一人でした。jQuery、javascript基本的なことは出来るけど、それじゃーなー。と思っていました。技術書も一通り読んで勉強したけど結構難しくて、完璧に理解できた訳じゃないなと。

ブレイクスルーJavaScriptでは初級者からの脱皮を考えている人にこんなこと勉強するといいよっていう指標を出してくれる本かなと思います。

ざっくり内容を紹介すると
・thisの使い方
・オブザーバパターン
・クロージャ
・プロトタイプ

これらを実際のコードを書きながら進めていく感じになります。正直、説明はそんなに詳しくは書かれていないです。なので、わからないものは自分で調べながら進めていくとより理解が深まると思います。

オススメの本



作りながら学ぶjQueryデザインの教科書

jQueryを作って学ぶのにオススメの本です。「アコーディオン」「ドロップダウンメニュー」や「モーダルウィンドウ」などよく使うUIを作りながらjQueryの基本を学べます。

イチマルニデザインブログをフォローしよう

イチマルニデザインブログではTwitterアカウントでWebに関する情報をつぶやいています。フォローすることで最新情報をすぐに受け取ることができます

いいなと思ったらシェアお願いします

同じカテゴリーの記事

ページの先頭へ