js动态表格创建

搬运来源: https://blog.csdn.net/F_eve/article/details/111062039

js动态表格创建

  • 首先创建一个表格的结构
  • 首先你要创建一个HTML和js文件
  • 创建元素 javaScript
  • 创建表格的类
  • 创建data和首页中的获取HTML中的容器
  • 在HTML里引入js文件
  • 创建一个方法
  • 创建删除按钮
  • 再创建一个方法
  • 改写数组原型方法
  • 删除按钮的操作流程
  • 增加数据列表了

首先创建一个表格的结构
下面展示一些 构思出来的HTML。

一开始我构思出来一个表格格式,来进行实现动态表格的

<table>
            <thead>
                <tr>
                    <th></th>
                    <th></th>
                    <th></th>
                    <th></th>
                </tr>
            </thead>
            <tbody>
                <tr>
                    <td></td>
                </tr>
            </tbody>
</table>

首先你要创建一个HTML和js文件

 

 

 创建好以后把HTML基本语法写好

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>

</body>
</html>

创建元素 javaScript

下面展示一些 面向对象

创建一个js文件 首先用面向对象的方式来构建元素 这样会方便一些

// 面向对象
    class Tolls {
        // 创建元素的方法
        VertexElement = element => document.createElement(element);
        // 获取元素的方法
        $ = selecrtor => document.querySelector(selecrtor) || document.getElementById(selector);
    }

创建表格的类
下面展示一些 通过数据动态的创建表格。

定义一个表格的类然后和上面的面向对象进行联动
这定义一个元素或者创建一个元素就可以在这个对象里使用者个元素和进行创建对象
container :是在HTML中的容器 这个实例放入到对象里
data:是一个直接创建的数组里面的内容到后面会放入到表格里实现增删的功能

// 创建表格的类
    class CreateTable extends Tolls {
        /**
         * 
         * @param {String} container 表格需要展示的容器,是一个字符串
         * @param {data} data 用于创建表格的数据
         */
        constructor(container , data) {
            super();
            // 容器元素
            this.el = this.$(container)
            // data数据
            this.data = data;   
        }
  }

创建data和首页中的获取HTML中的容器

下面展示一些 创建数组和引入

这是在HTML创建的容器
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div class="container"></div>
</body>
</html>

记得在对象外面创建一全局数组 然后传递data和HTML中的容器给CreateTable对象

let data = [
        {
            id:1,
            姓名: '李磊',
            年龄: 30,
            爱好: '吃'
        },
        {
            id:2,
            姓名: '黄磊',
            年龄: 30,
            爱好: '喝'
        },
        {
            id:3,
            姓名: '师资',
            年龄: 30,
            爱好: '玩'
        },
    ]
let t = new CreateTable(".container" , data)

在HTML里引入js文件

下面展示一些 引入

记得没设置window.onload就不要放在head里
所有HTML代码执行完以后在引入js
所有把js代码放到最后执行
<script src="./js/index.js"></script>

创建一个方法

下面展示一些 创建数据结构

在 CreateTable 类里创建一个方法
记得写在constructor下面进行创建
因为我们要调用里面的元素和容器
需要先执行,把容器和data放入表格类里面来进行操作

在用于创建表格的结构和传输数据内容

// 一个方法,专门用于创建表格的结构
constructTable() {

}

创建完成后我们要在上面进行调用,测试 这个方法是否出错,以便我们修改

// 创建表格的类
    class CreateTable extends Tolls {
        /**
         * 
         * @param {String} container 表格需要展示的容器,是一个字符串
         * @param {data} data 用于创建表格的数据
         */
        constructor(container , data) {
            super();
            // 容器元素
            this.el = this.$(container)
            // data数据
            this.data = data;
           
           // 调用创建表格:测试的作用
           this.constructTable()
        }
  }

由构思出来表格格式来进行创建表格了

<table>
            <thead>
                <tr>
                    <th></th>
                    <th></th>
                    <th></th>
                    <th></th>
                </tr>
            </thead>
            <tbody>
                <tr>
                    <td></td>
                </tr>
            </tbody>
</table>
constructTable() {
            // 通过这组数据把结构创建出来
            this.table = this.VertexElement('table')
            this.thead = this.VertexElement('thead')
            this.tbody = this.VertexElement('tbody')
}

