JSFiddle - React, Tailwind, and code Playground
HTML
<div>
<input type="text" id="todoInput"/>
</div>
<div>
<ul id="todoList"></ul>
</div>
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.7.0/underscore.js"></script>
CSS
.completed {
text-decoration:line-through;
}
JavaScript
(function(_, exports) {
'use strict';
exports.events = function() {
var events = {};
return {
on: function(name, func) {
(events[name] || (events[name] = []))
.push(func);
return this;
},
trigger: function(name /*, ...args */ ) {
var funcs = events[name] || [];
var args = _.rest(arguments);
funcs.forEach(function(func) {
setTimeout(function() {
func.apply(null, args);
}, 0);
});
return this;
}
};
};
})(this._, this.util = this.util || {});
(function(_, util, exports) {
'use strict';
// Model
var Todo = _.extend(function(id, description) {
this.id = id;
this.description = description;
this.complete = false;
}, util.events());
Todo.prototype.done = function() {
this.complete = true;
Todo.trigger('change', this);
};
// Collection
var TodoList = _.extend([], util.events());
TodoList.add = function(todo) {
this.push(todo);
this.trigger('change', TodoList);
};
// export
exports.Todo = Todo;
exports.TodoList = TodoList;
})(this._, this.util, this.todo = this.todo || {});
(function(_, util, exports) {
'use strict';
var Todo = exports.Todo;
var TodoList = exports.TodoList;
var input = window.document.getElementById('todoInput');
var ul = window.document.getElementById('todoList');
var View = _.extend({}, util.events());
var todoHtml = _.template('<li id="todo_<%= id %>" <% if(complete) { %>class="completed" <% }%>><%= description %></li>');
function render(todoList) {
ul.innerHTML = todoList.reduce(function(html, todo) {
return html + todoHtml(todo);
}, '');
}
function complete(todo) {
var target = window.document.getElementById('todo_' + todo.id);
target.className = todo.complete ? 'completed' : '';
}
function clearInput() {
input.value = '';
}
View.clearInput = clearInput; // export
Todo.on('change',...