BNjqaK
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title></title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
</head>
<body>
<div class="l-container">
<div class="row">
<div class="col">
<label for="">アカウント作成</label>
</div>
<div class="col">
<input type="text" placeholder="4文字以上8文字以内で入力" data-minlength="4" data-maxlength="8" required>
<ul>
<li data-error="required">必須です</li>
<li data-error="minlength">4文字以上で入力</li>
<li data-error="maxlength">8文字以内で入力</li>
</ul>
</div>
</div>
<div class="row">
<div class="col">
<label for="">パスワードを作成</label>
</div>
<div class="col">
<input type="text" placeholder="4文字以上8文字以内で入力" data-minlength="4" data-maxlength="8" required>
<ul>
<li data-error="required">必須です</li>
<li data-error="minlength">4文字以上で入力</li>
<li data-error="maxlength">8文字以内で入力</li>
</ul>
</div>
</div>
</div>
</body>
</html>
/*
* Model
*/
function AppModel(attrs){
this.val = "";
this.attrs = {
required: "",
maxlength: 8,
minlength: 4
};
this.listeners = {
valid: [],
invalid : []
};
};
AppModel.prototype.on = function(event, func){
this.listeners[event].push(func);
};
AppModel.prototype.trigger = function(event){
$.each(this.listeners[event], function(){
this();
});
};
AppModel.prototype.set = function(val){
if (this.val === val) return;
this.val = val;
this.validate();
};
AppModel.prototype.required = function(){
return this.val !== "";
};
AppModel.prototype.maxlength = function(num){
return num >= this.val.length;
};
AppModel.prototype.minlength = function(num){
return num <= this.val.length;
};
AppModel.prototype.validate = function(){
var val;
this.errors = [];
for (var key in this.attrs) {
if(!this[key](val)) this.errors.push(key);
}
this.trigger(!this.errors.length ? "valid" : "invalid");
};
/*
* View
*/
function AppView(el){
this.initialize(el);
this.handleEvents();
};
AppView.prototype.initialize = function(el){
this.$el = $(el);
this.$list = this.$el.next().children();
var obj = this.$el.data();
if (this.$el.prop("required")){
obj["required"] = "";
}
this.model = new AppModel(obj);
};
AppView.prototype.handleEvents = function(){
var self = this;
this.$el.on("keyup", function(e){
self.onKeyup(e);
});
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.onValid = function(){
this.$el.removeClass("error");
this.$list.hide();
};
AppView.prototype.onInvalid = function(){
var self = this;
this.$el.removeClass("error");
this.$list.hide();
$.each(this.model.errors, function(index, val) {
self.$list.filter("[data-error=\"" + val + "\"]").show();
});
};
$("input").each(function(){
new AppView(this);
})
ul{
margin:0;
padding:0;
list-style: none;
}
.l-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 #999;
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;
margin-top: 0.5em;
}
.row .col li:first-child {
margin-top:0;
}