function ItemList() {
this.root = $('#container');
this.snipet = $('#snipet').html();
}
ItemList.prototype.build = function () {
this.bindAllListener();
};
ItemList.prototype.bindAllListener = function () {
this.root.on('click', 'button.item-add', $.proxy(this.add, this));
this.root.on('click', '.item-remove-anchor', $.proxy(this.remove, this));
};
ItemList.prototype.add = function (evt) {
evt.preventDefault();
var template = _.template(this.snipet);
this.root.find('#item-list').append(template);
};
ItemList.prototype.remove = function (evt) {
evt.preventDefault();
var target = $(evt.currentTarget),
item = target.closest('.item-raw');
item.remove();
};
var itemList = new ItemList();
itemList.build();
<script type="text/template" id="snipet">
<div class="item-raw clearfix">
<p class="item-input">
item: <input type="text">
</p>
<p class="item-remove">
<a href="#" class="item-remove-anchor">x</a>
</p>
</div>
</script>
<div id="container">
<div id="item-list">
<div class="item-raw item-first clearfix">
<p class="item-input">
item: <input type="text">
</p>
</div>
</div>
<button class="item-add">add</button>
</div>
.item-first{
border-top: 6px solid #069;
counter-reset:item;
}
.item-raw{
border-left: 6px solid #069;
border-right: 6px solid #069;
border-bottom: 6px solid #069;
padding: 5px;
height: 2em;
line-height: 2em;
}
.item-input:before {
counter-increment: item;
content: counter(item) ") ";
}
.item-input {float: left;}
.item-remove{float: right;}
.item-remove-anchor{
text-decoration: none;
display: block;/*はてなブログで押しづらかったので*/
width: 20px;
text-align: center;
}
.clearfix:before,
.clearfix:after {
content: " ";
display: table;
}
.clearfix:after {
clear: both;
}
External resources loaded into this fiddle: