JS要点整理-12

124 阅读6分钟

表格案例

渲染表格——字符串

反引号``内添加可换行字符串,用${}引入变量

            //动态渲染表格1
                    var table = document.querySelector('table')
        var str = `
            <tr>
                <th>编号</th>
                <th>姓名</th>
                <th>年龄</th>
                <th>城市</th>
            </tr>
        ` 
        function bindHtml() {
            for(var i = 0; i < data.length; i++) {
            str += `
            <tr>
                <td>${data[i].id}</td>
                <td>${data[i].name}</td>
                <td>${data[i].age}</td>
                <td>${data[i].city}</td>
            </tr>
            `
        }
        table.innerHTML = str
        }
        bindHtml()

创建+添加

Object.keys(对象名)——对象的属性名形成的数组

document.createElement('tr')——创建tr

myTr.appendChild(myTd)——把td加到tr中

        function bindHtml() {
            // 根据 数据, 创建 表格内容
            for (var i = 0; i < data.length; i++) {
                // 1. 创建一个 tr 标签, 后续用于追加到 table
                var myTr = document.createElement('tr')

                // console.log(data[i])
                // console.log(Object.keys(data[i]))
                // 获取到 对象内部 有 多少个属性
                var dataKeys = Object.keys(data[i])
                // 根据 对象内部有多少个属性, 决定创建多少个 td
                for (var k = 0; k < dataKeys.length; k++) {
                    // 创建 td
                    var myTd = document.createElement('td')

                    // console.log(dataKeys[k]) // 获取到 对象中的 所有属性名
                    // console.log(data[i])    // 获取到 每一个对象

                    /**
                     *  data[i] === {id: 1, name: '张三', age: 25, city: '北京'}
                     *  dataKeys[k] ===  他的值就是上述对象的某一个 属性名
                     * 
                     *  通过 属性名 是能够获取到 对象中的 属性值
                     *
                     *      语法: 对象[属性名]
                     *      根据语法并结合当前 案例:        data[i][dataKeys[k]]
                    */
                    // myTd.innerText = data[i][dataKeys[k]]
                    var obj = data[i]       // 获取到 对象
                    var key = dataKeys[k]   // 获取到 属性名
                    myTd.innerText = obj[key]


                    // 将 处理好的 td 追加到 tr 中
                    myTr.appendChild(myTd)
                }


                // console.log(myTr)
                // 将创建出来的 tr 追加到 tablevar tbody = document.querySelector('tbody')
                tbody.appendChild(myTr)
            }

        }

        bindHtml()

全选

一个新的主流的开发思想

