首頁 > web前端 > js教程 > 原生JS實作表格排序

原生JS實作表格排序

小云云
發布: 2017-12-06 14:52:03
原創
2959 人瀏覽過

最近在學習js的表格排序,沒想到看不起眼的表格排序其實卻隱含了眾多JS知識點。在這裡記錄一下此次學習過程。希望對大家也有幫助。

完整的表格排序涉及了下列這些知識點:

  • call方法使用

  • sort方法深入

  • 資料綁定

  • DOM映射

下面詳細的總結一下這些知識點,最後結合這些知識點數實作下面這樣一個表格排序案例。
原生JS實作表格排序

完整的案例原始碼:https://github.com/daweihe/JS...

一、知識點總結

1、call方法使用

call方法的作用是改變方法中的this指向。

call這個方法是定義在Function.prototype的方法。我們定義的任何一個函數都可以認為它是Function這個類別的一個實例。那就可以透過實例的__proto__屬性來找到所屬類別的原型。任何一個函數都可以呼叫callapply等方法。

先來看一個例子:

var obj = {
    name : 'JS'
}

function testCall () {
    console.log(this);
}

testCall.call( obj );     // {name: "JS"}
登入後複製

首先函數testCall透過原型鏈尋找機制找到call方法執行,call方法在執行過程中把呼叫call方法這個函數實例中的this都改變成call的第一個參數,接下來呼叫call方法的這個實例函數執行。

看兩個題目:

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,相当于执行参数
登入後複製

call方法在執行的時候,call方法的第一個參數是用來改變this的,而從第二個參數開始都是傳給呼叫call的函數的參數。

在非嚴格模式下,給call方法不傳遞參數、或傳遞null、undefined後,this都是指向window

sum.call(); //window
sum.call(null); //window
sum.call(undefined); //window
登入後複製
登入後複製

嚴格模式下call執行的時候和非嚴格模式不同:

sum.call(); //undefined
sum.call(null); //null
sum.call(undefined); //undefined
登入後複製
登入後複製

下面使用call方法實作一個類別數組轉換為陣列的方法:

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類似的方法還有apply和bind方法,這裡簡單總結一下。

apply方法的作用和call方法一模一樣,只是傳參的形式不太一樣,apply將函數的參數用數組包裹起來:

function sum(num1, num2) {
    console.log(num2 + num1);
    console.log(this);
}

sum.apply(null,[100,200]);
登入後複製
登入後複製

bind方法同樣也是用來改變this關鍵字的,但是它只是只改變this指向,不立即執行呼叫this的函數。

function sum(num1, num2) {
    console.log(num2 + num1);
    console.log(this);
}