通过data循环创建出来几个tr和td

this.data.forEach(item => {
                
})
在通过item创建tr和td
根据data数组里面的每一项来创建tr
有几个item就有几个tr
再根据data数组里面每一项里面的每一个数据有几个就创建几个td
td是看data里面有几个属性就创建几个
上面data里面我创建了:id 姓名 年龄 爱好 4个属性
for(let k in item) 几个属性就循环几次
let td = this.VertexElement('td') 属性里面的值放入td里
tr.appendChild(td)把td放到tr里面去
这个el就是我们在上面给容器定义一个简单值可以方便拿取和调用
this.data.forEach(item => {
   // 创建Tr
  let tr = this.VertexElement('tr')
           // 创建td
          for(let k in item) {
           let td = this.VertexElement('td')
           td.innerHTML = item[k]
           tr.appendChild(td)
         }
         // 把tr要添加到tbody中
        this.tbody.appendChild(tr)
        // 把tbody添加到table
       this.table.appendChild(this.tbody)             
})
// table要添加到页面中
this.el.appendChild(this.table)
以下是上面写完的完整代码

下面展示一些 javaScript

// 面向对象
class Tolls {
    // 创建元素的方法
    VertexElement = element => document.createElement(element);
    // 获取元素的方法
    $ = selecrtor => document.querySelector(selecrtor) || document.getElementById(selector);
}

// 通过数据动态的来创建表格

    // 创建表格的类
    class CreateTable extends Tolls {
        /**
         * 
         * @param {String} container 表格需要展示的容器,是一个字符串
         * @param {data} data 用于创建表格的数据
         */
        constructor(container , data , isDelBtn = false) {
            super();
            // 容器元素
            this.el = this.$(container)
            // data数据
            this.data = data;

            // 调用创建表格
            this.constructTable()
        }
         // 一个方法,专门用于创建表格的结构
         constructTable() {
            // 通过这组数据把结构创建出来
            this.table = this.VertexElement('table')
            this.thead = this.VertexElement('thead')
            this.tbody = this.VertexElement('tbody')

            this.data.forEach(item => {
                let tr = this.VertexElement('tr')
                for(let k in item) {
                    let td = this.VertexElement('td')
                    td.innerHTML = item[k]
                    tr.appendChild(td)
                }
                 // 把tr要添加到tbody中
                this.tbody.appendChild(tr)
            })
            // 把tbody添加到table
            this.table.appendChild(this.tbody)
            // table要添加到页面中
            this.el.appendChild(this.table)
        }
        

    }

    let data = [
        {
            id:1,
            姓名: '李磊',
            年龄: 30,
            爱好: '吃'
        },
        {
            id:2,
            姓名: '黄磊',
            年龄: 30,
            爱好: '喝'
        },
        {
            id:3,
            姓名: '师资',
            年龄: 30,
            爱好: '玩'
        },
    ]


    let t = new CreateTable(".container" , data )

下面展示一些 HTML

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div class="container"></div>
</body>
</html>
<script src="./js/index.js"></script>

写完过后运行一下是否可以在页面显示数据

 

 

如果以上没问题那么我们就开是给列表一些样式 

<style>
        table {
            width: 500px;
            text-align: center;
            border-collapse: collapse;
        }
        td {
            border: 1px solid hotpink;
        }
    </style>

 

 

 显示结果如下

 

 

 现在这个表格是没有表头,接下来我们要来操作表头,把表头添加上去

首先要在第一个循环下面判断一下thead里面有没有子元素

 

 

 这个循环下面来写入判断

!this.thead.children.length
如果thead.lenggth是等于0,我给他加一个!号就变成了true了这样就可以执行if里面的语句
说明里面没有子元素,没有子元素那我们就在if里面写创建子元素的代码
为什么要创建tr呢,因为表头也是th所以我们要把th放到tr里面
也就是一开是构思我们要创建出来
写的格式和上面创建表的格式大差不差所以按照上面的思路来写是可以的
// 判断thead有没有子元素,如果没有就创建
if(!this.thead.children.length) {
    //创建tr
  let trr = this.VertexElement('tr')
      for(let j in item) {
      // 创建th
      let th = this.VertexElement('th')
      th.innerText = j
      trr.appendChild(th)
      }
      // 把trr和th添加到thead中
      this.thead.appendChild(trr)
}
在把thead添加到table里面去
 this.table.appendChild(thead)