数据驱动视图,即我们的任何操作不应该直接更改 真实 DOM 节点 (不应该直接修改标签),而是做一些操作的时候, 完成后直接修改数据, 然后用新数据重新渲染页面。

  1. 方法一(不推荐)
        var data = [
            {
                status: true,
                id: 1,
                name: '张三',
                age: 25,
                city: '北京',
            },
            {
                status: false,
                id: 2,
                name: '李四',
                age: 30,
                city: '上海',
            },
            {
                status: true,
                id: 3,
                name: '王五',
                age: 22,
                city: '杭州',
            }
        ]

        // 页面打开时调用渲染函数
        bindHtml()

        // 创建一个渲染函数
        function bindHtml() {
            // 在页面渲染前, 先清空之前的页面
            document.querySelector('table').innerHTML = ""
            // 在页面渲染前, 先计算出有多少个数据被选中, 用于决定是否选中全选
            var checkedNum = 0  // 存储当前数据中 有多少个数据被选中了
            for (var q = 0; q < data.length; q++) {
                // if (data[q].status === true) {
                //     checkedNum++
                // }
                data[q].status && checkedNum++      // 如果当前对象的status属性为 true, 那么代表当前对象被选中了, 所以计数器 + 1
            }

            
            // ==================创建表头内容开始==================
            var headerArr = ['选择', '编号', '姓名', '年龄', '城市']
            var headerTr = document.createElement('tr')         // 当前tr只需要创建一个, 内部放一些 th 也就是 表头
            for (var j = 0; j < headerArr.length; j++) {        // 根据数据批量创建表头
                var headerTh = document.createElement('th')     // 创建一个表头
                if (j === 0) {                                  // 如果 j === 0, 代表循环第一次执行, 那么这个单元格的内容, 应该是一个多选框
                    // headerTh.innerHTML = '<input type="checkbox" class="check_all" checked >'
                    var inp = document.createElement('input')   // 创建一个 input 标签, 注意: 默认为 输入框
                    inp.type = 'checkbox'                       // 如果没有这一行那么创建的默认是一个 单行文本输入框
                    inp.className = 'check_all'                 // 因为一会需要添加事件, 所以这里提前给这个选择框添加一个类名标识
                    inp.checked = checkedNum === data.length    // 根据 现有选中的数据数量和总数据数量对比, 如果相同代表所有数据被选中, 那么选中全选按钮
                    headerTh.appendChild(inp)                   // 将 多选框 添加到 th 中
                } else {
                    headerTh.innerHTML = headerArr[j]           // 给表头单元格赋值一个文本
                }
                headerTr.appendChild(headerTh)                  // 将 th 单元格 放到 tr 单元行 内
            }
            document.querySelector('table').appendChild(headerTr)
            // ==================创建表头内容结束==================

            
            // ==================创建表格内容开始==================
            for (var i = 0; i < data.length; i++) {
                var myTr = document.createElement('tr')
                var dataKeys = Object.keys(data[i])
                for (var k = 0; k < dataKeys.length; k++) {
                    var myTd = document.createElement('td')
                    // 如果 展示的内容是一个 布尔值, 那么应该展示一个多选框
                    if (data[i][dataKeys[k]] === true || data[i][dataKeys[k]] === false) {
                        var inpItems = document.createElement('input')  // 创建一个 input 标签, 注意: 默认为单行输入框
                        inpItems.type = 'checkbox'                      // 更改类型为 多选框
                        inpItems.className = 'check_item'               // 给多选框添加类名, 因为一会需要添加事件
                        inpItems.checked = data[i][dataKeys[k]]         // 给多选框 添加一个默认的选中状态, 需要根据数据提供的来展示
                        myTd.appendChild(inpItems)                      // 将 多选框 添加到 td 单元格中
                    } else {
                        myTd.innerText = data[i][dataKeys[k]]           // 如果 展示的内容不是 布尔值, 那么正常展示文本
                    }
                    myTr.appendChild(myTd)
                }
                document.querySelector('table').appendChild(myTr)
            }
            // ==================创建表格内容结束==================

            
            // ==================全选功能开始==================
            // 一个不太好的方式 重新获取 全选按钮, 将来有更好的方式
            var checkAll = document.querySelector('.check_all')
            checkAll.onclick = function (e) {
                // console.log(checkAll)
                // console.log(e.target)
    
                // 1. 修改数据
                for (var i = 0; i < data.length; i++) {
                    data[i].status = e.target.checked
                }
    
                console.log(data)
                // 2. 重新渲染页面
                bindHtml()
            }
            // ==================全选功能结束==================
        }

事件冒泡

当你点击了一个元素的时候, 相当于触发了这个元素的点击事件,当一个元素的点击事件被触发的时候, 那么按照JS中事件冒泡的特性,会将这个事件的触发也传递给自己的父级

冒泡会从 点击的元素开始, 一直到页面最顶层的元素,哪怕你的元素没有绑定点击事件, 也会触发事件冒泡

事件委托

