在上篇中,刚学习JQuery的一些基础理论知识后,接下来做了一个MiniNote案例来应用下这些知识,在这里,要说一句的是,学习这些之前,必须要了解JS高级的一些作用域、对象、函数、闭包等一些知识。
*前期准备
**知识准备
**开发思路
*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
进行下载。