Skip to content

Instantly share code, notes, and snippets.

@sebjwallace
Last active April 8, 2016 14:58
Show Gist options
  • Save sebjwallace/0c5537cb487040d5e0b216623c140fe9 to your computer and use it in GitHub Desktop.
Save sebjwallace/0c5537cb487040d5e0b216623c140fe9 to your computer and use it in GitHub Desktop.
virtual-dom todos <100 LOC

This was a small challenge to write a standard and unstyled todos app within 100 lines of code, while maintaining readability.

It can be found on JS Bin

This project was to also prove that components are nothing more than a function that returns a vTree. The only purpose of creating an instance of the component is to maintain internal state. If there is none, then a component can just be a pure function.

Although a trivial application, it might also demonstrate that such things can be built without frameworks or virtual-dom libraries - only the virtual-dom is needed.

I did use my own fork from the virtual-dom repo for this project so that I could use the vDOM object.

<!DOCTYPE html>
<html>
<head>
<script src="https://gitcdn.xyz/repo/sebjwallace/virtual-dom/master/dist/virtual-dom.js"></script>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
</body>
</html>
// https://jsbin.com/cukiruciqu/edit?js,output
var e = virtualDom.h;
var DOM = new virtualDom.vDOM();
function TodosModel(callback){
_todos = [];
return {
get: function(){
return _todos;
},
add: function(label){
_todos.push( {label: label, complete: false} );
callback(this);
},
remove: function(id){
_todos.splice(id,1);
callback(this);
},
edit: function(id,label){
_todos[id].label = label;
callback(this);
},
toggle: function(id){
_todos[id].complete = !_todos[id].complete;
callback(this);
}
};
}
function TodosView(render){
var editing = null;
function update(todos){
render(component(todos));
}
var component = function(todos){
function isEditing(todo){
if(editing == todo) return 'input';
else return 'label';
}
function isComplete(todo){
if(todo.complete)
return {'text-decoration': 'line-through'};
}
function addTodo(e){
if(e.keyCode != 13) return;
todos.add(e.target.value);
}
return e('#todos', [
e('h1', 'Todos'),
e('input', {
onkeypress: addTodo,
autofocus: true,
value: '',
placeholder: 'what needs to be done?'
}),
e('ul', todos.get().map(function(todo,i){
return e('li', [
e(isEditing(todo), {
onclick: function(){ editing = todo; update(todos); },
onblur: function(){ editing = null; update(todos); },
style: isComplete(todo),
value: todo.label
}, todo.label),
e('button', {onclick: todos.toggle.bind(todos,i)}, 'Done'),
e('button', {onclick: todos.remove.bind(todos,i) }, 'X')
]);
}))
]);
};
return component;
}
var todosComponent = new TodosView(DOM.render);
var todos = new TodosModel(
function(newTodos){ DOM.render( todosComponent(newTodos) ); }
);
todos.add('add todos');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment