摘要:最近在學(xué)習(xí)的表格排序,沒(méi)想到看不起眼的表格排序?qū)嶋H上卻暗含了眾多知識(shí)點(diǎn)。二實(shí)現(xiàn)表格排序使用獲取數(shù)據(jù)之所以使用動(dòng)態(tài)獲取數(shù)據(jù),是為了使用文檔碎片綁定數(shù)據(jù)。
最近在學(xué)習(xí)js的表格排序,沒(méi)想到看不起眼的表格排序?qū)嶋H上卻暗含了眾多JS知識(shí)點(diǎn)。在這里記錄一下此次學(xué)習(xí)過(guò)程。希望對(duì)大家也有所幫助。
完整的表格排序涉及了下列這些知識(shí)點(diǎn):
call方法使用
sort方法深入
數(shù)據(jù)綁定
DOM映射
下面詳細(xì)的總結(jié)一下這些知識(shí)點(diǎn),最后結(jié)合這些知識(shí)點(diǎn)實(shí)現(xiàn)下面這樣一個(gè)表格排序案例。
完整的案例源碼:https://github.com/daweihe/JS...
一、知識(shí)點(diǎn)總結(jié) 1、call方法使用call方法的作用是改變方法中的this指向。
call這個(gè)方法是定義在Function.prototype的方法。我們定義的任何一個(gè)函數(shù)都可以認(rèn)為它是Function這個(gè)類(lèi)的一個(gè)實(shí)例。那么就可以通過(guò)實(shí)例的__proto__屬性找到所屬類(lèi)的原型。任何一個(gè)函數(shù)都可以調(diào)用call和apply等方法。
先來(lái)看一個(gè)例子:
var obj = { name : "JS" } function testCall () { console.log(this); } testCall.call( obj ); // {name: "JS"}
首先函數(shù)testCall通過(guò)原型鏈查找機(jī)制找到call方法執(zhí)行,call方法在執(zhí)行過(guò)程中把調(diào)用call方法這個(gè)函數(shù)實(shí)例中的this都改變成call的第一個(gè)參數(shù),接下來(lái)調(diào)用call方法的這個(gè)實(shí)例函數(shù)執(zhí)行。
看兩個(gè)題目:
function fn1() { console.log(1); console.log(this); } function fn2() { console.log(2); console.log(this); } fn1.call(fn2); //this -> fn2 fn1.call.call(fn2); //這里的call是改變function.__proto__.call的call方法中的this,相當(dāng)于執(zhí)行參數(shù)
call方法在執(zhí)行的時(shí)候,call方法的第一個(gè)參數(shù)是用來(lái)改變this的,而從第二個(gè)參數(shù)開(kāi)始都是傳給調(diào)用call的函數(shù)的參數(shù)。
在非嚴(yán)格模式下,給call方法不傳遞參數(shù)、或者傳遞null、undefined后,this都是指向window。
sum.call(); //window sum.call(null); //window sum.call(undefined); //window
嚴(yán)格模式下call執(zhí)行的時(shí)候和非嚴(yán)格模式不同:
sum.call(); //undefined sum.call(null); //null sum.call(undefined); //undefined
下面使用call方法實(shí)現(xiàn)一個(gè)類(lèi)數(shù)組轉(zhuǎn)換為數(shù)組的方法:
function listToArray (likeAry) { var ary = []; try { ary = Array.prototype.slice.call(likeAry); } catch (e) { for (var i = 0; i < likeAry.length; i ++) { ary[ary.length] = likeAry[i]; } } return ary; }
和call類(lèi)似的方法還有apply和bind方法,這里簡(jiǎn)單總結(jié)一下。
apply方法的作用和call方法一模一樣,只是傳參的形式不太一樣,apply將函數(shù)的參數(shù)用數(shù)組包裹起來(lái):
function sum(num1, num2) { console.log(num2 + num1); console.log(this); } sum.apply(null,[100,200]);
bind方法同樣也是用來(lái)改變this關(guān)鍵字的,但是它只是僅僅改變this指向,不立即執(zhí)行調(diào)用this的函數(shù)。
function sum(num1, num2) { console.log(num2 + num1); console.log(this); } var obj = {name : "zx"} var temp = sum.bind(obj); //temp已經(jīng)是被改變了this的函數(shù) temp(100,200); //當(dāng)我們需要的時(shí)候才執(zhí)行 //或者像這樣處理 var temp = sum.bind(null, 100, 200); temp();
bind方法體現(xiàn)了js中的預(yù)處理思想。
2、 sort排序深入我們知道數(shù)組的sort方法只能排序10以?xún)?nèi)的數(shù)組。如果需要排序的數(shù)組中存在大于10的數(shù)字,我們就需要向sort方法中傳入回調(diào)函數(shù),常見(jiàn)的是這樣:
ary.sort(function (a,b) { return a - b; });
這樣就能實(shí)現(xiàn)數(shù)組的升序排序。那么這樣排序的原理到底是什么呢?
對(duì)于傳入的兩個(gè)參數(shù):a代表的是找到的數(shù)組中的當(dāng)前項(xiàng),b代表的是當(dāng)前項(xiàng)的后一項(xiàng)。
return a -b : 如果a大于b,返回結(jié)果,a與b交換位置。如果a小于b,那么a和b位置不變。 這是升序排序
return b -a : 如果b大于a,返回結(jié)果,a與b交換位置。如果a小于b,那么a和b位置不變。 這是降序排序
了解了基本原理后,對(duì)于這樣一個(gè)二維數(shù)組,如何實(shí)現(xiàn)按年齡排序?
var persons = [{ name:"dawei", age:55 },{ name:"ahung", age:3 },{ name:"maomi", age:2 },{ name:"heizi", age:78 },{ name:"afu", age:32 }];
其實(shí)很簡(jiǎn)單:
ary.sort(function(a,b){ return a.age - b.age; });
如果按姓名排序,則要涉及字符串的localeCompare()方法:
ary.sort(function(a,b){ return a.name.localeCompare(b.name); });
name.localeCompare()這個(gè)方法會(huì)根據(jù)兩個(gè)字符串的字母進(jìn)行比較,如果前一個(gè)字符串的第一個(gè)字母在24個(gè)英文字母中出現(xiàn)的位置比后一個(gè)字符串的第一個(gè)字符出現(xiàn)的位置靠前,則認(rèn)定第一個(gè)字符串小,返回-1。如果出現(xiàn)的位置靠后,則認(rèn)定第一個(gè)字符串大,返回1。如果所比較的字符相等。則比較下一個(gè)字符。
這個(gè)方法很實(shí)用,常用于按姓氏排序,對(duì)于漢字,該方法會(huì)自動(dòng)將漢字轉(zhuǎn)換為漢語(yǔ)拼音進(jìn)行比較。
3、數(shù)據(jù)綁定在js中一般使用動(dòng)態(tài)綁定或者拼接字符串的方式實(shí)現(xiàn)數(shù)據(jù)綁定。
動(dòng)態(tài)綁定:
//ary為需要添加到頁(yè)面中的數(shù)據(jù)數(shù)組
var oDiv = document.getElementById("box");//獲取容器
var myUl = oDiv.getElementsByTagName("ul")[0];//獲取列表
var arrLength = ary.length;
for (var i = 0;i < arrLength ; i ++)
{ //動(dòng)態(tài)創(chuàng)建元素
var oli = document.createElement("li");
oli.innerHTML = "" + (i + 5) + "" + ary[i].title;
myUl.appendChild(oli);//動(dòng)態(tài)添加元素
}
每添加一次就會(huì)引起一次DOM回流,如果數(shù)據(jù)量過(guò)大,這樣則會(huì)嚴(yán)重影響性能。
關(guān)于DOM的回流與重繪,推薦大家看一下這篇文章:http://www.css88.com/archives...
拼接字符串:
var str = ""; for(var i=0; i"; str += ""; str += (i+5); str += ""; str += ary[i].title; str += ""; } myUl.innerHTML += str;
這種方式雖然只引起一次回流,但是它會(huì)去除原來(lái)存在的元素中所有的事件和屬性。如果我們?yōu)榱斜碇械膌i標(biāo)簽添加鼠標(biāo)移入,背景變色的事件,那么這種方法會(huì)使這個(gè)事件失效。
為了解決上面的兩種數(shù)據(jù)綁定方法帶來(lái)的問(wèn)題,我們使用文檔碎片來(lái)添加數(shù)據(jù)。
var frg = document.createDocumentFragment();//創(chuàng)建文檔碎片 for (var i =0; i" + ary[i].title; frg.appendChild(li);//將數(shù)據(jù)動(dòng)態(tài)添加至文檔碎片中 } myUl.appendChild(frg); //將數(shù)據(jù)一次性添加到頁(yè)面中 frg = null; //釋放內(nèi)存
這樣即只引起一次DOM回流,又會(huì)保留原來(lái)存在的事件。
4、DOM映射DOM映射機(jī)制:所謂映射,就是指兩個(gè)元素集之間元素相互“對(duì)應(yīng)”的關(guān)系。頁(yè)面中的標(biāo)簽集合和在JS中獲取到的元素對(duì)象(元素集合)就是這樣的關(guān)系。如果頁(yè)面中的HTML標(biāo)簽結(jié)構(gòu)發(fā)送變化,那么集合中對(duì)應(yīng)的內(nèi)容也會(huì)跟著自動(dòng)改變。
對(duì)于這樣一個(gè)列表使用下列腳本:
var myul = document.getElementById("myul"); var mylis = myul.getElementsByTagName("li"); for (var i = mylis.length - 1 ; i >= 0; i --) { myul.appendChild(mylis[i]); } console.log(mylis.length); // 5
將獲取到的列表元素反序重新插入ul中,那么ul列表會(huì)變成下面這樣:
我們看到列表的長(zhǎng)度依然是5,只是位置顛倒了。這是因?yàn)槊總€(gè)li標(biāo)簽和JS中獲取的標(biāo)簽對(duì)象存在一個(gè)對(duì)應(yīng)關(guān)系,當(dāng)某個(gè)標(biāo)簽被重新插入到頁(yè)面中時(shí),頁(yè)面中對(duì)應(yīng)的標(biāo)簽會(huì)移動(dòng)到插入的位置。這就是DOM映射。
二、實(shí)現(xiàn)表格排序 1、使用ajax獲取數(shù)據(jù)之所以使用動(dòng)態(tài)獲取數(shù)據(jù),是為了使用文檔碎片綁定數(shù)據(jù)。
var res = ""; //聲明一個(gè)全局變量,接收數(shù)據(jù) var xhr = new XMLHttpRequest(); xhr.open("get", "date.txt", false); xhr.onreadystatechange = function() { if (xhr.readyState == 4 && xhr.status == 200) { res = JSON.parse(xhr.responseText); } } xhr.send(null);
此時(shí)數(shù)據(jù)就保存在了res這個(gè)全局變量之中。
2、使用文檔碎片綁定數(shù)據(jù)var frg = document.createDocumentFragment(); for (let i = 0; i < res.length; i++) { var tr = document.createElement("tr"); for (key in res[i]) { var td = document.createElement("td"); td.innerHTML = res[i][key]; tr.appendChild(td); } frg.appendChild(tr); } tbody.appendChild(frg);3、對(duì)表格進(jìn)行排序
這里涉及的點(diǎn)較多
//為兩列添加點(diǎn)擊事件 for (let i = 0; i < ths.length; i++) { let curTh = ths[i]; curTh.sortFlag = -1; //用于對(duì)列進(jìn)行升降序排列 curTh.index = i; //記錄當(dāng)前點(diǎn)擊列的索引,便于排序操作 if (curTh.className == "sort") { curTh.onclick = function() { sort.call(this); //改變排序函數(shù)內(nèi)this的指向,讓其指向當(dāng)前點(diǎn)擊列 } } } //排序方法 function sort() { //對(duì)數(shù)組元素進(jìn)行排序 let target = this; //這里將this取出,因?yàn)樵趕ort方法里需要使用該this,但是sort方法里的this是調(diào)用方法的數(shù)組 this.sortFlag *= -1; //1 代表升序 -1代表降序 let ary = listToArray(bodyTrs); //獲取body數(shù)據(jù) ary = ary.sort(function(a, b) { let one = a.cells[target.index].innerHTML; let two = b.cells[target.index].innerHTML; let oneNum = parseFloat(one); let twoNum = parseFloat(two); if (isNaN(oneNum) || isNaN(two)) { return one.localeCompare(two) * target.sortFlag; } else { return (oneNum - twoNum) * target.sortFlag; } }); //把排好序的數(shù)組重新寫(xiě)入頁(yè)面 let frg = document.createDocumentFragment(); for (let i = 0; i < ary.length; i++) { rg.appendChild(ary[i]); } tbody.appendChild(frg); frg = null; //點(diǎn)擊某列時(shí),要將其他列的排序標(biāo)志恢復(fù)為-1,讓下次再點(diǎn)擊任意一個(gè)標(biāo)簽時(shí)都是默認(rèn)是升序排列 for (let i = 0; i < ths.length; i++) { if (ths[i] != this) { ths[i].sortFlag = -1; } } }
表格排序應(yīng)用很常見(jiàn),在面試中也會(huì)有這樣的題目。這個(gè)小案例做下來(lái),受益匪淺。這是我在學(xué)習(xí)的某峰學(xué)院的JS課程中的一個(gè)案例,如果對(duì)JS掌握不扎實(shí)的同學(xué),歡迎保存:鏈接: https://pan.baidu.com/s/1jHVy8Uq 密碼: v4jk。如果鏈接失效,加Q群領(lǐng)取:154658901。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/90221.html
摘要:最近在學(xué)習(xí)的表格排序,沒(méi)想到看不起眼的表格排序?qū)嶋H上卻暗含了眾多知識(shí)點(diǎn)。二實(shí)現(xiàn)表格排序使用獲取數(shù)據(jù)之所以使用動(dòng)態(tài)獲取數(shù)據(jù),是為了使用文檔碎片綁定數(shù)據(jù)。 最近在學(xué)習(xí)js的表格排序,沒(méi)想到看不起眼的表格排序?qū)嶋H上卻暗含了眾多JS知識(shí)點(diǎn)。在這里記錄一下此次學(xué)習(xí)過(guò)程。希望對(duì)大家也有所幫助。 完整的表格排序涉及了下列這些知識(shí)點(diǎn): call方法使用 sort方法深入 數(shù)據(jù)綁定 DOM映射 下面...
摘要:個(gè)人前端文章整理從最開(kāi)始萌生寫(xiě)文章的想法,到著手開(kāi)始寫(xiě),再到現(xiàn)在已經(jīng)一年的時(shí)間了,由于工作比較忙,更新緩慢,后面還是會(huì)繼更新,現(xiàn)將已經(jīng)寫(xiě)好的文章整理一個(gè)目錄,方便更多的小伙伴去學(xué)習(xí)。 showImg(https://segmentfault.com/img/remote/1460000017490740?w=1920&h=1080); 個(gè)人前端文章整理 從最開(kāi)始萌生寫(xiě)文章的想法,到著手...
摘要:我也意識(shí)到在學(xué)習(xí)一個(gè)框架前,將框架的思想和原生的實(shí)現(xiàn)進(jìn)行對(duì)比有多么重要。這個(gè)是目前為止一個(gè)大的框架思路,當(dāng)然還要再進(jìn)行每個(gè)功能的細(xì)分。表格將上一步的并集數(shù)據(jù)顯示出來(lái)渲染分表格,表格有一個(gè)表頭,用于展示商品的種類(lèi)地區(qū)以及每月的銷(xiāo)售情況。 前言:由于剛?cè)肭岸藭r(shí)間并不長(zhǎng),之前最近一直處在學(xué)習(xí)的階段,現(xiàn)在準(zhǔn)備找工作,回首看看之前學(xué)的,發(fā)現(xiàn)了很多的瑕疵。我分析覺(jué)得主要原因在于之前有些東西學(xué)的太快...
閱讀 3601·2021-11-23 09:51
閱讀 1473·2021-11-04 16:08
閱讀 3547·2021-09-02 09:54
閱讀 3616·2019-08-30 15:55
閱讀 2594·2019-08-30 15:54
閱讀 958·2019-08-29 16:30
閱讀 2047·2019-08-29 16:15
閱讀 2317·2019-08-29 14:05