todo-list
前言
最近閱讀了JavaScript設計模式的面向對象篇,但是又苦於實踐,便想到了寫一個簡單的todo-list來鞏固自己JavaScript面向對象設計的思想。希望對和我一樣的小白有幫助。本文代碼使用的是ES5,並非ES6。
要求:了解原型鏈。
遵守
怕文中有人不明白為什么要這么寫,所以這里先說一下一些地方為什么要這么寫。
來自《編寫可維護的JavaScript》
- 將一些常量和不變的數據,放在配置文件里。
- CSS和JavaScript分離。
- 事件處理和業務邏輯單元分離。
// 如:
var config = {
classShow: 'show',
classHide: 'hide'
};
開始
布局
首先,先將HTML框架搭好,這里,就沒有特別的做美化了。
<div>
<input type="text" data-input="work">
<ul data-list="work">
</ul>
</div>
設計對象
這個地方,倒是花了我一部分事件,不知道到底該如何設計,因為自己之前寫過Java,Java里封裝類的思想是盡可能單一功能,和組件的思想很像。
而最開始,我就陷入了深深的思考中,因為我想將這個簡單的todo-list封裝成一個對象,如果按單一功能的思想的話,就得將input封裝為一個對象,li封裝為一個對象,ul封裝為一個對象,顯然,這是不可能的。
於是,我轉變思想,將它們整個封裝一個對象。
對象的屬性
對象的屬性有:input, ul, list。為了實例化多個對象,將name傳入函數里。因此, 代碼如下:
var TodoList = function(name) {
this.list = document.querySelector('[data-list="' + name + '"]');
this.childs = this.list.querySelectorAll('li');
this.input = document.querySelector('[data-input="' + name + '"]');
}
事件綁定
仔細思考,其實就是一個增加、刪除、查找的操作, 並且為了將事件處理和邏輯處理單元分隔開,所以事件綁定僅僅處理的是綁定事件。因此,代碼如下:
var TodoList = function(name) {
this.list = document.querySelector('[data-list="' + name + '"]');
this.childs = this.list.querySelectorAll('li');
this.input = document.querySelector('[data-input="' + name + '"]');
// 綁定事件
this.init = function() {
// input按下enter鍵
this.input.addEventListener('keyup', function(event) {
this.handleKeyup(event);
}.bind(this));
// 事件委托,點擊list
this.list.addEventListener('click', function(event) {
this.handleListClick(event);
}.bind(this));
// input的值改變時
this.input.addEventListener('input', function(event) {
this.handleInputChange(event);
}.bind(this));
};
};
/**
*
* 事件與應用單元分離
* handleKeyup
*
* {param} object event
*
*/
TodoList.prototype.handleKeyup = function(event) {
event.preventDefault();
event.stopPropagation();
this.addItem(event.keyCode, event.target);
}
/**
* 事件和應用單元分離
* 點擊list,事件冒泡階段處理, 委托事件
*
* {param} object event
*/
TodoList.prototype.handleListClick = function(event) {
event.preventDefault();
event.stopPropagation();
this.removeItem(event.target);
}
/**
* 事件和應用單元分離
* input內容改變,則模糊查找, 調用findItem()函數
*
* {param} object event
*/
TodoList.prototype.handleInputChange = function(event) {
var value = event.target.value;
event.preventDefault();
event.stopPropagation();
this.findItem(value);
}
在上面,你可能不知道為什么要綁定this,你需要了解一下bind函數的用處。
而調用事件對象的兩個API是為了阻止事件的默認行為 以及 防止事件對象傳播。
業務邏輯單元的操作
對事件綁定結束后,就需要對
事件綁定成功后,就將進行邏輯單元的代碼編寫。
看下面的代碼:
/**
*
* 增加一個todo-item
* 注意,這里要綁定this
*
* {param} number event.keyCode
* {param} object event.target
*
*/
TodoList.prototype.addItem = function(keyCode, target) {
if(event.keyCode == 13 && event.target.value.trim().length > 0) {
this.append();
this.clearInput();
this.childs = this.list.querySelectorAll('li');
}
}
/**
* 刪除一個todo-item
*
* {param} object event.target
*/
TodoList.prototype.removeItem = function(target) {
// 初始化執行的時候,不存在target
if(target) {
if(target.nodeName == 'BUTTON') {
target.parentNode.parentNode.removeChild(target.parentNode);
this.childs = this.list.querySelectorAll('li');
}
}
};
/**
*
* 查找輸入字符串是否存在
* 如果不存在,就顯示所有的list
* 如果存在,就顯示存在的
*
*/
TodoList.prototype.findItem = function(value) {
var find = false;
// 隱藏所有
for(var i = 0;i < this.childs.length;i++) {
this.childs[i].className = config.classHide;
}
// 如果存在就顯示
for(var i = 0;i < this.childs.length;i++) {
var nowEl = this.childs[i];
if(nowEl.firstChild.innerText.indexOf(value) != -1) {
nowEl.className = config.classShow;
find = true;
}
}
// 如果都沒有就全部顯示
if(!find) {
for(var i = 0;i < this.childs.length;i++) {
this.childs[i].className = config.classShow;
}
}
}
/**
*
* 清空輸入框
*
*/
TodoList.prototype.clearInput = function() {
this.input.value = '';
}
/**
*
* 為totolist append一個li標簽
*
*/
TodoList.prototype.append = function() {
var oLi = document.createElement('li');
var oBtn = document.createElement('button');
var oSpan = document.createElement('span');
oSpan.innerHTML = this.input.value.trim();
oBtn.innerHTML = 'delete';
oLi.appendChild(oSpan);
oLi.appendChild(oBtn);
this.list.appendChild(oLi);
};
實例化對象
var todo = new TodoList('work');
todo.init();
如果你想要查看這個簡單的todo-list所有的代碼,請點擊這里
參考
- 《編寫可維護的JavaScript》
- 《JavaScript設計模式》