基于jQuery&&jQuery UI完成MiniNote案例

在上篇中,刚学习JQuery的一些基础理论知识后,接下来作了一个MiniNote案例来应用下这些知识,在这里,要说一句的是,学习这些以前,必需要了解JS高级的一些做用域、对象、函数、闭包等一些知识javascript

*前期准备

**知识准备

这里写图片描述

**开发思路

这里写图片描述

*MiniNote视图

这里写图片描述

*实现过程

**基本布局实现

<!DOCTYPE html>
<html>
<head>
    <title>jQuery小应用</title>
    <meta charset=utf-8>
    <meta name=description content="index.html">
    <meta name=viewport content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="css/normalize.css">
    <link rel="stylesheet" href="css/jquery.datetimepicker.min.css">
    <link rel="stylesheet" href="css/base.css">
</head>
<body>
<h1>个人Mini-Note</h1>
<div class="msg">
    <span class="msg-content"></span>
    <span class="anchor confirmed">知道了</span>
</div>
<!-- 总容器开始部分 -->
<div class="container">
<!-- 清单列表 -->
    <div class="add-task">
        <form class="add-task">
        <input name="content" type="text" placeholder="例如:今天记得学习英语" autofocus autocomplete="off" >
        <button type="submit">submit</button>
    <!-- 任务列表 -->
    <div class="task-list">
        <!-- 任务详情 -->
        <!-- <div class="task-item"> -->
        <!-- 任务内容 -->
        <!-- <span><input type="checkbox"></span> <span class="task-content">item content 1</span> <span> <span>delete</span> <span>detail</span> </span> </div> -->
    </div>
    <!-- 相关细节 -->
    <div class="task-detail-mask"></div>
    <div class="task-detail">
<!-- <div class="content"></div> 今天记得学习英语! <div> -->
            <!-- 任务描述 -->
<!-- <div class="description"> <textarea name="" id="" cols="30" rows="10"></textarea> </div> </div> -->
        <!-- 任务提醒 -->
<!-- <div class="remind"> <input type="date"> <button type="submit">submit</button> </div> -->
    </div>
</div><!-- 总容器结束部分-->

<script src="js/jquery.js"></script>
<script src="js/jquery.datetimepicker.full.js"></script>
<script src="js/store.js"></script>
<script src="js/base.js"></script>
</body>
</html>

**样式实现

//Base.css
* {
    /*background: rgba(0, 0, 0, .1);*/
    -webkit-box-sizing: border-box;
    -moz-box-sizing: border-box;
    box-sizing: border-box;
    -webkit-transition: background .2s;
    -moz-transition: background .2s;
    -ms-transition: background .2s;
    -o-transition: background .2s;
    transition: background .2s;
    outline: 0;
}

body {
    background: #00334b;
    color: #fff;
}

.container {
    max-width: 700px;
    margin: 0 auto;
    position: relative;
    padding: 0 10px;
}

h1 {
    text-align: center;
}

.task-list {
    margin: 10px 0;
}

.fr {
    float: right;
}

input,
.task-item,
.task-detail-mask,
.task-detail,
textarea,
button {
    border-radius: 3px;
    padding: 10px;
}

textarea,
button,
input[type=text],
input[type=date] {
    border: 0;
}

textarea,
input[type=text], input[type=date] {
    width: 100%;
    display: block;
    background: #ddd;
    -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .3);
    -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .3);
    box-shadow: inset 0 1px 2px rgba(0, 0, 0, .3);
}

textarea {
    min-height: 100px;
}

textarea:hover,
textarea:focus,
input[type=text]:hover,
input[type=date]:hover,
input[type=text]:focus,
input[type=date]:focus {
    background: #eee;
    -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .2);
    -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .2);
    box-shadow: inset 0 1px 2px rgba(0, 0, 0, .2);
}

button {
    display: inline-block;
    cursor: pointer;
    color: #333;
}

.add-task input[type=text] {
    float: left;
    width: 84%;
    margin-right: 1%;
}

button.primary,
[type=submit] {
    background: #46b1e4;
}

.add-task [type=submit] {
    width: 15%;
}

.add-task [type=submit],
.task-item {
    -webkit-box-shadow: 0 2px 3px rgba(0, 0, 0, .4);
    -moz-box-shadow: 0 2px 3px rgba(0, 0, 0, .4);
    box-shadow: 0 2px 3px rgba(0, 0, 0, .4);
}

