灏天阁

一行代码实现 8 个 DOM 相关的操作函数 | 精细讲解

· Yin灏

前言

近期整理了一下 DOM 相关的操作函数,一行代码实现一个功能。涵盖了检测页面位置、获取同级元素、获取坐标、插入元素、查找元素、序列化表单等常见操作,可以在实际开发中大大提高代码的简洁性。

有人会说,你这种代码敢用在项目里?你猜会不会被同事骂死?

嘿嘿,那就教你一招,把本文每个功能函数所对应的代码详解,当成注释写上,这样不仅不会被骂,还会被夸牛*,我相信你会回来感谢我的!

以下每个函数均包含详细解释及使用示例,尽情享用吧!!!

上菜

1、检查当前环境是否支持触摸事件

可视版

image.png

可复制版

const touchSupported = (): boolean =>
  "ontouchstart" in window ||
  ((window as any)["DocumentTouch"] &&
    document instanceof (window as any)["DocumentTouch"]);

代码详解

该函数的逻辑包含两个部分,分别用 || 连接,只要其中一个条件为真,那么整个函数就返回 true

  1. 'ontouchstart' in window: 使用 in 关键字检查 window 对象上是否有 ontouchstart 属性。如果存在,说明当前环境支持触摸事件,这个条件为真。

  2. (window as any)['DocumentTouch'] && document instanceof (window as any)['DocumentTouch']: 这个条件包含两个子条件,用 && 连接,两个子条件都为真时,这个条件才为真。

    • (window as any)['DocumentTouch']: 使用类型断言 (window as any)window 对象视为 any 类型,然后检查其上是否有 DocumentTouch 属性。如果存在,说明当前环境可能支持触摸事件。
    • document instanceof (window as any)['DocumentTouch']: 如果 DocumentTouch 属性存在,那么继续检查 document 是否是 DocumentTouch 类型的实例。如果是,说明当前环境确实支持触摸事件,这个条件为真。

2、检查是否已滚动到页面底部

可视版

image.png

可复制版

const isAtBottom = (): boolean =>
  document.documentElement.clientHeight + window.scrollY >=
  document.documentElement.scrollHeight;

代码详解

这个函数用于判断当前页面是否已经滚动到底部。

函数逻辑包含一个条件,当条件为真时,说明页面已经滚动到底部。

以下是详细解释:

  1. document.documentElement.clientHeight: 获取文档根元素(<html>)的可视区域高度。
  2. window.scrollY: 获取当前窗口垂直滚动条的位置,即页面垂直方向上已滚动的像素值。
  3. document.documentElement.scrollHeight: 获取文档根元素(<html>)的完整高度,包括超出可视区域的部分。
  4. document.documentElement.clientHeight + window.scrollY >= document.documentElement.scrollHeight: 检查当前可视区域高度加上已滚动的像素值是否大于或等于文档的完整高度。如果是,则说明页面已经滚动到底部。

这个函数在处理页面滚动事件时特别有用,例如在用户滚动到底部时自动加载更多内容。

3、获取给定 DOM 元素的所有同级兄弟节点

可视版

image.png

可复制版

const siblings = (ele: Node): Node[] =>
  ele.parentNode
    ? [].slice.call(ele.parentNode.children).filter((child) => child !== ele)
    : [];

代码详解

该函数接受一个 Node 类型参数 ele,返回一个 Node[] 类型的数组。这个函数用于获取给定 DOM 元素的所有同级兄弟节点。

以下是详细解释:

  1. (ele: Node): Node[] =>: 定义一个接受 Node 类型参数 ele 且返回类型为 Node[] 的箭头函数。
  2. ele.parentNode: 获取给定元素的父节点。如果存在,继续下一步;如果不存在(即 ele 为根节点或不在文档树中),返回一个空数组 []
  3. [].slice.call(ele.parentNode.children): 使用 Array.prototype.slice.call() 将类数组对象 ele.parentNode.children 转换为数组。这里 ele.parentNode.children 是包含父节点下所有直接子节点的类数组对象。
  4. .filter((child) => child !== ele): 使用 filter 方法筛选出与给定元素 ele 不同的子节点,即获取所有兄弟节点。
  5. ele.parentNode ? ... : []: 使用三元条件运算符,当 ele.parentNode 存在时返回筛选后的兄弟节点数组,否则返回一个空数组 []

使用示例

const targetElement = document.querySelector("#target");
const siblingNodes = siblings(targetElement);

console.log("Sibling nodes of the target element:", siblingNodes);

4、获取指定 DOM 元素的绝对坐标

可视版

image.png

可复制版

const getPosition = (ele) => (
  (r = ele.getBoundingClientRect()),
  { left: r.left + window.scrollX, top: r.top + window.scrollY }
);

代码详解

该函数接受一个参数 ele(通常为一个 DOM 元素),返回一个包含两个属性 lefttop 的对象。这个函数用于获取给定 DOM 元素相对于 document 的绝对坐标(即距离 document 左上角的横向和纵向像素值)。

