Backbone写的todolist

使用到Backbone.View、Backbone.Model、Backbone.Collection、Backbone.history和Backbone.Router相关的知识点。
在这里插入图片描述

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>

  <style>
    ul,
    li {
      list-style: none;
      padding: 0;
      margin: 0;
    }

    .box {
      width: 550px;
      margin: 20px auto;
      box-shadow: 0 2px 4px 0 rgb(0 0 0 / 20%), 0 25px 50px 0 rgb(0 0 0 / 10%);
      position: relative;
    }

    .pull-left {
      float: left;
    }

    .pull-right {
      float: right;
    }

    .box header {
      padding: 10px 20px 10px 60px;
      border-bottom: 1px solid #ddd;
    }

    .box .new-todo {
      border: none;
      width: 100%;
      font-size: 24px;
    }

    .box .new-todo:focus {
      outline: none;
    }

    input::placeholder {
      color: #ddd;
    }

    .box .toggle-all {
      opacity: 0;
      position: absolute;
      z-index: 2;
    }

    .box .toggle-all+label {
      transform: rotate(90deg);
      display: inline-block;
      width: 45px;
      height: 45px;
      position: absolute;
      top: 0;
      left: 0;
      text-align: center;
      line-height: 45px;
      font-size: 28px;
      color: #ddd;
      user-select: none;
      cursor: pointer;
    }

    .box .toggle-all:checked+label {
      color: #333;
    }

    .box .todo-list li {
      border-bottom: 1px solid #ddd;
      position: relative;
    }

    .box .todo-list .view {
      position: relative;
    }

    .box .todo-list .view .toggle {
      position: absolute;
      left: 8px;
      bottom: 11px;
      width: 23px;
      height: 23px;
      opacity: 0;
      cursor: pointer;
    }

    .box .todo-list .view label {
      display: block;
      padding: 10px 50px;
      font-size: 20px;
      color: #555;
      background: #fff url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23ededed%22%20stroke-width%3D%223%22/%3E%3C/svg%3E') no-repeat left 2px;
    }

    .box .todo-list .view .toggle:checked+label {
      background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23bddad5%22%20stroke-width%3D%223%22/%3E%3Cpath%20fill%3D%22%235dc2af%22%20d%3D%22M72%2025L42%2071%2027%2056l-4%204%2020%2020%2034-52z%22/%3E%3C/svg%3E');
    }

    .box .todo-list .destroy {
      background-color: #fff;
      color: red;
      border: 0;
      position: absolute;
      right: 10px;
      top: 7px;
      font-size: 26px;
      cursor: pointer;
      display: none;
    }

    .box .todo-list .view label:hover+.destroy {
      display: block;
    }

    .box .todo-list .view .destroy:hover {
      display: block;
    }

    .box .todo-list .edit {
      display: none;
      padding: 10px;
      font-size: 20px;
      margin-left: 43px;
      width: 482px;
    }

    .box .todo-list .editing .edit {
      display: block;
    }

    .box .todo-list .editing .view {
      display: none;
    }

    .box .todo-list .completed .view label {
      text-decoration: line-through;
      color: #ddd;
    }

    .box footer {
      font-size: 12px;
      color: #666;
      padding: 15px 20px;
      position: relative;
      line-height: 12px;
    }

    .box footer::before {
      position: absolute;
      content: ' ';
      height: 50px;
      display: block;
      left: 0;
      right: 0;
      bottom: 0;
      box-shadow: 0 1px 1px #ddd, 0 8px 0 -3px #fff, 0 9px 1px -3px #ddd, 0 16px 0 -6px #fff, 0 17px 1px -6px #ddd;
    }

    .box footer a {
      color: #666;
      text-decoration: none;
    }

    .box footer .filters {
      position: absolute;
      top: 10px;
      left: 0;
      right: 0;
      text-align: center;
    }

    .box footer .filters li {
      display: inline-block;
      margin-right: 20px;
      padding: 5px 8px;
      border-radius: 2px;
      line-height: 12px;
    }

    .box footer .clear-completed {
      float: right;
      border: 0;
      color: #666;
      cursor: pointer;
      background-color: #fff;
      position: relative;
    }

    .box footer .filters li.selected {
      border: 1px solid #b53636;
    }

    .hide {
      display: none;
    }
  </style>
</head>

