Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

+ add another resource

JavaScript

Babel is required to process package imports. If you need a different preprocessor remove all packages first.

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

Behavior

Save Automatically?

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

              
                <div id="todoapp"></div>
<footer class="info">
  <p>Double-click to edit a todo</p>
  <p>Template by <a href="http://sindresorhus.com">Sindre Sorhus</a></p>
  <p>Created by <a href="http://github.com/Cweili">Cweili</a></p>
  <p>Base On Tech <a href="https://github.com/stasm/innerself">Innerself</a></p>
</footer>
              
            
!

CSS

              
                
              
            
!

JS

              
                /**
 * Innerself
 * https://github.com/stasm/innerself/blob/f755b172e393726f922207fb5c08ab6deae8c67c/index.js
 */
function html([first, ...strings], ...values) {
  // Weave the literal strings and the interpolations.
  // We don't have to explicitly handle array-typed values
  // because concat will spread them flat for us.
  return values.reduce(
    (acc, cur) => acc.concat(cur, strings.shift()),
    [first]
  )

  // Filter out interpolations which are bools, null or undefined.
  .filter(x => x && x !== true || x === 0)
  .join("");
}

function createStore(reducer) {
  let state = reducer();
  const roots = new Map();
  const prevs = new Map();

  function render() {
    for (const [root, component] of roots) {
      const output = component();

      // Poor man's Virtual DOM implementation :)  Compare the new output
      // with the last output for this root.  Don't trust the current
      // value of root.innerHTML as it may have been changed by other
      // scripts or extensions.
      if (output !== prevs.get(root)) {
        prevs.set(root, output);
        root.innerHTML = output;

        // Dispatch an event on the root to give developers a chance to
        // do some housekeeping after the whole DOM is replaced under
        // the root. You can re-focus elements in the listener to this
        // event. See example03.
        const event = new CustomEvent("render", { detail: state });
        root.dispatchEvent(event);
      }
    }
  };

  return {
    attach(component, root) {
      roots.set(root, component);
      render();
    },
    connect(component) {
      // Return a decorated component function.
      return (...args) => component(state, ...args);
    },
    dispatch(action, ...args) {
      state = reducer(state, action, args);
      render();
    },
  };
}

/**
 * TodoMVC Innerself
 */
const todoStorage = {
  set(todos) {
    window.localStorage.setItem('todos', JSON.stringify(todos));
  },
  get() {
    const todos = window.localStorage.getItem('todos');
    return todos ? JSON.parse(todos) : [];
  }
};

const init = {
  todos: todoStorage.get(),
  editedTodo: null,
  filter: 'all',
  filters: {
    all: () => true,
    active: todo => !todo.completed,
    completed: todo => todo.completed
  }
};

const actions = {
  add(state, newTodo) {
    if (newTodo && newTodo.trim()) {
      state.todos.push({
        title: newTodo.trim(),
        completed: false
      });
      todoStorage.set(state.todos);
    }
  },
  toggle({ todos }, index) {
    const todo = todos[index];
    todo.completed = !todo.completed;
    todoStorage.set(todos);
  },
  editStart(state, index) {
    state.editedTodo = state.todos[index];
  },
  editEnd(state, title) {
    const {
      editedTodo,
      todos
    } = state;
    if (editedTodo) {
      editedTodo.title = title.trim();
      if (!editedTodo.title) {
        todos.splice(todos.indexOf(editedTodo), 1);
      }
      state.editedTodo = null;
      todoStorage.set(todos);
    }
  },
  editCancel(state) {
    state.editedTodo = null;
  },
  remove({ todos }, index) {
    todos.splice(index, 1);
    todoStorage.set(todos);
  },
  toggleAll({ todos }, completed) {
    todos.forEach(todo => {
      todo.completed = completed;
    });
    todoStorage.set(todos);
  },
  clearCompleted({ todos }) {
    todos
      .map((todo, index) => todo.completed && index)
      .filter(index => index !== false)
      .reverse()
      .map(index => actions.remove({ todos }, index))
      .length;
  },
  switchFilter(state, filter) {
    state.filter = filter;
  }
};

function reducer(state = init, action, args) {
  actions[action] && actions[action](state, ...args);
  return state;
}

const {
  attach,
  connect,
  dispatch
} = createStore(reducer);

const TodoItem = connect((
  { editedTodo },
  todo,
  index
) => html`
  <li
    class="${todo.completed && 'completed'} ${todo == editedTodo && 'editing'}">
    <div class="view">
      <input
        class="toggle"
        type="checkbox"
        ${todo.completed && 'checked'}
        onchange="dispatch('toggle', ${index})"/>
      <label ondblclick="dispatch('editStart', ${index})">
        ${todo.title}
      </label>
      <button
        class="destroy"
        onclick="dispatch('remove', ${index})">
      </button>
    </div>
    <input
      class="edit"
      type="text"
      value="${todo.title}"
      onkeyup="event.keyCode == 13 && dispatch('editEnd', this.value) || event.keyCode == 27 && dispatch('editCancel')"
      onblur="dispatch('editEnd', this.value)"/>
  </li>
`);

const Header = connect(() => html`
  <header class="header">
    <h1>todos</h1>
    <input
      class="new-todo"
      autocomplete="off"
      placeholder="What needs to be done?"
      onkeyup="event.keyCode == 13 && dispatch('add', this.value)"
      autofocus/>
  </header>
`);

const Footer = connect(({
  todos,
  filter,
  filters
}) => html`
  <footer class="footer">
    <span class="todo-count">
      <strong>${
        todos
          .filter(filters.active)
          .length
      }</strong> item${
        todos
          .filter(filters.active)
          .length > 1 && 's'
      } left
    </span>
    <ul class="filters">
      ${
         Object.keys(filters).map(f => html`
          <li>
            <a
              href="###"
              class="${filter == f && 'selected'}"
              onclick="dispatch('switchFilter', '${f}')">
              ${f[0].toUpperCase() + f.substr(1)}
            </a>
          </li>
        `)
      }
    </ul>
    ${
       todos
        .filter(filters.completed)
        .length > 0 && html`
          <button class="clear-completed" onclick="dispatch('clearCompleted')">
            Clear completed
          </button>
        `
    }
  </footer>
`);

const TodoList = connect(({
  todos,
  filters,
  filter
}) => html`
  <section class="main">
    <input
      id="toggle-all"
      class="toggle-all"
      type="checkbox"
      ${todos.every(todo => todo.completed) && 'checked'}
      onchange="dispatch('toggleAll', this.checked)"/>
    <label for="toggle-all"></label>
    <ul class="todo-list">
      ${
        todos
          .filter(filters[filter])
          .map((todo, index) => TodoItem(todo, index))
      }
    </ul>
    ${Footer()}
  </section>
`);

const App = connect(({ todos }) => html`
  <section class="todoapp">
    ${Header()}
    ${todos.length > 0 && TodoList()}
  </section>
`);

window.dispatch = dispatch;

attach(App, document.getElementById('todoapp'));
              
            
!
999px

Console