// 判断thead有没有子元素,如果没有就创建
                if(!this.thead.children.length) {
                    let trr = this.VertexElement('tr')
                    for(let j in item) {
                        // 创建th
                        let th = this.VertexElement('th')
                        th.innerText = j
                        trr.appendChild(th)
                    }
                    // 把trr和th添加到thead中
                    this.thead.appendChild(trr)
                }
                 // 把tr要添加到tbody中
                this.tbody.appendChild(tr)
            })
             // 把tbody添加到table
             this.table.appendChild(this.tbody)
            //把thead也要添加到table
            this.table.appendChild(this.thead)
            // table要添加到页面中
            this.el.appendChild(this.table)
        }
运行一下看看页面是否显示表头

 

 

显示过后我想给表头添加样式那就很点单了
<style>
        table {
            width: 500px;
            text-align: center;
            border-collapse: collapse;
        }
        td,th {
            border: 1px solid hotpink;
        }
    </style>

 

 

 

创建删除按钮

下面展示一些 增添删除

在js里增添button
我用动态方式来打开或关闭删除按钮

首先先创建一个开关
this.isDelBtn = true;
constructor(container , data , isDelBtn = false) {
            super();
            // 容器元素
            this.el = this.$(container)
            // data数据
            this.data = data;
            // 是否需要删除按钮
            this.isDelBtn = true;
            this.
            // 调用创建表格
            this.constructTable()
        }
来判断一个是否需要按钮
继续在第一层循环里添加判断
if(this.isDelBtn) {
                    // 多创建一列
                    let td = this.VertexElement('td')
                    // 创建一个按钮
                    let delBtn = this.VertexElement('button')

                    delBtn.innerText = '删除'

                    td.appendChild(delBtn)

                    tr.appendChild(td)
                }

下面展示一些 javaScri

上述写完以后的代码
// 面向对象
class Tolls {
    // 创建元素的方法
    VertexElement = element => document.createElement(element);
    // 获取元素的方法
    $ = selecrtor => document.querySelector(selecrtor) || document.getElementById(selector);
}

// 通过数据动态的来创建表格

    // 创建表格的类
    class CreateTable extends Tolls {
        /**
         * 
         * @param {String} container 表格需要展示的容器,是一个字符串
         * @param {data} data 用于创建表格的数据
         */
        constructor(container , data , isDelBtn = false) {
            super();
            // 容器元素
            this.el = this.$(container)
            // data数据
            this.data = data;
            // 是否需要删除按钮
            this.isDelBtn = true;
            // 调用创建表格
            this.constructTable()
        }
         // 一个方法,专门用于创建表格的结构
         constructTable() {
            // 通过这组数据把结构创建出来
            this.table = this.VertexElement('table')
            this.thead = this.VertexElement('thead')
            this.tbody = this.VertexElement('tbody')

            this.data.forEach(item => {
                let tr = this.VertexElement('tr')
                for(let k in item) {
                    let td = this.VertexElement('td')
                    td.innerHTML = item[k]
                    tr.appendChild(td)
                }

                if(this.isDelBtn) {
                    // 多创建一列
                    let td = this.VertexElement('td')
                    // 创建一个按钮
                    let delBtn = this.VertexElement('button')

                    delBtn.innerText = '删除'

                    td.appendChild(delBtn)

                    tr.appendChild(td)
                }

                // 判断thead有没有子元素,如果没有就创建
                if(!this.thead.children.length) {
                    let trr = this.VertexElement('tr')
                    for(let j in item) {
                        // 创建th
                        let th = this.VertexElement('th')
                        th.innerText = j
                        trr.appendChild(th)
                    }
                    // 把trr和th添加到thead中
                    this.thead.appendChild(trr)
                }
                 // 把tr要添加到tbody中
                this.tbody.appendChild(tr)
            })
             // 把tbody添加到table
             this.table.appendChild(this.tbody)
            //把thead也要添加到table
            this.table.appendChild(this.thead)
            // table要添加到页面中
            this.el.appendChild(this.table)
        }
        

    }

    let data = [
        {
            id:1,
            姓名: '李磊',
            年龄: 30,
            爱好: '吃'
        },
        {
            id:2,
            姓名: '黄磊',
            年龄: 30,
            爱好: '喝'
        },
        {
            id:3,
            姓名: '师资',
            年龄: 30,
            爱好: '玩'
        },
    ]


    let t = new CreateTable(".container" , data )