var obj = {name : &#39;zx&#39;}

var temp = sum.bind(obj);   //temp已经是被改变了this的函数
temp(100,200);              //当我们需要的时候才执行


//或者像这样处理
var temp = sum.bind(null, 100, 200);
temp();
登入後複製
登入後複製

bind方法體現了js中的預處理思想。

2、sort排序深入

我們知道陣列的sort#方法只能排序10以內的陣列。如果需要排序的陣列中存在大於10的數字,我們就需要在sort方法中傳入回呼函數,常見的是這樣:

ary.sort(function (a,b) {
    return a - b;
});
登入後複製
登入後複製

這樣就能實現陣列的升序排序。那麼這樣排序的原理到底是什麼呢?

對於傳入的兩個參數:a代表的是找到的陣列中的目前項,b代表的是目前項的後一項。

  • return a -b : 若a大於b,回傳結果,a與b交換位置。如果a小於b,那麼a和b位置不變。 這是升序排序

  • return b -a : 如果b大於a,回傳結果,a與b交換位置。如果a小於b,那麼a和b位置不變。 這是降序排序

了解了基本原理後,對於這樣一個二維數組,如何實現按年齡排序?

var persons = [{
    name:&#39;dawei&#39;,
    age:55
},{
    name:&#39;ahung&#39;,
    age:3
},{
    name:&#39;maomi&#39;,
    age:2
},{
    name:&#39;heizi&#39;,
    age:78
},{
    name:&#39;afu&#39;,
    age:32
}];
登入後複製
登入後複製

其實很簡單:

ary.sort(function(a,b){
    return a.age - b.age;
});
登入後複製
登入後複製

如果按姓名排序,則要涉及字串的localeCompare()方法:

ary.sort(function(a,b){
    return a.name.localeCompare(b.name);
});
登入後複製
登入後複製

# name.localeCompare()這個方法會根據兩個字串的字母進行比較,如果前一個字串的第一個字母在24個英文字母中出現的位置比後一個字串的第一個字符出現的位置靠前,則認定第一個字串小,傳回-1。如果出現的位置靠後,則認定第一個字串大,回傳1。如果所比較的字元相等。則比較下一個字元。

這個方法很實用,常用於按姓氏排序,對於漢字,該方法會自動將漢字轉換為漢語拼音進行比較。

3、資料綁定

在js中一般使用動態綁定或拼接字串的方式實作資料綁定。

動態綁定:

//ary为需要添加到页面中的数据数组
var op = document.getElementById("box");//获取容器
var myUl = op.getElementsByTagName("ul")[0];//获取列表

var arrLength = ary.length;
for (var i = 0;i < arrLength ; i ++)
{  //动态创建元素
    var oli = document.createElement("li");
    oli.innerHTML = &#39;<span>' + (i + 5) + '</span>' + ary[i].title;
    myUl.appendChild(oli);//动态添加元素
}
登入後複製
登入後複製

每新增一次就會造成一次DOM回流,如果資料量過大,這樣會嚴重影響效能。

關於DOM的回流與重繪,推薦大家來看看這篇文章:http://www.css88.com/archives...

拼接字串:

var str = "";
for(var i=0; i<ary.length; i++){
    str += &#39;<li>';
    str += '<span>';
    str += (i+5);
    str += '</span>';
    str += ary[i].title;
    str += '</li>';
}

myUl.innerHTML += str;
登入後複製
登入後複製

這種方式雖然只造成一次回流,但是它會去除原來存在的元素中所有的事件和屬性。如果我們為清單中的li標籤新增滑鼠移入,背景變色的事件,那麼這種方法會使這個事件失效。

為了解決上面的兩種資料綁定方法所帶來的問題,我們使用文件碎片來新增資料。

var frg = document.createDocumentFragment();//创建文档碎片
for (var i =0; i <ary.length ;i ++ ){
    var li = document.createElement("li");
    li.innerHTML = '<span>' + ( i + 5 ) + '</span>' + ary[i].title;
    frg.appendChild(li);//将数据动态添加至文档碎片中
}
myUl.appendChild(frg); //将数据一次性添加到页面中
frg = null;  //释放内存
登入後複製
登入後複製

這樣即只引起一次DOM回流,又會保留原來存在的事件。

4、DOM映射

DOM映射机制:所谓映射,就是指两个元素集之间元素相互“对应”的关系。页面中的标签集合和在JS中获取到的元素对象(元素集合)就是这样的关系。如果页面中的HTML标签结构发送变化,那么集合中对应的内容也会跟着自动改变。

<ul id="myul">
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
</ul>
登入後複製
登入後複製

对于这样一个列表使用下列脚本:

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列表会变成下面这样:

<ul id="myul">
    <li>5</li>
    <li>4</li>
    <li>3</li>
    <li>2</li>
    <li>1</li>
</ul>
登入後複製
登入後複製

我们看到列表的长度依然是5,只是位置颠倒了。这是因为每个li标签和JS中获取的标签对象存在一个对应关系,当某个标签被重新插入到页面中时,页面中对应的标签会移动到插入的位置。这就是DOM映射。

二、实现表格排序

1、使用ajax获取数据

之所以使用动态获取数据,是为了使用文档碎片绑定数据。

var res = ''; //声明一个全局变量,接收数据
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);
登入後複製
登入後複製

此时数据就保存在了res这个全局变量之中。

2、使用文档碎片绑定数据

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、对表格进行排序

这里涉及的点较多

//为两列添加点击事件
for (let i = 0; i < ths.length; i++) {
    let curTh = ths[i];
    curTh.sortFlag = -1; //用于对列进行升降序排列
    curTh.index = i; //记录当前点击列的索引,便于排序操作
    if (curTh.className == &#39;sort&#39;) {
        curTh.onclick = function() {
            sort.call(this); //改变排序函数内this的指向,让其指向当前点击列
        }
    }
}


//排序方法
function sort() {
    //对数组元素进行排序
    let target = this; //这里将this取出,因为在sort方法里需要使用该this,但是sort方法里的this是调用方法的数组
    this.sortFlag *= -1; //1 代表升序   -1代表降序
    let ary = listToArray(bodyTrs); //获取body数据
    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;
        }
    });
    //把排好序的数组重新写入页面
    let frg = document.createDocumentFragment();
    for (let i = 0; i < ary.length; i++) {
        rg.appendChild(ary[i]);
    }
    tbody.appendChild(frg);
    frg = null;

    //点击某列时,要将其他列的排序标志恢复为-1,让下次再点击任意一个标签时都是默认是升序排列
    for (let i = 0; i < ths.length; i++) {
        if (ths[i] != this) {
            ths[i].sortFlag = -1;
        }
    }
}
登入後複製

表格排序应用很常见,在面试中也会有这样的题目。这个小案例做下来,受益匪浅。这是我在学习的某峰学院的JS课程中的一个案例,如果对JS掌握不扎实的同学,欢迎保存:链接: https://pan.baidu.com/s/1jHVy8Uq 密码: v4jk。如果链接失效,加Q群领取:154658901