以下是详细解释:

  1. ((r = ele.getBoundingClientRect()), ...: 使用圆括号包含的逗号表达式,首先调用 ele.getBoundingClientRect() 方法获取元素相对于视口的位置信息,该方法返回一个包含 lefttop 等属性的对象。将此对象赋值给变量 r
  2. { left: r.left + window.scrollX, top: r.top + window.scrollY }: 创建一个新对象,其属性 lefttop 分别为元素相对于视口的横向和纵向坐标加上窗口的滚动距离(window.scrollXwindow.scrollY)。这样就得到了元素相对于文档的绝对坐标。

使用示例

getPosition(document.body); // { left: 0, top: 0 }

5、将某个元素插入到指定元素之后

可视版

image.png

可复制版

const insertAfter = (ele: Element, anotherEle: Element): Element | null =>
  anotherEle.insertAdjacentElement("afterend", ele);

代码详解

该函数接受两个参数 eleanotherEle(均为 Element 类型),返回一个 Element | null 类型的结果。这个函数用于将一个元素(ele)插入到另一个元素(anotherEle)之后。

以下是详细解释:

  1. (ele: Element, anotherEle: Element): Element | null =>: 定义一个接受两个 Element 类型参数 eleanotherEle 且返回类型为 Element | null 的箭头函数。
  2. anotherEle.insertAdjacentElement('afterend', ele): 调用 insertAdjacentElement 方法将 ele 插入到 anotherEle 之后。该方法接受两个参数:插入位置和要插入的元素。插入位置为 'afterend',表示在目标元素 anotherEle 之后插入。要插入的元素为 ele

当插入成功时,insertAdjacentElement 方法返回插入的元素;如果插入失败(例如,anotherEle 不在文档树中),则返回 null

6、将某个元素替换为指定元素

可视版

image.png

可复制版

const replace = (ele: Element, newEle: Element): Element | null =>
  ele.parentNode ? ele.parentNode.replaceChild(newEle, ele) : null;

代码详解

该函数接受两个参数 elenewEle(均为 Element 类型),返回一个 Element | null 类型的结果。这个函数用于将一个元素(ele)替换为另一个元素(newEle)。

以下是详细解释:

  1. (ele: Element, newEle: Element): Element | null =>: 定义一个接受两个 Element 类型参数 elenewEle 且返回类型为 Element | null 的箭头函数。

  2. ele.parentNode ? ele.parentNode.replaceChild(newEle, ele) : null: 使用三元条件运算符。如果 ele.parentNode 存在,调用 replaceChild 方法将 ele 替换为 newEle;如果不存在(即 ele 为根节点或不在文档树中),返回 null

    • ele.parentNode: 获取给定元素 ele 的父节点。
    • ele.parentNode.replaceChild(newEle, ele): 调用 replaceChild 方法,将给定元素 ele 替换为新元素 newEle。此方法接受两个参数:新元素和要替换的元素。

当替换成功时,replaceChild 方法返回被替换的元素;如果替换失败(例如,ele 不在文档树中),则返回 null

7、将表单数据序列化为 JS 对象

可视版

image.png

可复制版

const serialize = (formEle) =>
  Array.from(new FormData(formEle)).reduce(
    (p, [k, v]) =>
      Object.assign({}, p, {
        [k]: p[k] ? (Array.isArray(p[k]) ? p[k] : [p[k]]).concat(v) : v,
      }),
    {}
  );

代码详解

该函数接受一个参数 formEle(通常为一个表单元素),返回一个对象。这个函数用于将表单元素的数据序列化为一个 JavaScript 对象,其中键为表单元素的 name 属性,值为表单元素的值。

以下是详细解释:

  1. Array.from(new FormData(formEle)): 使用 FormData 构造函数获取 formEle 中的表单数据,然后使用 Array.from() 将其转换为数组。数组的每个元素是一个包含两个元素(键值对)的数组,表示表单元素的 name 属性和值。

  2. reduce((p, [k, v]) => ..., {}): 使用 reduce 方法将键值对数组转换为一个对象。reduce 方法接受一个回调函数和一个初始值(这里为空对象 {})。

    • 回调函数的第一个参数 p 是累积器,用于保存已处理的键值对。
    • 回调函数的第二个参数 [k, v] 是当前处理的键值对数组,其中 k 是键(表单元素的 name 属性),v 是值(表单元素的值)。
  3. Object.assign({}, p, { [k]: ... }): 使用 Object.assign() 方法将当前的累积对象 p 和新键值对合并成一个新对象。新键值对的键为 k,值根据条件判断后生成。

  4. p[k] ? (Array.isArray(p[k]) ? p[k] : [p[k]]).concat(v) : v: 使用三元条件运算符检查累积器 p 中是否已存在键 k。如果存在,则检查其值是否为数组(Array.isArray(p[k]))。如果是数组,则使用 concat 方法将当前值 v 添加到数组中;否则,将原值和当前值 v 创建一个新数组。如果累积器 p 中不存在键 k,则将当前值 v 作为新值。

使用示例

const formElement = document.querySelector("#myForm");
const serializedData = serialize(formElement);

console.log("Serialized form data:", serializedData);

8、从字符串中移除所有的 HTML 标签

可视版

image.png

可复制版

const stripHtml = (html: string): string =>
  new DOMParser().parseFromString(html, "text/html").body.textContent || "";

代码详解

该函数接受一个 string 类型参数 html,返回一个 string 类型的结果。这个函数用于从输入的 HTML 字符串中移除所有的 HTML 标签,只保留纯文本内容。

以下是详细解释:

  1. new DOMParser(): 创建一个 DOMParser 实例,用于将 HTML 字符串解析为 DOM 文档。
  2. .parseFromString(html, 'text/html'): 调用 parseFromString 方法将输入的 HTML 字符串 html 解析为一个 text/html 类型的 DOM 文档。
  3. .body.textContent: 获取解析后的 DOM 文档的 body 元素的 textContent 属性,即该元素及其所有后代元素的纯文本内容。这将自动移除所有 HTML 标签。
  4. || '': 使用逻辑或操作符确保结果始终为字符串。如果 textContentnull(例如,输入的 HTML 字符串为空),则返回空字符串 ''

使用示例

const htmlString =
  "<h1>Title</h1><p>Paragraph with <b>bold</b> and <i>italic</i> text.</p>";
const plainText = stripHtml(htmlString);

console.log("Plain text content:", plainText);

- Book Lists -