.add-task [type=submit]:hover {
    background: #5fb9e4;
}

.task-item {
    background: #fff;
    color: #333;
    margin-bottom: 2px;
    cursor: pointer;
}

.task-item.completed {
    color: #aaa;
    /*background: rgba(255,255,255,.7);*/
    opacity: 0.4;
}

.task-item.completed:after {
    content: " ";
    height: 1px;
    background: #aaa;
    width: 96%;
    position: relative;
    top: -8px;
    display: block;
    float: right;
}

.task-item:hover {
    background: #ddd;
}

.task-detail-mask,
.task-detail {
    position: absolute;
    display: none;
}

.task-detail {
    background: #fff;
    overflow: auto;
    color: #333;
    width: 50%;
    height: 100%;
    padding: 10px;
    bottom: 0;
    right: 0;
    border-radius: 3px 0 0 3px;
    -webkit-box-shadow: 0 1px 2px 1px rgba(0, 0, 0, .1);
    -moz-box-shadow: 0 1px 2px 1px rgba(0, 0, 0, .1);
    box-shadow: 0 1px 2px 1px rgba(0, 0, 0, .1);
}

.task-detail .content {
    padding: 10px;
    font-weight: 900;
    cursor: pointer;
}

.task-detail .input-item {
    margin-bottom: 10px;
}

.task-detail-mask {
    position: fixed;
    top: 0;
    bottom: 0;
    right: 0;
    left: 0;
}

.task-content {
    margin-left: 10px;
}

.action {
    color: #888;
    font-size: 90%;
}

.action:hover {
    color: #333;
}
.msg {
    display: none;
    text-align: center;
    background: #ffe264;
    padding: 10px;
    color: #333;
}

.anchor {
    cursor: pointer;
}

.alerter {
    width: 0;
    height: 0;
}

**JS实现