下面展示一些 HTML。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        table {
            width: 500px;
            text-align: center;
            border-collapse: collapse;
        }
        td,th {
            border: 1px solid hotpink;
        }
    </style>
</head>
<body>
    <div class="container"></div>
</body>
</html>
<script src="./js/index.js"></script>

执行结果

 

 

 

发现了问题没有表头,这就很尴尬。
就继续在判断子元素里面在多写一个判断表头的代码
// 判断thead有没有子元素,如果没有就创建
                if(!this.thead.children.length) {
                    let trr = this.VertexElement('tr')
                    for(let j in item) {
                        // 创建th
                        let th = this.VertexElement('th')
                        th.innerText = j
                        trr.appendChild(th)
                    }
                    if(this.isDelBtn){
                        // 多创建一个th
                        let th = this.VertexElement('th')
                        th.innerText = '操作'
                        trr.appendChild(th)
                    }
                    // 把trr和th添加到thead中
                    this.thead.appendChild(trr)
                }
                 // 把tr要添加到tbody中
                this.tbody.appendChild(tr)

 

 

 

当这个属性为false时就可以让删除按钮消失了
当这个属性为true时就可以让删除按钮显示
这样用户可以控制显示还是隐藏
像开关一样打开或者关闭

 

 

 

 

 

 

我们可以通过实例化的变量来控制开和关

在这里添加一个参数isDelBtn

 

   let t = new CreateTable(".container" , data , isDelBtn)
这个值默认是false
以后就可以动态的让它打开或者关闭了
 constructor(container , data , isDelBtn = false) {
            super();
            // 容器元素
            this.el = this.$(container)
            // data数据
            this.data = data;
            // 是否需要删除按钮
            this.isDelBtn = isDelBtn;
            // 调用创建表格
            this.constructTable()
        }
以后我们就可以在里面传个true就可以打开删除按钮了

 

 

 

    let t = new CreateTable(".container" , data , isDelBtn)

再创建一个方法

下面展示一些 render

创建一个方法用来渲染表格
// 渲染表格
        render() {
            
        }
这个调用创建表格放到我们新创建的方法里面

 

 

 

然后换成this.render()

 

 

 

放进render() 渲染表格里

 

 

 

接下来做删除按钮
我想法是不想操作dom,写完这个js之后,以后操作的时候,我只要通过数组的方法,就能动态的添加数据给表格里面,或删除里面的数据。
就要每次把表格重新挂载一下页面里面去
要注意把上一次的表格清掉,如果不清掉会发现你都表格会多出很多表格出来。
现在把索引和data中的数据绑定到按钮中

 

 

 

因为item是里面的每一条数据,绑定到按钮,就说明绑定到当前按钮那对应的数据了
理解:循环第一次都时候,就创建一个按钮,会把第一条都数据绑定到按钮的data属性里面
然后在把index绑定到里面去!

 

 

 

从上述图片进行修改,在这里添加index, 用括号进行增加(item,index)

 

 

 

在把index绑定到按钮上

 

 

 

这样做就可以方便的html文件里获取表格的按钮和按钮点击的数据
在到html再创建一个script

 

 

 

我们在html进去测试一下是否可以获取button

 

 

 

写过进行运行,进入控制台(Console)

 

 

 

 

 

 

看看是否获取到三个button按钮.
如果获取到那么在测试能不能点击按钮获取到每条按钮对应都数据

 

 

 

 

 

 

在运行返回控制台看看点击删除按钮,看看控制台有没有数据

 

 

 

 

 

 得出结果:

 

 

每一条删除按钮对应这每一条的数据
也可以拿取它都index
把data修改为index

 

 得出结果:

 

 

后面做删除都时候就可以调佣data数据就可以方便都删除数据了
理解:删除都时候只需要操作data数据,我们给data数据中某个数据给它删除掉,那么我表格会对应删除掉数据
我们就可以重新创建这个表格了。
在控制台给大家演示一下:数据删除的操作
在控制台操作代码
在控制台敲出:data.splice(1,1)
敲好按下回车

 

 

