Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URLs added here will be added as <link>s in order, and before the CSS in the editor. You can use the CSS from another Pen by using its URL and the proper URL extension.

+ add another resource

JavaScript

Babel includes JSX processing.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

Auto Save

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                <h1>Todo</h1>

<form id="todo-form">
	<label for="todo-item">What do you need to do?</label>
	<input type="text" name="todo-item" id="todo-item">
	<button class="btn" id="add-todo">Add Todo</button>
</form>

<div id="app"></div>
              
            
!

CSS

              
                /**
 * Add box sizing to everything
 * @link http://www.paulirish.com/2012/box-sizing-border-box-ftw/
 */
*,
*:before,
*:after {
	box-sizing: border-box;
}

/**
 * Layout
 */
body {
	font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
	font-size: 112.5%;
	margin-left: auto;
	margin-right: auto;
	max-width: 40em;
	width: 88%;
}

/**
 * Forms
 */

label,
[type="text"] {
	display: block;
	width: 100%;
}

label {
	margin-bottom: 0.25em;
}

[type="text"] {
	font-size: inherit;
	margin-bottom: 1em;
	padding: 0.25em 1em;
}

[type="checkbox"] {
	margin-right: 0.5em;
}

/**
 * Buttons
 */

.btn {
	background-color: #0088cc;
	border: 1px solid #0088cc;
	border-radius: 1px;
	color: #ffffff;
	display: inline-block;
	font-size: 0.9375em;
	font-weight: normal;
	line-height: 1.2;
	margin-right: 0.3125em;
	margin-bottom: 0.3125em;
	padding: 0.5em 0.6875em;
}

.btn:hover,
.btn:focus,
.btn:active {
	background-color: #005580;
	border-color: #005580;
	color: #ffffff;
	text-decoration: none;
}

/**
 * Todos
 */

.todos {
	list-style: none;
	margin-left: 0;
	padding-left: 0;
}

.todos .completed {
	text-decoration: line-through;
}

.todo:before,
.todo:after {
	display: table;
	content: " ";
}

.todo:after {
	clear: both;
}

.todo label {
	width: auto;
}

.todo label,
.todo button {
	float: left;
}

.todo button {
	margin-left: 0.5em;
}
              
            
!

JS

              
                //
// Variables
//

var todoField = document.querySelector('#todo-item');
var todo;


//
// Methods
//

/**
 * Save todo items to local storage
 */
var saveTodos = function () {
	localStorage.setItem('todos', JSON.stringify(todo.data.todos));
};

/**
 * Get todo items from local storage
 * @return {Array} The todo items (or an empty array if none exist)
 */
var getTodos = function () {
	var todos = localStorage.getItem('todos');
	if (todos) return JSON.parse(todos);
	return [];
};

/**
 * Setup the initial todo list container
 */
var setup = function () {

	// Create the todo list
	todo = new Reef('#app', {
		data: {
			todos: getTodos()
		},
		template: function (props) {

			// Create each todo item
			var template = props.todos.map(function (todo, index) {

				// If it's being edited, show a form
				// Otherwise, show the item with a checkbox
				if (todo.edit) {
					return `
						<li class="todo">
							<form class="todo-edit-form">
								<input type="text" class="todo-update" value="${todo.item}" data-todo-edit="${index}">
								<button>Save</button>
							</form>
						</li>`;
				} else {
					return `
						<li class="todo">
							<label ${todo.completed ? ' class="completed"' : ''}>
								<input data-todo="${index}" type="checkbox" ${todo.completed ? ' checked="checked"' : ''}>
								<span class="todo-item">${todo.item}</span>
							</label>
							<button class="todo-edit">Edit</button>
							<button class="todo-delete">Delete</button>
						</li>`;
				}

			}).join('');

			// If there are todo items, wrap it in an unordered list
			if (template.length > 0) {
				return `<ul class="todos">${template}</ul><p><button class="todo-clear">Clear All Todos</button></p>`;
			}

			return '';

		}
	});

	// Render the todo list
	todo.render();

};