//Base.js
;(function () {
  'use strict';

  var $form_add_task = $('.add-task')
    , $window = $(window)
    , $body = $('body')
    , $task_delete_trigger
    , $task_detail
    , $task_detail_trigger
    , $task_detail = $('.task-detail')
    , $task_detail_mask = $('.task-detail-mask')
    , task_list = []
    , current_index
    , $update_form
    , $task_detail_content
    , $task_detail_content_input
    , $checkbox_complete
    , $msg = $('.msg')
    , $msg_content = $msg.find('.msg-content')
    , $msg_confirm = $msg.find('.confirmed')
    , $alerter = $('.alerter')
    ;

  init();

  $form_add_task.on('submit', on_add_task_form_submit)
  $task_detail_mask.on('click', hide_task_detail)

  function pop(arg) {
    if (!arg) {
      console.error('pop title is required');
    }

    var conf = {}
      , $box
      , $mask
      , $title
      , $content
      , $confirm
      , $cancel
      , timer
      , dfd
      , confirmed
      ;

    dfd = $.Deferred();

    if (typeof arg == 'string')
      conf.title = arg;
    else {
      conf = $.extend(conf, arg);
    }

    $box = $('<div>' +
      '<div class="pop-title">' + conf.title + '</div>' +
      '<div class="pop-content">' +
      '<div>' +
      '<button style="margin-right: 5px;" class="primary confirm">肯定</button>' +
      '<button class="cancel">取消</button>' +
      '</div>' +
      '</div>' +
      '</div>')
      .css({
        color: '#444',
        width: 300,
        height: 'auto',
        padding: '15px 10px',
        background: '#fff',
        position: 'fixed',
        'border-radius': 3,
        'box-shadow': '0 1px 2px rgba(0,0,0,.5)'
      })

    $title = $box.find('.pop-title').css({
      padding: '5px 10px',
      'font-weight': 900,
      'font-size': 20,
      'text-align': 'center'
    })

    $content = $box.find('.pop-content').css({
      padding: '5px 10px',
      'text-align': 'center'
    })

    $confirm = $content.find('button.confirm');
    $cancel = $content.find('button.cancel');

    $mask = $('<div></div>')
      .css({
        position: 'fixed',
        background: 'rgba(0,0,0,.5)',
        top: 0,
        bottom: 0,
        left: 0,
        right: 0,
      })

    timer = setInterval(function () {
      if (confirmed !== undefined) {
        dfd.resolve(confirmed);
        clearInterval(timer);
        dismiss_pop();
      }
    }, 50)

    $confirm.on('click', on_confirmed)
    $cancel.on('click', on_cancel);
    $mask.on('click', on_cancel);

    function on_cancel() {
      confirmed = false;
    }

    function on_confirmed() {
      confirmed = true;
    }


    function dismiss_pop() {
      $mask.remove();
      $box.remove();
    }

    function adjust_box_position() {
      var window_width = $window.width()
        , window_height = $window.height()
        , box_width = $box.width()
        , box_height = $box.height()
        , move_x
        , move_y
        ;

      move_x = (window_width - box_width) / 2;
      move_y = ((window_height - box_height) / 2) - 20;

      $box.css({
        left: move_x,
        top: move_y,
      })
    }

    $window.on('resize', function () {
      adjust_box_position();
    })
    $mask.appendTo($body);
    $box.appendTo($body);
    $window.resize();
    return dfd.promise();
  }

  function listen_msg_event() {
    $msg_confirm.on('click', function () {
      hide_msg();
    })
  }

  function on_add_task_form_submit(e) {
    var new_task = {}, $input;
    /*禁用默认行为*/
    e.preventDefault();
    /*获取新Task的值*/
    $input = $(this).find('input[name=content]');
    new_task.content = $input.val();
    /*若是新Task的值为空 则直接返回 不然继续执行*/
    if (!new_task.content) return;
    /*存入新Task*/
    if (add_task(new_task)) {
      // render_task_list();
      $input.val(null);
    }
  }

  /*监听打开Task详情事件*/
  function listen_task_detail() {
    var index;
    $('.task-item').on('dblclick', function () {
      index = $(this).data('index');
      show_task_detail(index);
    })

    $task_detail_trigger.on('click', function () {
      var $this = $(this);
      var $item = $this.parent().parent();
      index = $item.data('index');
      show_task_detail(index);
    })
  }

  /*监听完成Task事件*/
  function listen_checkbox_complete() {
    $checkbox_complete.on('click', function () {
      var $this = $(this);
      var index = $this.parent().parent().data('index');
      var item = get(index);
      if (item.complete)
        update_task(index, {complete: false});
      else
        update_task(index, {complete: true});
    })
  }

  function get(index) {
    return store.get('task_list')[index];
  }

  /*查看Task详情*/
  function show_task_detail(index) {
    /*生成详情模板*/
    render_task_detail(index);
    current_index = index;
    /*显示详情模板(默认隐藏)*/
    $task_detail.show();
    /*显示详情模板mask(默认隐藏)*/
    $task_detail_mask.show();
  }

  /*更新Task*/
  function update_task(index, data) {
    if (!index || !task_list[index])
      return;

    task_list[index] = $.extend({}, task_list[index], data);
    refresh_task_list();
  }

  /*隐藏Task详情*/
  function hide_task_detail() {
    $task_detail.hide();
    $task_detail_mask.hide();
  }

  /*渲染指定Task的详细信息*/
  function render_task_detail(index) {
    if (index === undefined || !task_list[index])
      return;

    var item = task_list[index];

    var tpl =
      '<form>' +
      '<div class="content">' +
      item.content +
      '</div>' +
      '<div class="input-item">' +
      '<input style="display: none;" type="text" name="content" value="' + (item.content || '') + '">' +
      '</div>' +
      '<div>' +
      '<div class="desc input-item">' +
      '<textarea name="desc">' + (item.desc || '') + '</textarea>' +
      '</div>' +
      '</div>' +
      '<div class="remind input-item">' +
      '<label>提醒时间</label>' +
      '<input class="datetime" name="remind_date" type="text" value="' + (item.remind_date || '') + '">' +
      '</div>' +
      '<div class="input-item"><button type="submit">更新</button></div>' +
      '</form>';

    /*用新模板替换旧模板*/
    $task_detail.html(null);
    $task_detail.html(tpl);
    $('.datetime').datetimepicker();
    /*选中其中的form元素, 由于以后会使用其监听submit事件*/
    $update_form = $task_detail.find('form');
    /*选中显示Task内容的元素*/
    $task_detail_content = $update_form.find('.content');
    /*选中Task input的元素*/
    $task_detail_content_input = $update_form.find('[name=content]');

    /*双击内容元素显示input, 隐藏本身*/
    $task_detail_content.on('dblclick', function () {
      $task_detail_content_input.show();
      $task_detail_content.hide();
    })

    $update_form.on('submit', function (e) {
      e.preventDefault();
      var data = {};
      /*获取表单中各个input的值*/
      data.content = $(this).find('[name=content]').val();
      data.desc = $(this).find('[name=desc]').val();
      data.remind_date = $(this).find('[name=remind_date]').val();

      update_task(index, data)
      hide_task_detail();
    })
  }

  /*查找并监听全部删除按钮的点击事件*/
  function listen_task_delete() {
    $task_delete_trigger.on('click', function () {
      var $this = $(this);
      /*找到删除按钮所在的task元素*/
      var $item = $this.parent().parent();
      var index = $item.data('index');
      /*确认删除*/
      pop('肯定删除?')
        .then(function (r) {
          r ? delete_task(index) : null;
        })
    })
  }

  function add_task(new_task) {
    /*将新Task推入task_list*/
    task_list.push(new_task);
    /*更新localStorage*/
    refresh_task_list();
    return true;
  }

  /* * 刷新localStorage数据并渲染模板 * */
  function refresh_task_list() {
    store.set('task_list', task_list);
    render_task_list();
  }

  /*删除一条Task*/
  function delete_task(index) {
    /*若是没有index 或者index不存在则直接返回*/
    if (index === undefined || !task_list[index]) return;

    delete task_list[index];
    /*更新localStorage*/
    refresh_task_list();
  }

  function init() {
    task_list = store.get('task_list') || [];
    listen_msg_event();
    if (task_list.length)
      render_task_list();
    task_remind_check();
  }

  function task_remind_check() {
    var current_timestamp;
    var itl = setInterval(function () {
      for (var i = 0; i < task_list.length; i++) {
        var item = get(i), task_timestamp;
        if (!item || !item.remind_date || item.informed)
          continue;

        current_timestamp = (new Date()).getTime();
        task_timestamp = (new Date(item.remind_date)).getTime();
        if (current_timestamp - task_timestamp >= 1) {
          update_task(i, {informed: true});
          show_msg(item.content);
        }
      }
    }, 300);
  }

  function show_msg(msg) {
    if (!msg) return;

    $msg_content.html(msg);
    $alerter.get(0).play();
    $msg.show();
  }

  function hide_msg() {
    $msg.hide();
  }

  /* * 渲染全部Task模板 * */
  function render_task_list() {
    var $task_list = $('.task-list');
    $task_list.html('');
    var complete_items = [];
    for (var i = 0; i < task_list.length; i++) {
      var item = task_list[i];
      if (item && item.complete)
        complete_items[i] = item;
      else
        var $task = render_task_item(item, i);
      $task_list.prepend($task);
    }

    for (var j = 0; j < complete_items.length; j++) {
      $task = render_task_item(complete_items[j], j);
      if (!$task) continue;
      $task.addClass('completed');
      $task_list.append($task);
    }

    $task_delete_trigger = $('.action.delete')
    $task_detail_trigger = $('.action.detail')
    $checkbox_complete = $('.task-list .complete[type=checkbox]')
    listen_task_delete();
    listen_task_detail();
    listen_checkbox_complete();
  }

  /* *渲染单条Task模板 * */
  function render_task_item(data, index) {
    if (!data || !index) return;
    var list_item_tpl =
      '<div class="task-item" data-index="' + index + '">' +
      '<span><input class="complete" ' + (data.complete ? 'checked' : '') + ' type="checkbox"></span>' +
      '<span class="task-content">' + data.content + '</span>' +
      '<span class="fr">' +
      '<span class="action delete"> 删除</span>' +
      '<span class="action detail"> 详细</span>' +
      '</span>' +
      '</div>';
    return $(list_item_tpl);
  }
})();

*案例小结

**1.经过MiniNote案例,能够学习到如何使用像store.js、datetimepicker.js等一些第三方插件的使用,而且在有精力的状况下,能够进一步学习下这些插件的源码;
**2.经过第三方插件的使用,咱们能够体验到JQuery给开发带来的便利,而且能够看出在HTML文本中,大部分不存在较多的冗余内容,JS代码相对于写纯JS实现一样的效果来讲也要简单地多,在此,当咱们学到必定阶段时,咱们须要本身撰写简易的插件,甚至框架;
**3.在实现item和item_detail时,采用了一种模板思想,能够实现item和item_detail动态地展示,而且避免了放在HTML文本而产生难以实现动态效果的问题。这种模板思想能够学习借鉴下。
PS:以上只是部分代码,如想要源码,能够点击https://github.com/Cecilia520/MiniNote
进行下载。
php