再从下面继续操作
编写:data
显出两调数据,说明删除了一条数据了

 

 

在调用:t.render()
回车,就会看见页面减少的数据在页面上显示了

 

 

会看见删除的上面多出一个表出来

 

 

因为我们重新创建了一个表出来,是删除的表,重新添加到页面上,没有把上面的表进行移除,所以会显示两张表.
render是从新创建表格,会继续添加到el里面去,所以我们在创建之前把之前都结构进行移除

找到render函数,在创建之前打印this.table看看是什么

 

 

打印结果:undefined

 

 

打印出来的是undefined,然后在创建之后打印一下看看有没有值

 

 

打印结果:有值

 

 

那么说明this.table在render函数里是可以获取到值的
所以我们只要在创建之前进行判断this.table是否存在,如果存在则移除this.table

 

 运行结果:

 

 

你就会看见页面就只有两条数据了

 

 

这就是删除的数据结构,我们会在后面会让它更加方便的进行删除

改写数组原型方法

创建一个observer函数

 

 

在observer函数里改造数组的原型
主要在里面改造两种方法
1.push方法
2.splice方法
在里写定义一个变量,来保存数组原来方法

 

 打印一下这两个的结果是什么

 

 然后在下面调用这个方法

 

 打印结果:

 

 这个显示的是我们当前原型上都方法。(虽然它在原型上但也是个数组)

 

 Array里面会以传进去的做为原型

 

 这一块就是传递的原型

 

 接下来遍历 arrMethods,

 

 

遍历arrMethods的目的是为了修改数组里面的push和splice
method是arrMethods里面定义的朴实和splice的方法
rewriteArrMethods[method]这句话的意思是:
rewriteArrMethods就是我们新创建的原型,它的作用就是替代Array.prototype,让它调用里它最近都原型上都push和splice,一旦调用它就会改变this指向
所以改变了this指向的方法所以this指向就变成了指向data这个数组了
 rewriteArrMethods[method] = function () {}
 而这层改造的函数的意义是为了调用之后的更新视图而准备的
这里修改push和splice这两个方法

 

 

接下来需要把arrMethods添加到data原型上
然后在打印this.data

 

 

点开你会发现里有有个prototype
里面有两个方法一个push另一个是splice

 

 

里面就改push和splice这两种方法
其他的方法都不改变

 

 

然后在控制台调用一下数组都方法
data.push(2)

打印结果:

 

为什么调用这方法返回都是4这个值
是因为4是它原本的值
然后我们把prototype改为__proto__
因为data是个实例,实例没有prototype属性,
到页面刷新一下就能看见结果了

 

 

打印结果:

 

 

 

然后我们在到控制台调用:data.push(2) 然后在调用 data.splice(2)

运行结果:

 

 

 

然而你会发现调用push和splice方法调用了但没有执行
oldArrMethods[method].apply(this, arguments)
这句话理解:
oldArrMethods[method]这句话相对于我们调用了数组的push方法
相对于我们data调用了数组的push和splice的方法,然后把push和splice的参数传递给arguments
那么我们这次调用

 

 

然后在到控制台进行操作

执行结果:

 

 执行结果:

 

 

现在就可以实现增加和删除了
oldArrMethods[method].apply(this, arguments)
这句话的理解是:oldArrMethods是数组原型的方法,里面是真正的push和splice的方法,this是指向data的,所以会真正的删除和添加数组。
然后下面在写上视图的更新,我们可以调用render就可以更新视图,只要data里面的数据变更了就随之而变更
在observer里添加_this这个变量从而保存实例上的this
这样就可以调用this里面的值来进行更新视图上面的变化

 

 

 

 

 

 然后写入_this.render()

 

 

然后在控制台继续操作
data.splice(1,2)
data.push({id:4,name:'历史',age:20})
就会实现视图和数组的响应

运行结果:

 

 

视图变化:

 

 

当push的时候你会发现,和格式,格格不入就会导致样式问题所以我们就可以在下面进行操作的时候就会对用户进行限制

运行结果:

 

 

视图变化:

 

把所有组件封装好后就可以调用。
后面写的时候就方便多了
然后你会发现当完成之后就会没有返回值,打印的都是undefined
所以我们要进行稍微的修改。
把结果返回去

 

 