最近在學習js的表格排序,沒想到看不起眼的表格排序其實卻隱含了眾多JS知識點。在這裡記錄一下此次學習過程。希望對大家也有幫助。

完整的表格排序涉及了下列這些知識點:

  • call方法使用

  • sort方法深入

  • 資料綁定

  • DOM映射

下面詳細的總結一下這些知識點,最後結合這些知識點數實作下面這樣一個表格排序案例。
原生JS實作表格排序

完整的案例原始碼:https://github.com/daweihe/JS...

一、知識點總結

1、call方法使用

call方法的作用是改變方法中的this指向。

call這個方法是定義在Function.prototype的方法。我們定義的任何一個函數都可以認為它是Function這個類別的一個實例。那就可以透過實例的__proto__屬性來找到所屬類別的原型。任何一個函數都可以呼叫callapply等方法。

先來看一個例子:

var obj = {
    name : &#39;JS&#39;
}

function testCall () {
    console.log(this);
}

testCall.call( obj );     // {name: "JS"}
登入後複製

首先函數testCall透過原型鏈尋找機制找到call方法執行,call方法在執行過程中把呼叫call方法這個函數實例中的this都改變成call的第一個參數,接下來呼叫call方法的這個實例函數執行。

看兩個題目:

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,相当于执行参数
登入後複製

call方法在執行的時候,call方法的第一個參數是用來改變this的,而從第二個參數開始都是傳給呼叫call的函數的參數。

在非嚴格模式下,給call方法不傳遞參數、或傳遞null、undefined後,this都是指向window

sum.call(); //window
sum.call(null); //window
sum.call(undefined); //window
登入後複製
登入後複製

嚴格模式下call執行的時候和非嚴格模式不同:

sum.call(); //undefined
sum.call(null); //null
sum.call(undefined); //undefined
登入後複製
登入後複製

下面使用call方法實作一個類別數組轉換為陣列的方法:

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類似的方法還有apply和bind方法,這裡簡單總結一下。

apply方法的作用和call方法一模一樣,只是傳參的形式不太一樣,apply將函數的參數用數組包裹起來:

function sum(num1, num2) {
    console.log(num2 + num1);
    console.log(this);
}

sum.apply(null,[100,200]);
登入後複製
登入後複製

bind方法同樣也是用來改變this關鍵字的,但是它只是只改變this指向,不立即執行呼叫this的函數。

function sum(num1, num2) {
    console.log(num2 + num1);
    console.log(this);
}