事件委托就是利用事件冒泡的原理,将所有子元素的一个事件(点击事件), 委托给共同的父级

  1. 方法二(事件委托)
        var data = [
            {
                status: true,
                id: 1,
                name: '张三',
                age: 25,
                city: '北京',
            },
            {
                status: false,
                id: 2,
                name: '李四',
                age: 30,
                city: '上海',
            },
            {
                status: true,
                id: 3,
                name: '王五',
                age: 22,
                city: '杭州',
            }
        ]

        // 页面打开时调用渲染函数
        bindHtml()

        // 创建一个渲染函数
        function bindHtml() {
            // 在页面渲染前, 先清空之前的页面
            document.querySelector('table').innerHTML = ""
            // 在页面渲染前, 先计算出有多少个数据被选中, 用于决定是否选中全选
            var checkedNum = 0  // 存储当前数据中 有多少个数据被选中了
            for (var q = 0; q < data.length; q++) {
                // if (data[q].status === true) {
                //     checkedNum++
                // }
                data[q].status && checkedNum++      // 如果当前对象的status属性为 true, 那么代表当前对象被选中了, 所以计数器 + 1
            }


            // ==================创建表头内容开始==================
            var headerArr = ['选择', '编号', '姓名', '年龄', '城市']
            var headerTr = document.createElement('tr')         // 当前tr只需要创建一个, 内部放一些 th 也就是 表头
            for (var j = 0; j < headerArr.length; j++) {        // 根据数据批量创建表头
                var headerTh = document.createElement('th')     // 创建一个表头
                if (j === 0) {                                  // 如果 j === 0, 代表循环第一次执行, 那么这个单元格的内容, 应该是一个多选框
                    // headerTh.innerHTML = '<input type="checkbox" class="check_all" checked >'
                    var inp = document.createElement('input')   // 创建一个 input 标签, 注意: 默认为 输入框
                    inp.type = 'checkbox'                       // 如果没有这一行那么创建的默认是一个 单行文本输入框
                    inp.className = 'check_all'                 // 因为一会需要添加事件, 所以这里提前给这个选择框添加一个类名标识
                    inp.checked = checkedNum === data.length    // 根据 现有选中的数据数量和总数据数量对比, 如果相同代表所有数据被选中, 那么选中全选按钮
                    headerTh.appendChild(inp)                   // 将 多选框 添加到 th 中
                } else {
                    headerTh.innerHTML = headerArr[j]           // 给表头单元格赋值一个文本
                }
                headerTr.appendChild(headerTh)                  // 将 th 单元格 放到 tr 单元行 内
            }
            document.querySelector('table').appendChild(headerTr)
            // ==================创建表头内容结束==================


            // ==================创建表格内容开始==================
            for (var i = 0; i < data.length; i++) {
                var myTr = document.createElement('tr')
                var dataKeys = Object.keys(data[i])
                for (var k = 0; k < dataKeys.length; k++) {
                    var myTd = document.createElement('td')
                    // 如果 展示的内容是一个 布尔值, 那么应该展示一个多选框
                    if (data[i][dataKeys[k]] === true || data[i][dataKeys[k]] === false) {
                        var inpItems = document.createElement('input')  // 创建一个 input 标签, 注意: 默认为单行输入框
                        inpItems.type = 'checkbox'                      // 更改类型为 多选框
                        inpItems.className = 'check_item'               // 给多选框添加类名, 因为一会需要添加事件
                        inpItems.checked = data[i][dataKeys[k]]         // 给多选框 添加一个默认的选中状态, 需要根据数据提供的来展示
                        inpItems.dataset.id = i + 1                     // 用于给 标签 添加一个 标识, 能够知道将来点的是那个
                        myTd.appendChild(inpItems)
                        // 将 多选框 添加到 td 单元格中
                    } else {
                        myTd.innerText = data[i][dataKeys[k]]           // 如果 展示的内容不是 布尔值, 那么正常展示文本
                    }
                    myTr.appendChild(myTd)
                }




                document.querySelector('table').appendChild(myTr)
            }
            // ==================创建表格内容结束==================
        }

        // ==================全选功能 (事件委托) 开始==================
        var myTable = document.querySelector('table')
        myTable.onclick = function (e) {
            if (e.target.className === 'check_all') {
                // 1. 修改数据
                for (var i = 0; i < data.length; i++) {
                    data[i].status = e.target.checked
                }

                // 2. 重新渲染页面
                bindHtml()
            }
            if (e.target.className === 'check_item') {
                // 1. 修改数据
                // console.log(e.target.dataset.id - 0)    // 因为 我们获取到的 数据被 浏览器转换为 字符串了, 然后自己数据中是数字类型, 所以需要 - 0
                for (var k = 0; k < data.length; k++) {
                    if (e.target.dataset.id - 0 === data[k].id) {
                        // data[k].status = !data[k].status
                        data[k].status = e.target.checked
                    }
                }

                // 2. 重新渲染页面
                bindHtml()
            }
        }

排序

        // ==================排序功能开始==================
        var sortBtn = document.querySelector('#sort_btn')
        var num = 0
        sortBtn.onclick = function () {
            num++   // 每次点击的时候 修改计数器的数字
            if (num === 1) { // 1. 第一次点击 按照 年龄的从小到大
                data.sort(function (a, b) {return a.age - b.age})
            } else if (num === 2) { // 2. 第二次点击 按照 年龄的从大到小
                data.sort(function (a, b) {return b.age - a.age})
            } else { // 3. 第三次点击 恢复默认排序(按照ID从小到大)
                data.sort(function (a, b) {return a.id - b.id})
                num = 0 // 清零计数器, 下次可以重新执行逻辑
            }
            // 上述的 if 语句内 处理完数据后, 重新渲染页面
            bindHtml()
        }
        // ==================排序功能结束==================