在控制台在次打印结果会发现它有值了。

 

 

我们可以把按钮暴露在外面
所以我们现在要做的是把按钮暴露在实例上
首先在constructor里定义delBtns这个数组

注意!顺序记得把this.render()放到最后面在上面创建,因为:现有delBtns然后在调用render渲染页面才有按钮

 

 

然后列表和按钮都里把delBtn放到delBtns里
this.delBtn.push(delBtn)
就是把按钮放到实例上

 

 

 把最低下的observer方法放到constructor里面进行调用
 把最底下的observer删除

 

 把observer放到constructor里
进行深度观察

 

然后到html文件里下面script里面内容先清空
先在里重新写入
这是直接定义的一个容器准备放入传递按钮事件和增加事件
let t = new CreateTable()

 

 

要把container 和data,true传递封装好的数据里面
1,container 是上面div
2. data是我们马上迁移都数组
3. true是我们操作删除按钮显示与隐藏都控制

 

 

把js里面都data数组迁移到HTML文件里面

 

 

 然后打印一下t.belBtns

 

 运行结果:

 

 

 你会发现打印出来了3个按钮,就说明成功迁移了

删除按钮的操作流程

 

初次在html中new出了表格,let t 就是那表格实例
然后给t.delBtnClick传个函数进去
里面写上data.splice(this.index,1)
等于调用js里面面的splice的方法传递给删除按钮的事件
这样就很方便了,我们只要调用delBtnClick这个方法就可以删除列表了
接下来我们还要在js里面创建一个delBtnClick这样一个方法
我们在实例上添加一个delBtnClick的方法

 

 

js里的delBtnClick是用来给按钮绑定点击事件的
handle是事件处理函数,就是html里面的他t.delBtnClick里面传递给js里的delBtnClick的函数

 

 

之前我们每次调用了render函数都会调用constructTable这个函数,
只要我们每次调用constructTable这个函数都会把新创建出来的delBtn添加到this.delBtns这个数组里面,
然后我们在上面清掉this.delBtns,然后我们在把新的重新给它添加进去

 

 

我们要做CreateTable里定义一个函数
然后在删除按钮的绑定事件中可以写
this.delBtns.forEach(btn => {
            this.delBtnHandle = handle;
            btn.onclick = this.delBtnHandle;
   })
 这句话的理解是:
每个按钮都给它注册一个点击事件,然后点击删除按钮时会触发this.delBtnHandle
然后我们要在render函数里调用this.delBtnClick()这个方法
然后我们还要在this.delBtnClick传递一个事件处理函数进去,添加this.delBtnHandle放进去
得到this.delBtnClick(this.delBtnHandle)

为什么要把handle写在this.delBtnHandle里而不直接写呢?因为这句话是要把后面的删除的处理函数接上所以才这样写

 

 

 

 

 

 然后运行测试一下,返回到页面上,删除按钮就可以实现了。

 

 运行结果:

 

 

就会有删除的效果了!

增加数据列表了

首先在页面上增加样式表能添加的信息如:姓名、年龄、爱好、操作
我们在html的body中添加样式列表
输入过后运行一下!

 

 运行结果:

 

 

 

然后在html下面的script里封装一个dom的方法
let $ = selector => document.querySelector(selector)
这样我们就可以用$("add")获得元素了这样更加的方便快捷开发js
接着我们要给add注册点击事件了
$("#add").onclick = function () {}
点击就可以触发function里面的所有代码
而里需要写的是将收集到的数据变成一个对象放入到data
因为data中的数据是一个一个对象

 

 

然后我们要获取页面上用户输入到input里面的值!
获取输入框里面值我们要用到value属性

 

 

而$('#name')我在上面说到过,获取一id叫input的元素,而$是我们封装的dom方法

 

 

然后我们第一件事情就是我们能不能获取到这几个value值
log一下
console.log(name, age, hobby)
在input里输入的只要相互对应上

 

 

运行结果:

 

 

我们获取到了姓名、年龄、爱好,唯独没有id
因为id是我们要自己给它的而不用户给的,我们所添加的id是唯一的,所以我们先考虑一下怎么生成增加的id
我们let一个boj把我们所需要的添加到这个对象里面去
把姓名、年龄、爱好要相互对应起来
我是以键值对的形式来写的

 

 

