实现classList

实现classList前,先补充点知识

数组是特殊的对象,如何特殊?
  • 1.对象属性是数字(索引)
  • 2.有length属性
什么是类数组对象?
  • 1.具备数组的特征
  • 2.写法上跟数组一样,但不是数组,原型是Object。
类数组调用数组方法的方式
var o = {0: 'a', 1: 'b', 2: 'c', length: 3}
[].push.call(o, 'd')
对classList的理解

classList是所有可视化DOM实例的属性,也是用来设置DOM的类
classList属性的值是DOMTokenList类型,本质是一个集合对象,提供了add,remove,toggle等函数操作类

提示:classList属性对象可以轻松地完成jQuery对class相关的操作功能,但是IE10以下不兼容

接下来我们打造一个可以兼容所有浏览器的classList

第一步:如何给所有DOM元素都加上我们自己定义的neClassList属性

首先思考所有DOM元素继承于哪里

<div id="mydiv" class="classA classB classAB"></div>

我们可以通过constructor得到对象是由哪个构造器创建出来的

// HTML元素属于HTMLElement对象
console.log(mydiv.constructor) //ƒ HTMLDivElement() { [native code] }
// native code 本地代码,表示调用当前系统的底层API
console.log(mydiv instanceof HTMLElement) // true

可以得出HTMLElement是所有元素的祖先类,故在HTMLElement上定义neClassList属性

Object.defineProperty(HTMLElement.prototype, 'neClassList', {
    get: function () {
        // 当前元素没有NEDOMTokenList时创建NEDOMTokenList,有就直接访问
        if(!this.__dtl__) { //__dtl__自己创建的私有变量
            this.__dtl__ = new NEDOMTokenList(this.className.split(" "), this)// 将所有类名和dom传过去
        }
        return this.__dtl__
    }
})
第二步:定义构造器NEDOMTokenList
function NEDOMTokenList (classes, dom) {
    // 改造成类数组
    [].push.apply(this, classes)
}

可以通过console.log(mydiv.neClassList)验证
在这里插入图片描述

第三步:在原型链上添加我们想要的功能
NEDOMTokenList.prototype = {
	// 原型链的标准,构造器
    constructor: NEDOMTokenList, 
    // 添加元素
    add: function add(clazz) {
        if(this.contains(clazz)) return
        [].push.call(this, clazz)
    },
    // 移除元素
    remove: function remove(clazz) {
        for(var i = 0; i<this.length; i++) {
            if(clazz === this[i]) {
                [].splice.call(this, i, 1)
                return clazz
            }
        }
    },
    // 是否包含元素
    contains: function contains(clazz) {
        return [].includes.call(this, clazz)
    },
    // 切换元素
    toggle: function toggle(clazz) {
        this.contains(clazz) ? this.remove(clazz) : this.add(clazz)
    }
}

但我们发现一个问题,添加了classC后并没有同步HTML中

mydiv.neClassList.add('classC')

在这里插入图片描述

第四步:利用监听器进行同步
// 每次操作完后重新设置value
Object.defineProperty(this, 'value', {
    enomerable: false, // 设置私有变量不可枚举
    set: function (nv) {
        dom.className = nv
    }
})

再在每个方法后面添加this.value = [].join.call(this, ' '),如

add: function add(clazz) {
        if(this.contains(clazz)) return
        [].push.call(this, clazz)
        this.value = [].join.call(this, ' ')
    },

这样就能实现同步了
在这里插入图片描述
至此,实现了可以兼容所有浏览器的classList,最后,奉上标准的代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <div id="mydiv" class="classA classB classAB"></div>
    <script>
        function NEDOMTokenList (classes, dom) {
            // 改造成类数组
            [].push.apply(this, classes)
            // 监听器
            Object.defineProperty(this, 'value', {
                enomerable: false, // 设置私有变量不可枚举
                set: function (nv) {
                    dom.className = nv
                }
            })
        }
        // 类数组的属性方法
        NEDOMTokenList.prototype = Object.create(Array.prototype, { // 基于数组的功能上扩展
        	// 原型链的标准,构造器
            constructor: {
                value: NEDOMTokenList // value{}  编译器的规则
            },
            // 添加元素
            add: {
                value: function add(clazz) {
                    if(this.contains(clazz)) return // 若包含元素则不添加
                    [].push.call(this, clazz)
                    this.value = [].join.call(this, ' ')
                }
            },
            // 移除元素
            remove: {
                value: function remove(clazz) {
                    for(var i = 0; i<this.length; i++) {
                        if(clazz === this[i]) {
                            [].splice.call(this, i, 1)
                            return clazz
                        }
                    }
                    this.value = [].join.call(this, ' ')
                }
            },
            // 是否包含元素
            contains: {
                value: function contains(clazz) {
                    return [].includes.call(this, clazz)
                }
            },
            // 切换元素
            toggle: {
                value: function toggle(clazz) {
                    this.contains(clazz) ? this.remove(clazz) : this.add(clazz)
                    this.value = [].join.call(this, ' ')
                }
            }
        })

        Object.defineProperty(HTMLElement.prototype, 'neClassList', {
            get: function () {
                if(!this.__dtl__) {
                    this.__dtl__ = new NEDOMTokenList(this.className.split(" "), this)
                }
                return this.__dtl__
            }
        })
        mydiv.neClassList.add('classC')
        mydiv.neClassList.add('classA')
        mydiv.neClassList.remove('classA')
        mydiv.neClassList.add('classA')
        console.log(mydiv.neClassList)
        console.log(mydiv.neClassList.contains('classAB'))
    </script>
</body>
</html>
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

callmeCassie

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值