新增

        // ==================新增功能开始==================

        // 1. 点击 "新增按钮" 打开 遮罩层 和 信息框
        var addBtn = document.querySelector('#add_btn')
        var overlay = document.querySelector('.overlay')
        var addUserBox = document.querySelector('.add_user_box')

        addBtn.onclick = function () {
            overlay.classList.remove('close')
            addUserBox.classList.remove('close')
        }

        // 2. 点击 "新增用户按钮" 收集用户输入的信息, 然后创建一个对象, 追加到原数组的末尾, 最后重新渲染页面
        var addUserBtn = document.querySelector('.add_user_btn')
        addUserBtn.onclick = function () {
            // 2.1 获取到用户输入的信息
            var nameEl = document.querySelector('.username')
            var ageEl = document.querySelector('.userage')
            var cityEl = document.querySelector('.usercity')

            var nameValue = nameEl.value
            var ageValue = ageEl.value
            var cityValue = cityEl.value

            // 2.2 安全校验 (非空校验, 规则校验/正则校验)
            if (nameValue === '' || ageValue === '' || cityValue === '') return alert('您的输入框有一个为空, 请补全输入框')

            // 2.3 如果代码能够正常执行到这里, 说明输入框的的内容不是空的, 所以可以组装一个对象, 然后追加到原数组的末尾
            var obj = {
                status: false,
                id: data.length + 1,
                name: nameValue,
                age: ageValue - 0,
                city: cityValue,
            }
            data.push(obj)

            // 2.4 调用渲染函数
            bindHtml()

            // 2.5 清空弹框数据
            nameEl.value = ''
            ageEl.value = ''
            cityEl.value = ''

            // 2.6 关闭弹框
            closeBoxFn()
        }


        // 3. 点击 "取消按钮" 关闭 遮罩层和信息框
        var closeBoxBtn = document.querySelector('.close_box_btn')
        closeBoxBtn.onclick = closeBoxFn

        function closeBoxFn () {
            overlay.classList.add('close')
            addUserBox.classList.add('close')
        }

        // ==================新增功能结束==================

获取平均年龄

        // ==================获取平均年龄开始==================
        var getAge = document.querySelector('#get_age')
        getAge.onclick = function () {
            // 1. 获取到年龄的总和
            var sum = 0
            for (var i = 0; i < data.length; i++) {
                sum += data[i].age
            }

            // 2. 总和 / 总数量 === 年龄的平均值
            sum = sum / data.length

            // 3. 提示给用户年龄的平均值
            alert(sum.toFixed(1))
        }
        // ==================获取平均年龄结束==================

本地存储

var data = JSON.parse(window.localStorage.getItem('data')) || [
            {
                status: true,
                id: 1,
                name: '张三',
                age: 25,
                city: '北京',
            },
            {
                status: false,
                id: 2,
                name: '李四',
                age: 30,
                city: '上海',
            },
            {
                status: true,
                id: 3,
                name: '王五',
                age: 22,
                city: '杭州',
            }
        ]
        //渲染函数中
window.localStorage.setItem('data', JSON.stringify(data))