然后我们处理一下id的生成
我就在控制台给你们操作一下看看是怎么生成它的id
Math.random().toString(32).substr(2)

运行结果:

 

 

你看显示的值都是唯一且不冲突、不重复的。
和你们说以什么意思:

Math.random()是随机生成一个数值:

 

加上toString(32)就变成了随机生成数值加上字母的一个值
toString(32)是把这个数转成32位进制的数
在这里插入图片描述
最后加上substr(2)就是把前面的0和点都去掉去后面的数。
就生成了一个随机值的ID了

在这里插入图片描述

所以我们就可以把id添加到obj里面了

 

 

然后我们打印一下obj看看是什么
log一下
console.log(obj)

运行结果:
在这里插入图片描述

看我们就可以获取到它的id、姓名、年龄、爱好这个对象了。

接下来我们只要把obj添加到data里面去就行了
 data.push(obj)

 

 

运行一下
然后输入里面的值,点击增加

在这里插入图片描述
就可以增加一个列表了

运行结果:
在这里插入图片描述

虽然我们可以实现增加和删除了差多没有其他了
但是我们还要优化一下需要增加过后清除一下输入框里面的值
所以我们在push过去清除input里面的值
让三个input元素的value值等于空就行了

 

 

输入值过后点击增加就可以看到增加过后输入框的值没有了

 

 

运行结果:

 

 

最后还有一个要就是输入框没有值的值也可以添加,我们要解决一下这个问题
你输入框什么都输入的情况下就会出现图中情况

点击增加

 

你会发现会添加多个空的列表
所以我们设置一下没有值的时候提示用户要填写所有的值才能进行增加列表

 

 

我们要进行一个非空验证
如果有一个为空的时候就要提示用户要把字段填完整,且不能添加列表
alert()是一个提示框
而return的作用是中断下面的逻辑,不让下面代码执行

 

 

看一下运行结果:
我们假装输错看看他会不会提示和不会增加列表

 

 

 

 

然后把我们的列表正常运行看看最终的结果:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
可以正常的增加和删除了,这就是一个完整的js动态列表创建!

完整代码:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<style>
    table {
        width: 500px;
        text-align: center;
        border-collapse: collapse;
    }

    td,
    th {
        border: 1px solid deeppink;
    }
</style>

<body>
    姓名:
    <input type="text" id="name">
    年龄:
    <input type="text" id="age">
    爱好:
    <input type="text" id="hobby">

    <button id="add">增加</button>

    <div class="container">

    </div>
</body>

</html>
<script src="./04.table.js"></script>
<script>
    let data = [
        {
            id: 1,
            姓名: '李雷',
            年龄: 30,
            爱好: '吃'
        },
        {
            id: 2,
            姓名: '李凝',
            年龄: 20,
            爱好: '喝'
        },
        {
            id: 3,
            姓名: '小明',
            年龄: 10,
            爱好: '写代码'
        },
    ]

    let t = new CreateTable('.container', data, true)
    //删除按钮点击事件,传递一个事件处理函数就可以了
    t.delBtnClick(function () {
        console.log(this)
        console.log(this.index)
        //删除逻辑
        data.splice(this.index, 1)
    })

    //封装一个获取dom的方法
    let $ = selector => document.querySelector(selector)

    //给增加按钮注册点击事件

    $("#add").onclick = function () {
        let name = $('#name').value;

        let age = $('#age').value

        let hobby = $("#hobby").value

        console.log(name, age, hobby)

        //非空验证 其中一个字段是空的 那就不能添加

        if (!name || !age || !hobby) { 
            alert('字段要填完整')
            return //中断下面的逻辑
        }

        //因为data中的数据是一个一个的对象,所以我们需要将收集到的数据变成一个对象
        //直接push到data就可以

        let obj = {
            id: Math.random().toString(32).substr(2),
            姓名: name,
            年龄: age,
            爱好: hobby
        }

        // console.log(obj)

        data.push(obj)
        //清空输入框的值
        $("#name").value = ""
        $("#age").value = ""
        $("#hobby").value = ""
    }

</script>
//面向对象

class Tools {
    //创建元素的方法
    _c = tagName => document.createElement(tagName)
    //获取元素的方法
    $ = selector => document.querySelector(selector) || document.getElementById(selector)
}