var obj = {name : &#39;zx&#39;}

var temp = sum.bind(obj);   //temp已经是被改变了this的函数
temp(100,200);              //当我们需要的时候才执行


//或者像这样处理
var temp = sum.bind(null, 100, 200);
temp();
登入後複製
登入後複製

bind方法體現了js中的預處理思想。

2、sort排序深入

我們知道陣列的sort#方法只能排序10以內的陣列。如果需要排序的陣列中存在大於10的數字,我們就需要在sort方法中傳入回呼函數,常見的是這樣:

ary.sort(function (a,b) {
    return a - b;
});
登入後複製
登入後複製

這樣就能實現陣列的升序排序。那麼這樣排序的原理到底是什麼呢?

對於傳入的兩個參數:a代表的是找到的陣列中的目前項,b代表的是目前項的後一項。

  • return a -b : 若a大於b,回傳結果,a與b交換位置。如果a小於b,那麼a和b位置不變。 這是升序排序

  • return b -a : 如果b大於a,回傳結果,a與b交換位置。如果a小於b,那麼a和b位置不變。 這是降序排序

了解了基本原理後,對於這樣一個二維數組,如何實現按年齡排序?

var persons = [{
    name:&#39;dawei&#39;,
    age:55
},{
    name:&#39;ahung&#39;,
    age:3
},{
    name:&#39;maomi&#39;,
    age:2
},{
    name:&#39;heizi&#39;,
    age:78
},{
    name:&#39;afu&#39;,
    age:32
}];
登入後複製
登入後複製

其實很簡單:

ary.sort(function(a,b){
    return a.age - b.age;
});
登入後複製
登入後複製

如果按姓名排序,則要涉及字串的localeCompare()方法:

ary.sort(function(a,b){
    return a.name.localeCompare(b.name);
});
登入後複製
登入後複製

# name.localeCompare()這個方法會根據兩個字串的字母進行比較,如果前一個字串的第一個字母在24個英文字母中出現的位置比後一個字串的第一個字符出現的位置靠前,則認定第一個字串小,傳回-1。如果出現的位置靠後,則認定第一個字串大,回傳1。如果所比較的字元相等。則比較下一個字元。

這個方法很實用,常用於按姓氏排序,對於漢字,該方法會自動將漢字轉換為漢語拼音進行比較。

3、資料綁定

在js中一般使用動態綁定或拼接字串的方式實作資料綁定。

動態綁定:

//ary为需要添加到页面中的数据数组
var op = document.getElementById("box");//获取容器
var myUl = op.getElementsByTagName("ul")[0];//获取列表

var arrLength = ary.length;
for (var i = 0;i < arrLength ; i ++)
{  //动态创建元素
    var oli = document.createElement("li");
    oli.innerHTML = &#39;<span>' + (i + 5) + '</span>' + ary[i].title;
    myUl.appendChild(oli);//动态添加元素
}
登入後複製
登入後複製

每新增一次就會造成一次DOM回流,如果資料量過大,這樣會嚴重影響效能。

關於DOM的回流與重繪,推薦大家來看看這篇文章:http://www.css88.com/archives...

拼接字串:

var str = "";
for(var i=0; i<ary.length; i++){
    str += &#39;<li>';
    str += '<span>';
    str += (i+5);
    str += '</span>';
    str += ary[i].title;
    str += '</li>';
}

myUl.innerHTML += str;
登入後複製
登入後複製

這種方式雖然只造成一次回流,但是它會去除原來存在的元素中所有的事件和屬性。如果我們為清單中的li標籤新增滑鼠移入,背景變色的事件,那麼這種方法會使這個事件失效。

為了解決上面的兩種資料綁定方法所帶來的問題,我們使用文件碎片來新增資料。

var frg = document.createDocumentFragment();//创建文档碎片
for (var i =0; i <ary.length ;i ++ ){
    var li = document.createElement("li");
    li.innerHTML = '<span>' + ( i + 5 ) + '</span>' + ary[i].title;
    frg.appendChild(li);//将数据动态添加至文档碎片中
}
myUl.appendChild(frg); //将数据一次性添加到页面中
frg = null;  //释放内存
登入後複製
登入後複製

這樣即只引起一次DOM回流,又會保留原來存在的事件。

4、DOM映射

DOM映射机制:所谓映射,就是指两个元素集之间元素相互“对应”的关系。页面中的标签集合和在JS中获取到的元素对象(元素集合)就是这样的关系。如果页面中的HTML标签结构发送变化,那么集合中对应的内容也会跟着自动改变。

<ul id="myul">
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
</ul>
登入後複製
登入後複製

对于这样一个列表使用下列脚本:

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列表会变成下面这样:

<ul id="myul">
    <li>5</li>
    <li>4</li>
    <li>3</li>
    <li>2</li>
    <li>1</li>
</ul>
登入後複製
登入後複製

我们看到列表的长度依然是5,只是位置颠倒了。这是因为每个li标签和JS中获取的标签对象存在一个对应关系,当某个标签被重新插入到页面中时,页面中对应的标签会移动到插入的位置。这就是DOM映射。

二、实现表格排序

1、使用ajax获取数据

之所以使用动态获取数据,是为了使用文档碎片绑定数据。

var res = ''; //声明一个全局变量,接收数据
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);
登入後複製
登入後複製

此时数据就保存在了res这个全局变量之中。

2、使用文档碎片绑定数据

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、对表格进行排序

这里涉及的点较多

//为两列添加点击事件
for (let i = 0; i < ths.length; i++) {
    let curTh = ths[i];
    curTh.sortFlag = -1; //用于对列进行升降序排列
    curTh.index = i; //记录当前点击列的索引,便于排序操作
    if (curTh.className == 'sort') {
        curTh.onclick = function() {
            sort.call(this); //改变排序函数内this的指向,让其指向当前点击列
        }
    }
}


//排序方法
function sort() {
    //对数组元素进行排序
    let target = this; //这里将this取出,因为在sort方法里需要使用该this,但是sort方法里的this是调用方法的数组
    this.sortFlag *= -1; //1 代表升序   -1代表降序
    let ary = listToArray(bodyTrs); //获取body数据
    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;
        }
    });
    //把排好序的数组重新写入页面
    let frg = document.createDocumentFragment();
    for (let i = 0; i < ary.length; i++) {
        rg.appendChild(ary[i]);
    }
    tbody.appendChild(frg);
    frg = null;

    //点击某列时,要将其他列的排序标志恢复为-1,让下次再点击任意一个标签时都是默认是升序排列
    for (let i = 0; i < ths.length; i++) {
        if (ths[i] != this) {
            ths[i].sortFlag = -1;
        }
    }
}
登入後複製

以上内容就是原生JS实现表格排序,希望能帮助到大家。

js学习总结经典小案例之表格排序

jquery中tablesorter表格排序组件是如何使用的?

js表格排序实例详解(支持int,float,date,string四种数据类型)

以上是原生JS實作表格排序的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板