最终版

        var oldData = [
            {
                status: false,
                id: 1,
                name: '张三',
                age: 25,
                city: '北京',
            },
            {
                status: false,
                id: 2,
                name: '李四',
                age: 30,
                city: '上海',
            },
            {
                status: false,
                id: 3,
                name: '王五',
                age: 22,
                city: '杭州',
            }
        ] 
        if(!window.localStorage.getItem('arr')) {
            window.localStorage.setItem('arr', JSON.stringify(oldData))
        }
        var data = JSON.parse(localStorage.getItem('arr'))
        var table = document.querySelector('table')
        function bindHtml() {
            document.querySelector('table').innerHTML = ''
            var sum = 0
            for (var z = 0; z < data.length; z++) {
                data[z].status && sum++
            }
            //表头
            var headerArr = ['选择', '编号', '姓名', '年龄', '城市']   
            var headerTr = document.createElement('tr')
            for(var i = 0; i < headerArr.length; i++) {
                var headerTh = document.createElement('th')
                if (i === 0) {
                    var inp = document.createElement('input')
                    inp.className = 'check_all'
                    inp.type ='checkbox'
                    inp.checked = sum === data.length
                    headerTh.appendChild(inp)
                } else {
                    headerTh.innerHTML = headerArr[i]
                }
                headerTr.appendChild(headerTh)
            }
            table.appendChild(headerTr)
            //表格
            for (var j = 0; j < data.length; j++) {
                var myTr = document.createElement('tr')
                var dataKeys = Object.keys(data[j])
                for (var k = 0; k < dataKeys.length; k++) {
                    var myTd = document.createElement('td')
                    if (data[j][dataKeys[k]] === true || data[j][dataKeys[k]] === false) {
                        var inp2 = document.createElement('input')
                        inp2.className = 'check_item'
                        inp2.type = 'checkbox'
                        inp2.dataset.id = j + 1
                        inp2.checked = data[j][dataKeys[k]]
                        myTd.appendChild(inp2)
                    } else {
                        myTd.innerText = data[j][dataKeys[k]]
                    }
                    myTr.appendChild(myTd)
                }
                table.appendChild(myTr)
            }  
            window.localStorage.setItem('arr', JSON.stringify(data))
        }
        bindHtml()
        //全选
        table.onclick = function (e) {
            if (e.target.className === 'check_all') {
                for (var a = 0; a < data.length; a++) {
                    data[a].status = e.target.checked
                }
                bindHtml()
            }
            if(e.target.className === 'check_item') {
                for (var b = 0; b < data.length; b++) {
                    if (e.target.dataset.id - 0 === data[b].id) {
                        data[b].status = e.target.checked
                    }
                }
                bindHtml()               
            }
        }
        //排序
        var sortBtn = document.querySelector('.sort_btn')
        var sum2 = 0
        sortBtn.onclick = function () {
            sum2++
            if (sum2 === 1) {
                data.sort(function (a, b) {
                    return a.age - b.age
                })
            } else if (sum2 === 2) {
                data.sort(function (a, b) {
                    return b.age - a.age
                })
            } else {
                data.sort(function (a, b) {
                    return a.id - b.id
                })
                sum2 = 0
            } 
            bindHtml()                     
        }
        //新增
        var AddBtn = document.querySelector('.add_btn')
        var outer = document.querySelector('.outer')
        var cancelBtn = document.querySelector('.cancel_btn')
        var addingBtn = document.querySelector('.adding_btn')
        AddBtn.onclick = function () {
            outer.classList.remove('close')
        }
        cancelBtn.onclick = function () {
            outer.classList.add('close')
        }
        addingBtn.onclick = function () {
            var nameEl = document.querySelector('.username')
            var ageEl = document.querySelector('.userage')
            var cityEl = document.querySelector('.usercity')
            var nameValue = nameEl.value
            var ageValue = ageEl.value
            var cityValue = cityEl.value
            if (nameValue === '' || ageValue === '' || cityValue === '') return alert('请您检查并补全输入框')
            var obj = {
                status: false,
                id: data.length + 1,
                name: nameValue,
                age: ageValue - 0,
                city: cityValue,
            }
            data.push(obj)
            bindHtml()
            nameEl.value = ''
            ageEl.value = ''
            cityEl.value = ''
            outer.classList.add('close')
        }
        //平均数
        var ageBtn = document.querySelector('.age_btn')
        var backBtn = document.querySelector('.back_btn')
        var outer2 = document.querySelector('.outer2')
        var span = document.querySelector('span')
        ageBtn.onclick = function () {
            outer2.classList.remove('close')
            var average = 0
            for (var c = 0; c < data.length; c++) {
                average += data[c].age
            }
            span.innerText = (average / data.length).toFixed(2)
        }
        backBtn.onclick = function () {
            outer2.classList.add('close')
        }
        //删除
                    // ==================删除功能(事件委托)开始==================
            if (e.target.className === "del_btn") {
                // var res = window.confirm('您确定删除吗?')
                // console.log('现在要删除一个内容', res)
                if (!confirm('您确定删除吗?')) return

                // 改变数据
                console.log('如果代码能够执行到这里, 说明需要删除', e.target.dataset.id)
                // data.splice(0, 1)
                // data.splice(1, 1)
                // data.splice(2, 1)
                data.splice(e.target.dataset.id - 1, 1)

                // 重新渲染页面
                bindHtml()
            }
            // ==================删除功能(事件委托)结束==================