Bootstrap

从 classList 到 DOMTokenList: 简化类名管理的工具

引言

在现代的网页开发中, 与用户界面交互的核心是操作和控制 DOM。 其中, 处理元素的外观和交互是不可或缺的一个环节, 而其中 classListDOMTokenList 作为一个强大工具, 为开发人员提供了便捷的方式来管理 DOM 元素的类名。

在这篇文章中, 我们将简单梳理下 classListDOMTokenList 之间的关系, 以及对应的一些基础属性和方法!!! 知道这些内容后, 有助于我们更好的处理、管理类名!!!

一、classList 属性

Element.classListDOM 对象上的一个只读属性, 该属性返回一个元素的 class 属性集合, 该值实现了 DOMTokenList 接口(关于 DOMTokenList 接口下文会进行详细的介绍)

image

作用: 通过 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");

这里我们只需要知道 classListDOMTokenList 接口的实现, 至于它有哪些方法、属性, 下面我们来慢慢讲解

二、DOMTokenList 概述

DOMTokenList 接口用于表示一组由 空格分隔字符串 合集, 它最常以 Element.prototype.classList 属性的形式被使用, 如上文所示

2.1 数据结构

下面我们尝试打印下选中节点的 classList 属性, 来简单过下 DOMTokenList 对象的一个格式

补充: 在 chrome 控制台中 $0 是有特殊含义的, 表示当前选中的 DOM 节点

image

image

从上图可以看出, DOMTokenList 对象是一个 类数组 对象, 每个值都有一个对应的索引(从 0 开始), 同时具有 length 以及 value 属性, 以及多个方法实例方法, 下面我们来简单介绍下这些属性、方法

2.2 创建实例

我们可以尝试使用 new 创建一个 DOMTokenList 实例, 你会发现这么做将会报错(因为它是不允许被手动实例化的)

image

既然无法直接进行实例化, 那是不是就无法手动创建 DOMTokenList 实例呢? 其实不是的, 我们可以创建一个工具函数, 通过创建 DOM 的方式来间接创建 DOMTokenList 实例, 如下代码所示:

const createDOMTokenList = (value) => {
  const div = document.createElement("div");

  if (value) {
    div.className = value;
  }
  return div.classList
}

2.3 两个属性

  1. length: 上文提到 DOMTokenList 接口实现了一个 类数组 对象, 那么它必然会有一个 length 属性, 返回当前 类数组 的一个长度
const list = createDOMTokenList('foo wrapper main bar')
console.log(list.length) // 4
  1. value: DOMTokenList 接口返回的 类数组, 它的每一项值实际上是将 原始值 通过空格拆分后的结果, 那么 value 这个属性值可以帮助我们获取到未拆分的一个原始值
const list = createDOMTokenList('foo wrapper main bar')
console.log(list.value) // foo wrapper main bar

2.4 增删改查

  1. 增: 可通过 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
  1. 删: 可通过 DOMTokenList.remove(token1[, token2[, ...tokenN]]) 方法, 删除一个或多个 token
const list = createDOMTokenList('foo wrapper main bar')
list.remove('wrapper', 'bar', 'a')
console.log(list.value) // foo main
  1. 替换: 可通过 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
  1. 切换: 可通过 DOMTokenList.toggle(token [, force]) 来切换(来回添加隐藏)指定 token
  1. 不设置第二参数 force 的情况下, 如果存在指定 token 则移除并返回 false, 如果不存在则添加并返回 true
  2. 如果第二参数 force 设置为 true, 则表示强制设置指定 toekn, 如果存在则直接返回 true, 如果不存在则添加并返回 true
  3. 如果第二参数 force 设置为 false, 则表示强制隐藏指定 toekn, 如果存在则隐藏并返回 false, 如果不存在则直接返回 false
  4. 补充: 该方法返回值表示, 操作完后, 是否还存在指定 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']
  1. 查: 可通过 DOMTokenList.item(index) 或者指定索引位置的 token
const list = createDOMTokenList('foo wrapper main bar')
list.item(0) // foo
list.item(2) // main
  1. 查: 可通过 DOMTokenList.contains(token) 来判断指定 token 是否存在于列表中, 如果存在则返回 true 否则返回 false
const list = createDOMTokenList('foo wrapper main bar')
list.contains('main') // true
list.contains('body') // false

2.5 循环

  1. 使用 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)
})

image

  1. 先获取值, 然后再循环: 同 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 非空

对于 addremovereplacetoggle 这几个需要输入 token 并且会修改列表的方法, 如果 token空字符串 将会报错(传 null 或者 undefined 是不会报错的)

image

2.7 去重(空格、token)

  1. 对于 add 以及 remove 方法只要执行了, 就会去除重复的空格、token

image
image

  1. 对于 replace 方法, 只有替换成功才会移除重复的空格、token

image

  1. 对于 toggle 切换, 只有修改列表才会移除重复的空格、token

image

三、DOMTokenList 实际用途

  1. 类名管理: DOMTokenList 常常和 classList 强绑定, 在实际开发中, class 属性通常用于定义元素的样式和行为, 而 DOMTokenList 提供了便利的方法来管理这些类名!!!

  2. 操作其他空格分隔的字符串: 从 DOMTokenList 特性出发, 我们可以想象到的一个场景就是通过它来处理一串由空格分隔的字符串, 通过它可以很方便的对子串进行增、删、改、查、替换、遍历等操作!!

四、DOMTokenList 的局限性和注意事项

  1. 不支持重复 token: DOMTokenList 中不支持添加重复的 token
  2. 不能设置分隔符: DOMTokenList 只能处理由空格分隔的字符串, 对于这个分隔符(空格)是没办法修改的

五、参考


image

;