JavaScript ES5面向对象实现一个todolist

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设计模式》
posted @ 2017-04-10 19:27  动机在未来  阅读(2056)  评论(0编辑  收藏  举报