所有代碼可以在我的Github中找到,記錄從task002開(kāi)始移剪。
DOM操作
要求
// 實(shí)現(xiàn)一個(gè)簡(jiǎn)單的Query
function $(selector) {
}
// 可以通過(guò)id獲取DOM對(duì)象,通過(guò)#標(biāo)示,例如
$("#adom"); // 返回id為adom的DOM對(duì)象
// 可以通過(guò)tagName獲取DOM對(duì)象忿危,例如
$("a"); // 返回第一個(gè)<a>對(duì)象
// 可以通過(guò)樣式名稱(chēng)獲取DOM對(duì)象,例如
$(".classa"); // 返回第一個(gè)樣式定義包含classa的對(duì)象
// 可以通過(guò)attribute匹配獲取DOM對(duì)象毒嫡,例如
$("[data-log]"); // 返回第一個(gè)包含屬性data-log的對(duì)象
$("[data-time=2015]"); // 返回第一個(gè)包含屬性data-time且值為2015的對(duì)象
// 可以通過(guò)簡(jiǎn)單的組合提高查詢(xún)便利性癌蚁,例如
$("#adom .classa"); // 返回id為adom的DOM所包含的所有子節(jié)點(diǎn)中幻梯,第一個(gè)樣式定義包含classa的對(duì)象
思路
這一題花費(fèi)的時(shí)間比較久。
第一種思路是直接在
$()
中判斷是否有空格努释,然后再分情況查詢(xún)碘梢。單獨(dú)查詢(xún)的話(huà)比較好實(shí)現(xiàn),但在組合查詢(xún)中遇到了麻煩伐蒂。直接寫(xiě)的話(huà)比較麻煩幾乎是把幾種情況都再寫(xiě)了一遍煞躬;使用遞歸的話(huà)因?yàn)榉祷氐闹挡皇菙?shù)組,效果并不理想逸邦。于是放棄恩沛。第二種思路是不再判斷是否包含空格,而是直接使用
split(" ")
分成數(shù)組缕减,使用for
循環(huán)挨個(gè)查詢(xún)雷客。但在組合查詢(xún)這一情況時(shí)想不到好的解決辦法,還是放棄了桥狡。
最后搜索了一下別人的答案搅裙,綜合了一下各種思路,個(gè)人感覺(jué)這種最好理解實(shí)現(xiàn)起來(lái)也比較方便:
首先實(shí)現(xiàn)domQuery函數(shù)
裹芝,這個(gè)函數(shù)直接返回查詢(xún)到的結(jié)果部逮,而不是數(shù)組中的第一個(gè)對(duì)象,然后再$()
中再查詢(xún)到第一個(gè)對(duì)象嫂易。
在domQuery(selector,root)
中有兩個(gè)參數(shù)兄朋,其中root就是考慮到組合查詢(xún)的情況,在傳入的selector有兩個(gè)的情況下怜械,第一個(gè)selector查詢(xún)到的元素作為第二個(gè)子selector的root來(lái)進(jìn)行查詢(xún)颅和。
實(shí)現(xiàn)
-
domQuery
函數(shù)
function domQuery(selector, root) {
var text;
var elements = [];
//if root is not defined, root = document
if (!root) {
root = document;
}
if (selector.charAt(0) === "#") {
text = selector.replace(/^\#/, "");
elements = document.getElementById(text);
} else if (selector.charAt(0) === ".") {
text = selector.replace(/^\./, "");
elements = root.getElementsByClassName(text);
} else if ((selector.charAt(0) === "[") && (selector.charAt(selector.length - 1) === "]")) {
//get all the elements
var eles = root.getElementsByTagName("*");
//delete "[" and "]"
selector = selector.replace(/^\[/, "");
selector = selector.replace(/\]$/, "");
var texts = selector.split("=");
var attr = texts[0];
var value = texts[1];
//有屬性值的情況
if (texts[1]) {
for (var i = 0, length1 = eles.length; i < length1; i++) {
if (eles[i].hasAttribute(attr)) {
if (eles[i].getAttribute(attr) === value) {
elements = eles[i];
}
}
}
}
//沒(méi)有屬性值
else {
for (var i = 0, length1 = eles.length; i < length1; i++) {
if (eles[i].hasAttribute(attr)) {
elements = eles[i];
}
}
}
} else {
elements = root.getElementsByTagName(selector);
}
return elements;
}
2.$()
函數(shù)
function $(selector) {
//multiple queries
var result = [];
if (selector.indexOf(" ") !== -1) {
//split selector by space
var selectors = selector.split(" ");
parents = domQuery(selectors[0]);
for (var i = 1, length1 = selectors.length; i < length1; i++) {
if (parents.length) {
parents = domQuery(selectors[i], parents[0]);
} else {
parents = domQuery(selectors[i], parents);
}
}
result = parents;
}
//single query
else {
var result = domQuery(selector, document);
}
if (result.length) {
return result[0];
} else {
return result;
}
}
補(bǔ)充
代碼邏輯
重點(diǎn)說(shuō)明一下$()
中的組合查詢(xún)的邏輯
if (selector.indexOf(" ") !== -1) {
//split selector by space
var selectors = selector.split(" ");
parents = domQuery(selectors[0]);
for (var i = 1, length1 = selectors.length; i < length1; i++) {
if (parents.length) {
parents = domQuery(selectors[i], parents[0]);
} else {
parents = domQuery(selectors[i], parents);
}
}
result = parents;
}
-
parents = domQuery(selectors[0])
獲得最開(kāi)始的root根節(jié)點(diǎn),然后開(kāi)始for循環(huán)宫盔,每一次循環(huán)domQuery得到的值都作為下一次循環(huán)的根節(jié)點(diǎn)融虽,從而達(dá)到組合查詢(xún)的目的; - 這里有個(gè)問(wèn)題是
getElementById
和getElementsByClassName
灼芭、getElementsByTagName
等方法不同在于getElementById
返回單個(gè)元素有额,其他方法返回的則是數(shù)組,這就造成了處理上的困難彼绷; -
if (parents.length)
就用于判斷當(dāng)前得到的結(jié)果是否為數(shù)組巍佑,如果是,則只取數(shù)組的第一個(gè)元素寄悯; - 但這樣的查詢(xún)其實(shí)是不全面的萤衰,只能查詢(xún)到返回?cái)?shù)組的第一個(gè)中是否包含所查詢(xún)?cè)兀绻堑诙€(gè)或者第三個(gè)包含猜旬,那么是查詢(xún)不到的脆栋。
- 現(xiàn)在所能做到的倦卖,要么查詢(xún)傳進(jìn)來(lái)的參數(shù)只有2個(gè);若要查詢(xún)多個(gè)椿争,則像上面所說(shuō)的那樣無(wú)法查詢(xún)除第一個(gè)以外的元素是否包含怕膛。
如何改進(jìn)還在摸索中
補(bǔ)充發(fā)現(xiàn)
如果組合中第二個(gè)selector為id,即類(lèi)似$(".a #b")
這種情況是不行的秦踪。會(huì)報(bào)root.getElementById is not a function
錯(cuò)誤褐捻。
在Chrome控制臺(tái)也實(shí)驗(yàn)了一下,getElementById
方法只能用于document
椅邓。
猜測(cè)了一下柠逞,大概是因?yàn)閕d是唯一的,并不需要多一個(gè)子集去篩選景馁?
DOM事件
要求
- ?
// 給一個(gè)element綁定一個(gè)針對(duì)event事件的響應(yīng)
//響應(yīng)函數(shù)為listener
function addEvent(element, event, listener) {
// your implement
}
// 移除element對(duì)象對(duì)于event事件發(fā)生時(shí)執(zhí)行l(wèi)istener的響應(yīng)
function removeEvent(element, event, listener) {
// your implement
}
// 實(shí)現(xiàn)對(duì)click事件的綁定
function addClickEvent(element, listener) {
// your implement
}
// 實(shí)現(xiàn)對(duì)于按Enter鍵時(shí)的事件綁定
function addEnterEvent(element, listener) {
// your implement
}
- ?
// 事件代理
function delegateEvent(element, tag, eventName, listener) {
// your implement
}
$.delegate = delegateEvent;
// 使用示例
// 還是上面那段HTML板壮,實(shí)現(xiàn)對(duì)list這個(gè)ul里面所有l(wèi)i的click事件進(jìn)行響應(yīng)
$.delegate($("#list"), "li", "click", clickHandle);
- ?
//函數(shù)封裝
$.on(selector, event, listener) {
// your implement
}
$.click(selector, listener) {
// your implement
}
$.un(selector, event, listener) {
// your implement
}
$.delegate(selector, tag, event, listener) {
// your implement
}
// 使用示例:
$.click("[data-log]", logListener);
$.delegate('#list', "li", "click", liClicker);
思路
- 一開(kāi)始其實(shí)沒(méi)太懂這個(gè)要求,嘗試寫(xiě)了一下也不盡人意合住,于是搜索了一下其他人的方法个束,才發(fā)現(xiàn)其實(shí)是很簡(jiǎn)單的一個(gè)問(wèn)題自己想復(fù)雜了,直接使用js中提供的
addEventListener
方法就可以實(shí)現(xiàn)聊疲,接下來(lái)的幾個(gè)方法同理 - 弄懂了事件代理的原理后就很簡(jiǎn)單,將事件綁定到元素上一級(jí)的element中沪悲,在點(diǎn)擊時(shí)判斷是否節(jié)點(diǎn)名
nodeName
等于tag获洲,是的話(huà)就執(zhí)行l(wèi)istener函數(shù)。 - 封裝沒(méi)什么好說(shuō)的殿如,不過(guò)element換成了selector贡珊,那么利用之前實(shí)現(xiàn)的
$()
把element變成$(selector)
就沒(méi)問(wèn)題了。
實(shí)現(xiàn)
- ?
// 給一個(gè)element綁定一個(gè)針對(duì)event事件的響應(yīng)涉馁,響應(yīng)函數(shù)為listener
function addEvent(element, event, listener) {
if (element.addEventListener) {
element.addEventListener(event, listener, false)
}
}
// 移除element對(duì)象對(duì)于event事件發(fā)生時(shí)執(zhí)行l(wèi)istener的響應(yīng)
function removeEvent(element, event, listener) {
if (element.removeEventListener) { //標(biāo)準(zhǔn)
element.removeEventListener(event, listener, false);
}
}
// 實(shí)現(xiàn)對(duì)click事件的綁定
function addClickEvent(element, listener) {
addEvent(element, 'click', listener);
}
// 實(shí)現(xiàn)對(duì)于按Enter鍵時(shí)的事件綁定
function addEnterEvent(element, listener) {
addEvent(element, "keydown", function(e) {
if (e.keyCode === 13) {
listener();
}
});
}
- ?
function delegateEvent(element, tag, eventName, listener) {
$.eventName(element, function(e) {
var e = e || window.event;
var target = e.target || e.srcElement;
if (target.nodeName.toLowerCase() === tag) {
//?????
listener.call(target, e);
}
});
}
- ?
//函數(shù)封裝
$.on = function(selector, event, listener) {
return addEvent($(selector), event, listener);
};
$.un = function(selector, event, listener) {
return removeEvent($(selector), event, listener);
};
$.click = function(selector, listener) {
return addClickEvent($(selector), listener);
};
$.enter = function(selector, listener) {
return addEnterEvent($(selector), listener);
};
$.delegate = function(selector, tag, event, listener) {
return delegateEvent($(selector), tag, eventName, listener);
};
補(bǔ)充
暫無(wú)
BOM
要求
// 判斷是否為IE瀏覽器门岔,返回-1或者版本號(hào)
function isIE() {
// your implement
}
// 設(shè)置cookie
function setCookie(cookieName, cookieValue, expiredays) {
// your implement
}
// 獲取cookie值
function getCookie(cookieName) {
// your implement
}
思路
本來(lái)想用navigator判斷瀏覽器是否為IE的,但是看到說(shuō)navigator的信息有誤導(dǎo)性烤送,然后搜了一下發(fā)現(xiàn)還有用
ActiveXObject
檢測(cè)的寒随。MSDN里面這么描述的:
此對(duì)象為 Microsoft 擴(kuò)展,僅在 Internet Explorer 中受支持帮坚,在 Windows 8.x 應(yīng)用商店應(yīng)用中不受支持妻往。
那么只要檢測(cè)是否存在ActiveXObject
對(duì)象就可以知道是否為IE瀏覽器。
接著是檢查版本试和,在自己電腦上試了一下讯泣,IE版本是11,userAgent
信息:
"Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; rv:11.0) like Gecko"
而IE10(不包括)之前的IE的userAgent
信息格式:
Mozilla/5.0 (compatible; MSIE 9.0; Windows Phone OS 7.5; Trident/5.0; IEMobile/9.0)
分兩種情況阅悍,一個(gè)正則式就可以搞定好渠。設(shè)置cookie和獲取cookie在W3C里直接就給了方法了昨稼,也沒(méi)啥難理解的點(diǎn),就了解一下cookie了拳锚。
實(shí)現(xiàn)
- ?
function isIE() {
if (!!window.ActiveXObject || "ActiveXObject" in window) {
var version = getIEVersion();
return version;
} else {
return -1;
}
}
function getIEVersion () {
var reg = /(Trident.*rv\:|MSIE\s)((\d+)\.0)/;
var uaString = navigator.userAgent;
var versionMatch = uaString.match(reg);
if (versionMatch) {
return versionMatch[3];
}
}
- ?
function setCookie(c_name,value,expiredays)
{
var exdate=new Date()
exdate.setDate(exdate.getDate()+expiredays)
document.cookie=c_name+ "=" +escape(value)+
((expiredays==null) ? "" : ";expires="+exdate.toGMTString())
}
function getCookie(c_name)
{
if (document.cookie.length>0)
{
c_start=document.cookie.indexOf(c_name + "=")
if (c_start!=-1)
{
c_start=c_start + c_name.length+1
c_end=document.cookie.indexOf(";",c_start)
if (c_end==-1) c_end=document.cookie.length
return unescape(document.cookie.substring(c_start,c_end))
}
}
return "";
}
補(bǔ)充
暫無(wú)
AJAX
要求
function ajax(url, options) {
// your implement
}
// 使用示例:
ajax(
'http://localhost:8080/server/ajaxtest',
{
data: {
name: 'simon',
password: '123456'
},
onsuccess: function (responseText, xhr) {
console.log(responseText);
}
}
);
options是一個(gè)對(duì)象假栓,里面可以包括的參數(shù)為:
- type: post或者get,可以有一個(gè)默認(rèn)值
- data: 發(fā)送的數(shù)據(jù)晌畅,為一個(gè)鍵值對(duì)象或者為一個(gè)用&連接的賦值字符串
- onsuccess: 成功時(shí)的調(diào)用函數(shù)
- onfail: 失敗時(shí)的調(diào)用函數(shù)
思路
這個(gè)也沒(méi)啥好說(shuō)的但指,了解一下xhr對(duì)象就能寫(xiě)了。
實(shí)現(xiàn)
function ajax(url, options) {
//新建一個(gè)XHR對(duì)象
var xmlhttp;
if (window.XMLHttpRequest) {
// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp = new XMLHttpRequest();
} else {
// code for IE6, IE5
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
//若沒(méi)有設(shè)置type抗楔,則默認(rèn)為get
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
options.onsuccess();
} else {
options.onfail();
}
type = options.type || get;
xmlhttp.open(type, url, true);
if (type === "get") {
xmlhttp.send();
} else {
xmlhttp.send(options.data);
}
}
補(bǔ)充
這里補(bǔ)充一下readyState和status的幾種狀態(tài)
屬性 | 描述 |
---|---|
readyState | 0: 請(qǐng)求未初始化 1: 服務(wù)器連接已建立 2: 請(qǐng)求已接收 3: 請(qǐng)求處理中 4: 請(qǐng)求已完成棋凳,且響應(yīng)已就緒 |
status | 200: "OK" 404: 未找到頁(yè)面 |