Backbone.js Todo Example
by fangyang
HTML
<script src="https://rawgithub.com/douglascrockford/JSON-js/master/json2.js"></script>
<script src="https://rawgithub.com/jashkenas/underscore/1.5.2/underscore-min.js"></script>
<script src="https://rawgithub.com/jashkenas/backbone/1.0.0/backbone-min.js"></script>
<script src="https://rawgithub.com/jeromegn/Backbone.localStorage/v1.1.6/backbone.localStorage.js"></script>
<div id="todoapp">
<header>
<h1>Todos by Backbone.js</h1>
<input id="new-todo" type="text" placeholder="What needs to be done?"/>
</header>
<section id="main" style="display: block;">
<input id="toggle-all" type="checkbox"/>
<label for="toggle-all">Mark all as complete</label>
<ul id="todo-list"></ul>
</section>
<footer style="display: block;">
<div class="todo-count"><b>2</b> items left</div>
</footer>
</div>
<script type="text/template" id="item-template">
<div class="view">
<input class="toggle" type="checkbox" <%= done ? 'checked="checked"' : '' %> />
<label><%- title %></label>
<a class="destroy"></a>
</div>
<input class="edit" type="text" value="<%- title %>" />
</script>
<script type="text/template" id="stats-template">
<% if (done) { %>
<a id="clear-completed">Clear <%= done %> completed <%= done == 1 ? 'item' : 'items' %></a>
<% } %>
<div class="todo-count"><b><%= remaining %></b> <%= remaining == 1 ? 'item' : 'items' %> left</div>
</script>
CSS
body {
font-family: helvetica;
font-size: 14px;
background: #eeeeee;
color: #333333;
width: 520px;
margin: 0 auto;
}
#todoapp {
background: #fff;
padding: 20px;
margin-bottom: 40px;
}
#todoapp h1 {
font-size: 36px;
text-align: center;
}
#todoapp input[type="text"] {
width: 466px;
font-size: 24px;
line-height: 1.4em;
padding: 6px;
}
#main {
display: none;
}
#todo-list {
margin: 10px 0;
padding: 0;
list-style: none;
}
#todo-list li {
padding: 18px 20px 18px 0;
position: relative;
font-size: 24px;
border-bottom: 1px solid #cccccc;
}
#todo-list li:last-child {
border-bottom: none;
}
#todo-list li.done label {
color: #777777;
text-decoration: line-through;
}
#todo-list li .edit {
display: none;
}
#todo-list li.editing {
border-bottom: 1px solid #778899;
}
#todo-list li.editing .view {
display: none;
}
#todo-list li.editing .edit {
display: block;
width: 444px;
padding: 13px 15px 14px 20px;
margin: 0;
}
#todo-list li.done label {
color: #777777;
text-decoration: line-through;
}
#todo-list .destroy {
position: absolute;
right: 5px;
top: 20px;
display: none;
cursor: pointer;
width: 20px;
height: 20px;
}
#todoapp footer {
display: none;
margin: 0 -20px -20px -20px;
overflow: hidden;
color: #555555;
background: #f4fce8;
border-top: 1px solid #ededed;
padding: 0 20px;
line-height: 37px;
}
#clear-completed {
float: right;
line-height: 20px;
text-decoration: none;
background: rgba(0, 0, 0, 0.1);
color: #555555;
font-size: 11px;
margin-top: 8px;
margin-bottom: 8px;
padding: 0 10px 1px;
cursor: pointer;
}
JavaScript
//see detail -> official example
//http://backbonejs.org/docs/todos.html
//http://backbonejs.org/examples/todos/index.html
$(function(){
//Define Model
var Todo = Backbone.Model.extend({
defaults: function() {
return {
title: "no title...",
order: Todos.nextOrder(),
done: false
};
},
toggle: function() {
this.save({done: !this.get("done")});
}
});
//Model Collection
var TodoList = Backbone.Collection.extend({
model: Todo,
localStorage: new Backbone.LocalStorage("todos-backbone"),
done: function() {
return this.where({done: true});
},
remaining: function() {
return this.without.apply(this, this.done());
},
nextOrder: function() {
if (!this.length) return 1;
return this.last().get("order") + 1;
},
comparator: 'order'
});
var Todos = new TodoList;
//Model View & event action
var TodoView = Backbone.View.extend({
tagName: "li",
template: _.template($("#item-template").html()),
events: {
"click .toggle" : "toggleDone",
"dblclick .view" : "edit",
"click a.destroy" : "clear",
"keypress .edit" : "updateOnEnter",
"blur .edit" : "close"
},
initialize: function() {
this.listenTo(this.model, "change", this.render);
this.listenTo(this.model, "destroy", this.remove);
},
render: function() {
this.$el.html(this.template(this.model.toJSON()));
this.$el.toggleClass("done", this.model.get("done"));
this.input = this.$(".edit");
return this;
},
toggleDone: function() {
this.model.toggle();
},
edit: function() {
this.$el.addClass("editing");
this.input.focus();
},
close: function() {
var value = this.input.val();
if (!value) {
this.clear();
} else {
this.model.save({title: value});
this.$el.removeClass("editing");
}
},
updateOnEnter: function(e) {
if (e.keyCode == 13) this.close();
},
clear:...