<body>
  <div class="box">
    <header>
      <input type="text" class="new-todo" placeholder="What needs tobe done">
    </header>
    <section class="hide">
      <input type="checkbox" id="toggle-all" class="toggle-all">
      <label for="toggle-all"></label>

      <ul class="todo-list"></ul>
    </section>

    <footer class="hide">

    </footer>
  </div>

  <script type="text/template" id="todo-item">
    <div class="view">
      <input type="checkbox" class="toggle" <%= completed ? 'checked' : '' %>>
      <label><%= title %></label>
      <button type="button" class="destroy">x</button>
    </div>
    <input type="text" class="edit" value="<%= title %>">
  </script>

  <script type="text/template" id="start-template">
    <span class="todo-count">
      <strong><%= remaining %></strong> <%= remaining === 1 ? 'item' : 'items' %> left
    </span>

    <ul class="filters">
      <li>
        <a href="#/">All</a>
      </li>
      <li>
        <a href="#/active">Active</a>
      </li>
      <li>
        <a href="#/completed">Completed</a>
      </li>
    </ul>

    <% if(completed) { %>
    <button class="clear-completed">Clear completed</button>
    <% } %>
  </script>

  <script src="./../js/jquery-3.4.1.min.js"></script>
  <script src="./../js/underscore-min.js"></script>
  <script src="./../js/backbone.js"></script>

  <script>
    var Todo = Backbone.Model.extend({
      defaults: {
        title: '',
        completed: false
      }
    });

    var Todos = Backbone.Collection.extend({
      model: Todo,
      completed: function () {
        return this.where({ completed: true })
      },
      remaining: function () {
        return this.where({ completed: false })
      }
    });

    var AppView = Backbone.View.extend({
      el: '.box',
      template: _.template($('#start-template').html()),
      initialize: function () {
        this.render();
        this.allCheckbox = this.$('#toggle-all')[0];

        this.listenTo(todos, 'add', this.infoBoxShow);

        this.listenTo(todos, 'destroy', this.infoBoxShow);

        //监听所有todo的completed属性变化
        this.listenTo(todos, 'change:completed', this.changeCompleted);

        //监听底部按钮
        this.on('filter', this.filter);
      },
      render: function () {
        this.$('footer').html(this.template({
          remaining: todos.remaining().length,
          completed: todos.completed().length
        }));
      },
      events: {
        'keyup .new-todo': 'addTodo',
        'click .toggle-all': 'allSelected',
        'click .clear-completed': 'clearCompleted'
      },
      addTodo: function (event) {
        if (event.keyCode !== 13) {
          return false;
        }

        var $newTodo = this.$el.find('.new-todo');
        var val = $newTodo.val();

        if (!$.trim(val)) {
          return false;
        }

        var todo = new Todo({
          title: val
        });

        todos.add(todo);

        var todoView = new TodoView({
          model: todo
        });

        var ele = todoView.render().$el;
        if (globalFilter === 'completed') {
          ele.addClass('hide');
        }
        this.$el.find('.todo-list').append(ele);

        $newTodo.val('');
      },
      allSelected: function () {
        var completed = this.allCheckbox.checked;
        todos.each(function (model) {
          model.set('completed', completed)
        });
      },
      infoBoxShow() { //判断section DOM是否显示
        if (todos.length) {
          this.$('section').removeClass('hide');
          this.$('footer').removeClass('hide');
        } else {
          this.$('section').addClass('hide');
          this.$('footer').addClass('hide');
        }

        this.render();
        this.footerAddAction();
      },
      clearCompleted() {
        _.invoke(todos.completed(), 'destroy');
      },
      changeCompleted: function (model) {
        //全选按钮
        $(this.allCheckbox).prop('checked', todos.completed().length);

        this.render();
        this.filter();
      },
      footerAddAction: function () {
        this.$('.filters li').each(function () {
          $(this).removeClass('selected');
          var href = $(this).find('a[href="#/' + (globalFilter ? globalFilter : '') + '"]').closest('li').addClass('selected');
        });
      },
      filter: function () {
        todos.each(function (model) {
          model.trigger('filter')
        });

        this.footerAddAction();
      }
    });

    var TodoView = Backbone.View.extend({
      tagName: 'li',
      template: _.template($('#todo-item').html()),
      initialize: function () {
        this.listenTo(this.model, 'change', this.render);
        this.listenTo(this.model, 'destroy', this.remove);
        this.listenTo(this.model, 'filter', this.toggleVisible)
      },
      render: function () {
        this.$el.html(this.template(this.model.toJSON()));
        if (this.model.get('completed')) {
          this.$el.addClass('completed');
        } else {
          this.$el.removeClass('completed');
        }

        return this;
      },
      events: {
        'click input.toggle': 'toogle',
        'click button.destroy': 'destroy',
        'dblclick label': 'showUpdateBox',
        'blur .edit': 'updeteTitle',
      },
      toogle: function (e) {
        this.model.set('completed', $(e.currentTarget).is(':checked'));
        this.toggleVisible();
      },
      destroy: function (e) {
        this.model.destroy();//从todos中移除model
        this.remove();  //删除view
      },
      showUpdateBox: function () {
        var $input = this.$('.edit');
        var textLength = $input.val().length;
        this.$el.addClass('editing')
        $input.focus();
        $input[0].setSelectionRange(textLength, textLength);
      },
      updeteTitle: function () {
        var val = this.$('.edit').val();

        if (!val) {
          this.destroy();
        } else {
          this.model.set('title', val);
        }
        this.$el.removeClass('editing')
      },
      toggleVisible: function () {
        this.$el.toggleClass('hide', this.isHidden());
      },
      isHidden: function () {
        return this.model.get('completed') ? globalFilter === 'active' : globalFilter === 'completed';
      }
    });

    var TodoRouter = Backbone.Router.extend({
      routes: {
        '*filter': 'filter',
      },
      filter: function (params) {
        globalFilter = params;
        appview.trigger('filter');
      }
    });

    var todos = new Todos();
    var appview = new AppView();
    var todoRouter = new TodoRouter();
    Backbone.history.start();
  </script>
</body>

</html>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值