实现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>

猜你喜欢

转载自blog.csdn.net/callmeCassie/article/details/90674181
今日推荐