//通過數據动态的来创建表格

//创建表格的类
class CreateTable extends Tools {
    /**
     * 
     * @param {String} container 表格需要展示的容器,是一个字符串
     * @param {Array} data 用于创建表格的数据
     */
    constructor(container, data, isDelBtn = false) {
        super()
        //容器元素
        this.el = this.$(container)
        //data数据
        this.data = data;
        //是否需要删除按钮
        this.isDelBtn = isDelBtn;
        //保存按钮们
        this.delBtns = []
        this.render()
        //调用观察者
        this.observer()
        //删除按钮初始事件处理函数是null
        this.delBtnHandle = null;
    }

    //一个方法,专门用于创建表格的结构

    constructTable() {
        this.table = this._c('table');
        this.thead = this._c('thead')
        this.tbody = this._c('tbody')
        this.delBtns = [];//清除之前的按钮
        this.data.forEach((item, index) => {
            //创建tr
            let tr = this._c('tr')
            for (let k in item) {
                let td = this._c('td');
                td.innerHTML = item[k]
                tr.appendChild(td)
            }

            if (this.isDelBtn) {
                //多创建一列
                let td = this._c('td');
                //创建一个按钮
                let delBtn = this._c('button')
                delBtn.innerText = '删除'
                //我们把索引,和data中的数据绑定到这个按钮中
                delBtn.data = item;
                delBtn.index = index;
                td.appendChild(delBtn)
                tr.appendChild(td)
                //把创建出来的按钮放到实例上
                // console.log(this.delBtns)
               
                this.delBtns.push(delBtn)
            }

            //判断thead有没有子元素,如果没有就创建
            if (!this.thead.children.length) {
                let trr = this._c('tr')
                for (let j in item) {
                    //创建th
                    let th = this._c('th');
                    th.innerText = j
                    trr.appendChild(th)
                }
                if (this.isDelBtn) {
                    //多创建一个th
                    let th = this._c('th');
                    th.innerText = "操作"
                    trr.appendChild(th)
                }
                //把trr和th添加到thead中
                this.thead.appendChild(trr)
            }

            //把tr要添加到tbody中
            this.tbody.appendChild(tr)

        })

        //把tbody添加到table
        this.table.appendChild(this.tbody)
        //把thead也要添加到table
        this.table.appendChild(this.thead)
        //table要添加到页面中
        this.el.appendChild(this.table)
    }

    //给删除按钮绑定点击事件
    //handle就是事件处理函数
    delBtnClick(handle) {
        //将handle这个事件处理函数保存到实例上,以免丢失
        this.delBtnHandle = handle;
        // console.log(this.delBtns)
        //在这里给按钮绑定点击事件
        this.delBtns.forEach(btn => {
            btn.onclick = this.delBtnHandle;
        })
    }

    //观察data数据,改造数组原型的方法

    observer() {
        //保存实例的this
        let _this = this;
        //来上一个变量,保存数组原来的方法

        //我们主要改造两个方法 一个push 另一个是splice
        let arrMethods = ['push', 'splice']
        //保存数组原型上的方法
        let oldArrMethods = Array.prototype;
        //构建出一个新的原型,用于替换数组原来的方法
        let rewriteArrMethods = Object.create(oldArrMethods)

        // console.log(oldArrMethods)
        // console.log(rewriteArrMethods)
        arrMethods.forEach(method => {
            rewriteArrMethods[method] = function () {
                //这里面的this指向数组就是调用者
                console.log(method + "被调用了")
                //这一步的目的就是 改造之后没法真正的push和splice 所以我们需要调用数组真正的push 和splice
                let res = oldArrMethods[method].apply(this, arguments)
                //更新视图
                _this.render()
                return res
            }
        })

        //直接改写数组原型的方法
        this.data.__proto__ = rewriteArrMethods;

        // console.log(this.data)

    }

    //渲染表格
    render() {
        //先移除掉之前的结构
        if (this.table) {
            this.el.removeChild(this.table)
        }
        this.constructTable()
        //给新创建出来的按钮重新绑定事件
        this.delBtnClick(this.delBtnHandle)
        // console.log(this.table)
    }
}

 

posted @ 2022-06-23 11:01  小小小光子  阅读(2392)  评论(0编辑  收藏  举报