从 classList 到 DOMTokenList: 简化类名管理的工具
引言
在现代的网页开发中, 与用户界面交互的核心是操作和控制 DOM
。 其中, 处理元素的外观和交互是不可或缺的一个环节, 而其中 classList
和 DOMTokenList
作为一个强大工具, 为开发人员提供了便捷的方式来管理 DOM
元素的类名。
在这篇文章中, 我们将简单梳理下 classList
和 DOMTokenList
之间的关系, 以及对应的一些基础属性和方法!!! 知道这些内容后, 有助于我们更好的处理、管理类名!!!
一、classList 属性
Element.classList
是 DOM
对象上的一个只读属性, 该属性返回一个元素的 class
属性集合, 该值实现了 DOMTokenList
接口(关于 DOMTokenList
接口下文会进行详细的介绍)
作用: 通过 classList
我们可以很方便针对元素 className
进行增、删、改、查、遍历等一些列操作, 并且相比于将 element.className
作为以空格分隔的字符串来进行操作, 通过 classList
进行处理无疑是更为方便、灵活的!
const div = document.createElement("div");
div.className = "foo";
// 初始状态:<div class="foo"></div>
console.log(div.outerHTML);
// 使用 classList API 移除、添加类值
div.classList.remove("foo");
div.classList.add("anotherclass");
// <div class="anotherclass"></div>
console.log(div.outerHTML);
// 如果 visible 类值已存在,则移除它,否则添加它
div.classList.toggle("visible");
// add/remove visible, depending on test conditional, i less than 10
div.classList.toggle("visible", i < 10);
console.log(div.classList.contains("foo"));
// 添加或移除多个类值
div.classList.add("foo", "bar", "baz");
div.classList.remove("foo", "bar", "baz");
// 使用展开语法添加或移除多个类值
const cls = ["foo", "bar"];
div.classList.add(...cls);
div.classList.remove(...cls);
// 将类值 "foo" 替换成 "bar"
div.classList.replace("foo", "bar");
这里我们只需要知道 classList
是 DOMTokenList
接口的实现, 至于它有哪些方法、属性, 下面我们来慢慢讲解
二、DOMTokenList 概述
DOMTokenList
接口用于表示一组由 空格分隔
的 字符串
合集, 它最常以 Element.prototype.classList
属性的形式被使用, 如上文所示
2.1 数据结构
下面我们尝试打印下选中节点的 classList
属性, 来简单过下 DOMTokenList
对象的一个格式
补充: 在
chrome
控制台中$0
是有特殊含义的, 表示当前选中的DOM
节点
从上图可以看出, DOMTokenList
对象是一个 类数组
对象, 每个值都有一个对应的索引(从 0
开始), 同时具有 length
以及 value
属性, 以及多个方法实例方法, 下面我们来简单介绍下这些属性、方法
2.2 创建实例
我们可以尝试使用 new
创建一个 DOMTokenList
实例, 你会发现这么做将会报错(因为它是不允许被手动实例化的)
既然无法直接进行实例化, 那是不是就无法手动创建 DOMTokenList
实例呢? 其实不是的, 我们可以创建一个工具函数, 通过创建 DOM
的方式来间接创建 DOMTokenList
实例, 如下代码所示:
const createDOMTokenList = (value) => {
const div = document.createElement("div");
if (value) {
div.className = value;
}
return div.classList
}
2.3 两个属性
length
: 上文提到DOMTokenList
接口实现了一个类数组
对象, 那么它必然会有一个length
属性, 返回当前类数组
的一个长度
const list = createDOMTokenList('foo wrapper main bar')
console.log(list.length) // 4
value
:DOMTokenList
接口返回的类数组
, 它的每一项值实际上是将原始值
通过空格拆分后的结果, 那么value
这个属性值可以帮助我们获取到未拆分的一个原始值
const list = createDOMTokenList('foo wrapper main bar')
console.log(list.value) // foo wrapper main bar
2.4 增删改查
- 增: 可通过
DOMTokenList.add(token1[, token2[, ...tokenN]])
方法, 新增一个或多个token
const list = createDOMTokenList('foo wrapper main bar')
list.add('a', 'b', 'c', 'd')
console.log(list.value) // foo wrapper main bar a b c d
- 删: 可通过
DOMTokenList.remove(token1[, token2[, ...tokenN]])
方法, 删除一个或多个token
const list = createDOMTokenList('foo wrapper main bar')
list.remove('wrapper', 'bar', 'a')
console.log(list.value) // foo main
- 替换: 可通过
DOMTokenList.replace(oldToken, newToken)
, 将oldToken
替换为newToken
, 替换成功返回true
, 否则返回false
const list = createDOMTokenList('foo wrapper main bar')
list.replace('wrapper', 'a') // true
console.log(list.value) // foo a main bar
list.replace('fo', 'f') // false
console.log(list.value) // foo a main bar
- 切换: 可通过
DOMTokenList.toggle(token [, force])
来切换(来回添加隐藏)指定token
- 不设置第二参数
force
的情况下, 如果存在指定token
则移除并返回false
, 如果不存在则添加并返回true
- 如果第二参数
force
设置为true
, 则表示强制设置指定toekn
, 如果存在则直接返回true
, 如果不存在则添加并返回true
- 如果第二参数
force
设置为false
, 则表示强制隐藏指定toekn
, 如果存在则隐藏并返回false
, 如果不存在则直接返回false
- 补充: 该方法返回值表示, 操作完后, 是否还存在指定
toekn
const list = createDOMTokenList('foo wrapper main bar')
list.toggle('main') // false
list // DOMTokenList(3) ['foo', 'wrapper', 'bar']
list.toggle('main') // true
list // DOMTokenList(4) ['foo', 'wrapper', 'bar', 'main']
list.toggle('main', true) // true
list // DOMTokenList(4) ['foo', 'wrapper', 'bar', 'main']
list.toggle('main2', true) // true
list // DOMTokenList(5) ['foo', 'wrapper', 'bar', 'main', 'main2']
list.toggle('main3', false) // false
list // DOMTokenList(5) ['foo', 'wrapper', 'bar', 'main', 'main2']
list.toggle('main2', false) // false
list // DOMTokenList(5) ['foo', 'wrapper', 'bar', 'main']
- 查: 可通过
DOMTokenList.item(index)
或者指定索引位置的token
const list = createDOMTokenList('foo wrapper main bar')
list.item(0) // foo
list.item(2) // main
- 查: 可通过
DOMTokenList.contains(token)
来判断指定token
是否存在于列表中, 如果存在则返回true
否则返回false
const list = createDOMTokenList('foo wrapper main bar')
list.contains('main') // true
list.contains('body') // false
2.5 循环
- 使用
forEach
进行循环: 通过DOMTokenList.forEach(callback [, thisArg])
来遍历token
, 该方法参数同Array.forEach
const list = createDOMTokenList('foo wrapper main bar')
list.forEach((current, index, origin) => {
console.log('current: ', current)
console.log('index: ', index)
console.log('origin: ', origin)
})
- 先获取值, 然后再循环: 同
Array
可通过DOMTokenList.keys()
、DOMTokenList.values()
、DOMTokenList.entries()
等方法获取到索引、或者值的一个集合, 然后进行遍历
需要注意的是, 这几个方法返回的值都是一个迭代器, 关于迭代器的知识可查阅 《迭代器、生成器详解🔥🔥🔥》
const list = createDOMTokenList('foo wrapper main bar')
[...list.keys()].forEach(v => {
console.log(v) // 0 1 2 3
})
[...list.values()].forEach(v => {
console.log(v) // foo wrapper main bar
})
[...list.entries()].forEach(v => {
console.log(v) // [0, foo] [1, wrapper] [2, main] [3, bar]
})
2.6 非空
对于 add
、remove
、replace
、toggle
这几个需要输入 token
并且会修改列表的方法, 如果 token
为 空字符串
将会报错(传 null
或者 undefined
是不会报错的)
2.7 去重(空格、token)
- 对于
add
以及remove
方法只要执行了, 就会去除重复的空格、token
- 对于
replace
方法, 只有替换成功才会移除重复的空格、token
- 对于
toggle
切换, 只有修改列表才会移除重复的空格、token
三、DOMTokenList 实际用途
-
类名管理:
DOMTokenList
常常和classList
强绑定, 在实际开发中,class
属性通常用于定义元素的样式和行为, 而DOMTokenList
提供了便利的方法来管理这些类名!!! -
操作其他空格分隔的字符串: 从
DOMTokenList
特性出发, 我们可以想象到的一个场景就是通过它来处理一串由空格分隔的字符串, 通过它可以很方便的对子串进行增、删、改、查、替换、遍历等操作!!
四、DOMTokenList 的局限性和注意事项
- 不支持重复
token
:DOMTokenList
中不支持添加重复的token
- 不能设置分隔符:
DOMTokenList
只能处理由空格分隔的字符串, 对于这个分隔符(空格)是没办法修改的
五、参考
- JavaScript classList
- DOMTokenList
- Element classList