/**
 * Add a todo to the list
 */
var addTodo = function (event) {

	// Only run if there's an item to add
	if (todoField.value.length < 1) return;

	// Prevent default form submission
	event.preventDefault();

	// Update the state
	todo.data.todos.push({
		item: todoField.value,
		completed: false
	});

	// Clear the input field and return to focus
	todoField.value = '';
	todoField.focus();

};

/**
 * Mark a todo as complete (or incomplete)
 * @param  {Node} item  The todo item
 */
var completeTodo = function (item) {

	// Get the todo item
	var todoItem = todo.data.todos[item.getAttribute('data-todo')];
	if (!todoItem) return;

	// If it's completed, uncomplete it
	// Otherwise, mark is as complete
	if (todoItem.completed) {
		todoItem.completed = false;
	} else {
		todoItem.completed = true;
	}

};

/**
 * Delete a todo list item
 * @param  {Node} btn The delete button that was clicked
 */
var deleteTodo = function (btn) {

	// Get the index for the todo list item
	var index = btn.closest('.todo').querySelector('input').getAttribute('data-todo');
	if (!index) return;

	// Remove the item from state
	todo.data.todos.splice(index, 1);

};

/**
 * Edit a todo list item
 * @param  {Node} btn The edit button that was clicked
 */
var editTodo = function (btn) {

	// Get the todo list DOM element
	var todoListItem = btn.closest('.todo');
	if (!todoListItem) return;

	// Get the index for the item
	var index = todoListItem.querySelector('input').getAttribute('data-todo');
	if (!index) return;

	// Get the item from state
	var todoItem = todo.data.todos[index];
	if (!todoItem) return;

	// Update state
	todoItem.edit = true;

};

var saveEditTodo = function (event) {

	// Prevent default form submit
	event.preventDefault();

	// Get the todo list DOM node
	var newTodo = event.target.querySelector('.todo-update');
	if (!newTodo) return;

	// Get the todo list item index
	var index = newTodo.getAttribute('data-todo-edit');
	if (!index) return;

	// Get the item from state
	var todoItem = todo.data.todos[index];
	if (!todoItem) return;

	// If the item is empty, delete the todo item
	// Otherwise, update it
	if (newTodo.value.length < 1) {
		todo.data.todos.splice(index, 1);
	} else {
		todoItem.item = newTodo.value;
		todoItem.edit = false;
	}

};

/**
 * Remove all todo items from state
 */
var clearTodos = function () {
	todo.data.todos = [];
};

/**
 * Handle click events
 */
var clickHandler = function (event) {

	// Complete todos
	var todo = event.target.closest('[data-todo]');
	if (todo) {
		completeTodo(todo);
	};

	// Edit todo
	var editBtn = event.target.closest('.todo-edit');
	if (editBtn) {
		editTodo(editBtn);
	}

	// Delete todo
	var deleteBtn = event.target.closest('.todo-delete');
	if (deleteBtn) {
		deleteTodo(deleteBtn);
	}

	// Clear all todos
	if (event.target.closest('.todo-clear')) {
		clearTodos();
	}
};

/**
 * Handle form submit events
 */
var submitHandler = function (event) {

	// If it's the "new todo" form, add the item
	if (event.target.matches('#todo-form')) {
		addTodo(event);
	}

	// If it's the "edit todo" form, save the edit
	if (event.target.matches('.todo-edit-form')) {
		saveEditTodo(event);
	}
};

/**
 * Handle render events
 */
var renderHandler = function (event) {

	// If the rendered element is not the #app, bail
	if (!event.target.matches('#app')) return;

	// Save the current state to localStorage
	saveTodos();

};


//
// Inits & Event Listeners
//

// Setup the DOM
setup();

// Listen for events
document.addEventListener('submit', submitHandler, false);
document.addEventListener('click', clickHandler, false);
document.addEventListener('render', renderHandler, false);
              
            
!
999px

Console