最近在看老項目的代碼,一個客戶端的js代碼粗仓,幾千行的代碼,全是function(){} var...的垂直布局,真的是要感動的哭了借浊。哈哈
一開始都是這樣塘淑,想實現(xiàn)什么功能,不管三七二十一蚂斤,function走起存捺,最終堆起無數(shù)個變量和函數(shù)來完成一個畫面的js。我也是橡淆,但過段時間自己去改代碼bug或者加功能的時候召噩,我的天,這是我寫的嗎逸爵,什么時候?qū)懙木叩危趺蠢聿磺逅悸妨?而且,修改一個地方其他地方也得改师倔,改完了還容易出新bug构韵,偶爾都會忘了是自己寫的
慢慢的代碼看多了點,了解了些js的模塊封裝的一些方式趋艘,面向?qū)ο蟮南嚓P(guān)思想疲恢,越來越覺得易讀、易改的代碼應(yīng)該需要更好的組織形式
下面來看看平時寫代碼瓷胧,面向過程到面向?qū)ο笙匀崂砬岸碎_發(fā)思路
首先需要知道你要做什么需求得明白再來分析代碼怎么寫
功能需求
如下圖,功能比較簡易:
選擇之后添加搓萧,展示區(qū)便陳列:
展示區(qū)點擊‘X’的時候去除當(dāng)前內(nèi)容杂数,選擇區(qū)相應(yīng)也取消對應(yīng)的勾選:
優(yōu)化前代碼
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>多選框問題</title>
</head>
<body>
<!--<input type="text" data-bind-content="name" />
<span data-bind-content='name'></span>-->
<h4>選擇區(qū)</h4>
<div>
<ul id="ul1">
<li>全選<input type="checkbox" name="checkall" /></li>
<li><input type="checkbox" name="checkthis" /><span>1</span></li>
<li><input type="checkbox" name="checkthis" /><span>2</span></li>
<li><input type="checkbox" name="checkthis" /><span>3</span></li>
<li><input type="checkbox" name="checkthis" /><span>4</span></li>
<li><input type="checkbox" name="checkthis" /><span>5</span></li>
</ul>
</div>
<button id="add">添加</button>
<h4>展示區(qū)</h4>
<ul id="ul2"></ul>
</body>
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
<script type="text/javascript">
//封裝
var checkBox = (function () {
var globalV = [];
var yourChose = function (tableId, addClickId, showId) {
console.log($('#' + tableId + ' input[name=checkall]'));
//全選
$('#' + tableId + ' input[name=checkall]').click(function () {
//如果選擇全選, 所有的選擇框都選中,去除全選,所有的選擇框去除選中
if ($(this).prop('checked')) {
$('#' + tableId + ' input[name=checkthis]').prop('checked', true);
//全選的時候,將所有選框的數(shù)據(jù)取出來傳給全局變量globalV
$('#' + tableId + ' input[name=checkthis]').each(function (i, ele) {
var choseDate = {};
choseDate.isChecked = true;
choseDate.id = $(ele).parent().children('span').html();
globalV.push(choseDate);
});
} else {
$('#' + tableId + ' input[name=checkthis]').prop('checked', false);
}
console.log(globalV);
})
//對各個選擇框綁定事件
$('#' + tableId).on('change', 'input[name=checkthis]', function () {
var arr = [];//存儲每個選擇框的狀態(tài)
var choseDate = {};//存儲被選中的選擇框的數(shù)據(jù)
//<li><input type="checkbox" name="check-this" /><span>3</span></li>獲取span里面的值
var this_value = $(this).parent().children('span').html();
//遍歷每個選擇框取選擇的狀態(tài)
$('#' + tableId + ' input[name=checkthis]').each(function (i, ele) {
arr.push($(ele).prop('checked'));
});
//如果有未選中的狀態(tài),去除全選框的選中狀態(tài),否則保留添加全選框的的選中狀態(tài)
if (arr.indexOf(false) == -1) {
$('#' + tableId + ' input[name=checkall]').prop('checked', true);
} else {
$('#' + tableId + ' input[name=checkall]').prop('checked', false);
}
//對應(yīng)每個選擇框的change事件,如果這個選擇框選中,則存儲這個選擇框的數(shù)據(jù),否則遍歷存儲數(shù)據(jù)的變量,移除這個取消選中的的選擇框的數(shù)據(jù)
if ($(this).is(':checked')) {
choseDate.isChecked = true;
choseDate.id = this_value;
globalV.push(choseDate);
} else {
for (var i = 0; i < globalV.length; i++) {
if (this_value == globalV[i].id) {
globalV.splice(i, 1);
}
}
}
console.log(globalV);
});
//點擊添加按鈕的事件
$('#'+addClickId).click(function (e) {
e.preventDefault();
$('#'+showId).empty();//清空展示區(qū)里面的內(nèi)容
console.log(globalV);
//如果沒有選中任何選擇框,則彈出提示
if (globalV.length == 0) {
alert('請先選擇!');
} else {
//如果選中了一些選擇框,則全局變量數(shù)據(jù)不為空,開始遍歷全局變量
for (var j = 0; j < globalV.length; j++) {
//按照全局變量globalV,給展示區(qū)創(chuàng)建元素;(包含了刪除按鈕)
var liElement = '<li>\
<span>'+ globalV[j].id + '</span>\
<p style="display:inline-block;width:20px;height:20px;background-color:red;border-radius:50%;text-align:center">X</p>\
</li>';
$('#'+showId).append(liElement);
}
//給刪除按鈕添加點擊事件
$('#'+showId).on('click', 'p', function () {
//var findAndChangeState=$(this).parent('li').children('span').html();
//找到這個刪除按鈕對應(yīng)的父級標(biāo)簽li下面的span標(biāo)簽的內(nèi)容;注意:這個是簡化;就放在了標(biāo)簽里面,實際情況可能是個屬性,獲取的這個值對應(yīng)一個選擇框
//由這個值來查找對應(yīng)的選擇框,從而改變選擇框的狀態(tài);
//這里是點擊了刪除按鈕,那么與他對應(yīng)的選擇框的選中狀態(tài)也會被去除
var findAndChangeState = $(this).parent('li').children('span').html();
//遍歷選擇框找到與刪除按鈕對應(yīng)的選擇框,將其狀態(tài)改為未選中,同時將全選的選擇框也改為未選中
$('#' + tableId + ' input[name=checkthis]').each(function (i, ele) {
if ($(this).parent().children('span').html() == findAndChangeState) {
$(this).parent().children('input').prop('checked', false);
$('#' + tableId + ' input[name=checkall]').prop('checked', false);
}
});
//改完之后這個刪除按鈕對應(yīng)的父級標(biāo)簽
$(this).parent('li').remove();
})
}
})
};
return {
globalV:globalV,
yourChose:yourChose
}
})()
checkBox.yourChose('ul1', 'add', 'ul2')
</script>
</html>
優(yōu)化后代碼
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>多選框問題</title>
</head>
<body>
<!--<input type="text" data-bind-content="name" />
<span data-bind-content='name'></span>-->
<h4>選擇區(qū)</h4>
<div>
<ul id="ul1">
<li>全選<input type="checkbox" name="checkall" /></li>
<li><input type="checkbox" name="checkthis" /><span>1</span></li>
<li><input type="checkbox" name="checkthis" /><span>2</span></li>
<li><input type="checkbox" name="checkthis" /><span>3</span></li>
<li><input type="checkbox" name="checkthis" /><span>4</span></li>
<li><input type="checkbox" name="checkthis" /><span>5</span></li>
</ul>
</div>
<button id="add">添加</button>
<h4>展示區(qū)</h4>
<ul id="ul2"></ul>
</body>
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
<script type="text/javascript">
//封裝
var checkBox = (function () {
var globalV = [];
var yourChose = function (tableId, addClickId, showId) {
//負責(zé)更新數(shù)據(jù)
var updateData = function () {
globalV = [];
$('#' + tableId + ' input[name=checkthis]').each(function () {
if ($(this).is(':checked')) {
var choseDate = {};
var this_value = $(this).parent().children('span').html();
choseDate.isChecked = true;
choseDate.id = this_value;
globalV.push(choseDate);
}
});
}
//負責(zé)更新畫面
//checkBox狀態(tài)
function fun1() {
if ($(this).attr("name") == "checkthis") {
var arr = [];//存儲每個選擇框的狀態(tài)
var choseDate = {};//存儲被選中的選擇框的數(shù)據(jù)
//<li><input type="checkbox" name="check-this" /><span>3</span></li>獲取span里面的值
var this_value = $(this).parent().children('span').html();
//遍歷每個選擇框取選擇的狀態(tài)
$('#' + tableId + ' input[name=checkthis]').each(function (i, ele) {
arr.push($(ele).prop('checked'));
});
//如果有未選中的狀態(tài),去除全選框的選中狀態(tài),否則保留添加全選框的的選中狀態(tài)
if (arr.indexOf(false) == -1) {
$('#' + tableId + ' input[name=checkall]').prop('checked', true);
} else {
$('#' + tableId + ' input[name=checkall]').prop('checked', false);
}
} else {
//如果選擇全選, 所有的選擇框都選中,去除全選,所有的選擇框去除選中
if ($(this).prop('checked')) {
$('#' + tableId + ' input[name=checkthis]').prop('checked', true);
} else {
$('#' + tableId + ' input[name=checkthis]').prop('checked', false);
}
}
}
//展示區(qū)狀態(tài)(新增)
function fun2() {
$('#' + showId).empty();//清空展示區(qū)里面的內(nèi)容
updateData();
//如果沒有選中任何選擇框,則彈出提示
if (globalV.length == 0) {
alert('請先選擇!');
} else {
//如果選中了一些選擇框,則全局變量數(shù)據(jù)不為空,開始遍歷全局變量
for (var j = 0; j < globalV.length; j++) {
//按照全局變量globalV,給展示區(qū)創(chuàng)建元素;(包含了刪除按鈕)
var liElement = '<li>\
<span>'+ globalV[j].id + '</span>\
<p style="display:inline-block;width:20px;height:20px;background-color:red;border-radius:50%;text-align:center">X</p>\
</li>';
$('#' + showId).append(liElement);
}
//給刪除按鈕添加點擊事件
bindEvent('#' + showId + ' p', "click", event.removeLi);
}
}
//展示區(qū)狀態(tài)(刪除)
function fun3() {
//var findAndChangeState=$(this).parent('li').children('span').html();
//找到這個刪除按鈕對應(yīng)的父級標(biāo)簽li下面的span標(biāo)簽的內(nèi)容;注意:這個是簡化;就放在了標(biāo)簽里面,實際情況可能是個屬性,獲取的這個值對應(yīng)一個選擇框
//由這個值來查找對應(yīng)的選擇框,從而改變選擇框的狀態(tài);
//這里是點擊了刪除按鈕,那么與他對應(yīng)的選擇框的選中狀態(tài)也會被去除
var findAndChangeState = $(this).parent('li').children('span').html();
//遍歷選擇框找到與刪除按鈕對應(yīng)的選擇框,將其狀態(tài)改為未選中,同時將全選的選擇框也改為未選中
$('#' + tableId + ' input[name=checkthis]').each(function (i, ele) {
if ($(this).parent().children('span').html() == findAndChangeState) {
$(this).parent().children('input').prop('checked', false);
$('#' + tableId + ' input[name=checkall]').prop('checked', false);
}
});
//改完之后這個刪除按鈕對應(yīng)的父級標(biāo)簽
$(this).parent('li').remove();
}
//負責(zé)注冊事件
var event = {
select: fun1,
add: fun2,
removeLi: fun3
};
var bindEvent = function (selector, type, fun) {
$(selector).bind(type, fun);
};
//對各個選擇框綁定事件
bindEvent('#' + tableId + ' input[type=checkbox]', "click", event.select);
//點擊添加按鈕的事件
bindEvent('#' + addClickId, "click", event.add);
};
return {
globalV: globalV,
yourChose: yourChose
}
})()
checkBox.yourChose('ul1', 'add', 'ul2');
</script>
</html>
更改后的版本里的代碼其實都是原來的代碼,但組織后的效果是:事件統(tǒng)一綁定(bindEvent)瘸洛,畫面統(tǒng)一更新(fun1揍移、fun2、fun3)反肋,數(shù)據(jù)統(tǒng)一設(shè)定(updateData)那伐。
區(qū)分的很清楚品嚣,哪兒出錯找哪兒助被,幾乎不會交叉。而且比較容易拓展株扛,像事件可以繼續(xù)bindEvent綁定养距,畫面更新的函數(shù)可以相應(yīng)與fun1诉探、fun2、fun3并列添加铃在,數(shù)據(jù)的額外處理可以添加到updateData里。
這僅僅是代碼組織上的優(yōu)化,其實代碼本身也有很多可以改進的地方定铜,像全選的判定阳液、選擇區(qū)聯(lián)動刪除等都有更好的思路和代碼實現(xiàn)。
發(fā)現(xiàn)揣炕,其實這里面已經(jīng)有mvc的影子了帘皿,各司其職,分工明確畸陡,事件綁定那部分就算是一個弱controller鹰溜,綁定事件,分發(fā)事件響應(yīng)函數(shù);更新畫面狀態(tài)部分相當(dāng)于view了丁恭,更新畫面曹动;updateData更新數(shù)據(jù)部分更新的就是modle;