Skip to content

Dom & Event

dom 的全称是 document object model (文档对象模型)

Dom 树

顾名思义,它是一个树结构,如下图所示 alt text

data-*

在 HTML5 中,我妈可以通过 data-* 自定义属性,它可以从 dataset 属性中获取

html
<div class="info" data-name="ice" data-age="24"></div>

<script>
const info = document.querySelector(".info")
console.log(info.dataset) // { name: 'ice', age: 24 }
<script/>

元素的大小与滚动

client

Details
html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>client</title>
    <style>
      .box {
        width: 100px;
        height: 100px;
        padding: 10px;
        border: 5px solid skyblue;
        background-color: pink;
        /* overflow: auto; */

        /* white-space: nowrap;
        overflow-y: scroll; */
      }
    </style>
  </head>
  <body>
    <div class="box">
      This is a box.This is a box.This is a box.This is a box.This is a box.This
      is a box.This is a box.
    </div>
    <script>
      const el = document.querySelector(".box");
      console.log(el.clientWidth); // 120 -> contentWidth + paddingWidth (不包含滚动条)
      console.log(el.clientHeight); // 120 -> contentHeight + paddingHeight (不包含滚动条)
      console.log(el.clientTop); // 5 -> borderTopWidth
      console.log(el.clientLeft); // 5 -> borderLeftWidth
    </script>
  </body>
</html>
  • ClientWidth
    contentWidth + paddingWidth (不包含滚动条)
  • ClientHeight
    contentHeight + paddingHeight (不包含滚动条)
  • ClientTop
    borderTop 的宽度 (上边框)
  • ClientLeft
    borderLeft 的宽度 (左边框)

offset

Details
html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>offset</title>
    <style>
      .box {
        width: 100px;
        height: 100px;
        padding: 10px;
        border: 5px solid skyblue;
        background-color: pink;
        /* overflow: auto; */

        /* white-space: nowrap;
        overflow-y: scroll; */
      }
    </style>
  </head>
  <body>
    <div class="box">
      This is a box.This is a box.This is a box.This is a box.This is a box.This
      is a box.This is a box.
    </div>
    <script>
      const el = document.querySelector(".box");
      console.log(el.offsetWidth); // 130 -> width + padding + border
      console.log(el.offsetHeight); // 130 -> height + padding + border
      console.log(el.offsetTop); // 8 -> margin-top
      console.log(el.offsetLeft); // 8 -> margin-left
    </script>
  </body>
</html>
  • offsetWidth
    元素的完整宽度 -> borderWidth + ClientWidth(含滚动条宽度)
  • offsetHeight
    元素的完整高度 -> borderHeight + ClientHeight (含滚动条高度)
  • offsetTop
    距离父元素的 X
  • offsetLeft
    距离父元素的 Y

scroll

Details
html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>scroll</title>
    <style>
      .box {
        width: 100px;
        height: 100px;
        padding: 10px;
        border: 5px solid skyblue;
        background-color: pink;
        overflow: auto;

        /* white-space: nowrap;
        overflow-y: scroll; */
      }
    </style>
  </head>
  <body>
    <div class="box">
      This is a box.This is a box.This is a box.This is a box.This is a box.This
      is a box.This is a box. This is a box.This is a box.This is a box.This is
      a box.This is a box.This is a box.This is a box.
    </div>
    <div class="output">scrollTop: <span class="val">0</span></div>
    <script>
      const boxEl = document.querySelector(".box");
      const valEl = document.querySelector(".val");
      console.log(boxEl.scrollHeight); // 整个可滚动区域的高度

      boxEl.addEventListener("scroll", (e) => {
        valEl.textContent = e.target.scrollTop;
      });
    </script>
  </body>
</html>
  • scrollHeight 整个可滚动区域的高度

  • scrollTop 滚动被卷进去的高度

window 上的大小与滚动

width/height

  • innerWidth / innerHeight: window 窗口的宽度/高度 (包括滚动条)

  • outerWidth / outerHeight: window 窗口整个的宽度/高度 (包含调试器等)

滚动位置

  • scrollX X 轴的滚动位置
  • scrollY Y 轴的滚动位置
  • scrollTo (X, Y) 页面滚动 至 绝对坐标

事件

事件流

在浏览器中,点击一个元素,点击的不仅仅是当前元素,因为会存在嵌套,元素会存在父子元素叠加层级

Details
html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>eventStream</title>
    <style>
      .container {
        width: 150px;
        height: 150px;
        background: pink;
        .box {
          width: 50px;
          height: 50px;
          background: skyblue;
        }
      }
    </style>
  </head>
  <body>
    <div class="container">
      <div class="box">hi ice</div>
    </div>
    <script>
      const bodyEl = document.body;
      const containerEl = document.querySelector(".container");
      const boxEl = document.querySelector(".box");

      // 事件冒泡
      bodyEl.addEventListener("click", (e) => {
        console.log("bubble: bobubodyEl click");
      });

      containerEl.addEventListener("click", (e) => {
        console.log("bubble: containerEl click");
      });

      boxEl.addEventListener("click", (e) => {
        console.log("bubble: boxEl click");
      });

      // 事件捕获
      bodyEl.addEventListener(
        "click",
        (e) => {
          console.log("capture: bodyEl click");
        },
        true
      );

      containerEl.addEventListener(
        "click",
        (e) => {
          console.log("capture: containerEl click");
        },
        true
      );

      boxEl.addEventListener(
        "click",
        (e) => {
          console.log("capture: boxEl click");
        },
        true
      );
    </script>
  </body>
</html>

Bubble

事件冒泡,从内至外,默认是冒泡

.box -> .container -> body

Capture

事件捕获,从外至内,如果同时存在冒泡/捕获,会先捕获在冒泡

body -> .container -> .box

事件对象

我们可以点击元素,查看对应的属性

Details
vue
<template>
  <div :class="$style.container" @click="handleClick">
    <div :class="$style.box"></div>
  </div>
</template>
<script setup>
const handleClick = (e) => {
  console.log('target', e.target) // 1. 事件发生的元素
  console.log('currentTarget', e.currentTarget) // 2. 事件绑定的元素

  console.log('offsetX', e.offsetX) // 3. 元素内部的 x 坐标 padding起点
  console.log('offsetY', e.offsetY) // 4. 元素内部的 Y 坐标

  console.log('clientX', e.clientX) // 5. 触发点相对于浏览器窗口的 x 坐标
  console.log('clientY', e.clientY) // 6. 触发点相对于浏览器窗口的 y 坐标

  console.log('pageX', e.pageX) // 7. 触发点相对于整个文档的 x 坐标
  console.log('pageY', e.pageY) // 8. 触发点相对于整个文档的 Y 坐标

  console.log('screenX', e.screenX) // 9. 触发点相对于整个屏幕的 x 坐标
  console.log('screenY', e.screenY) // 10. 触发点相对于整个屏幕的 y 坐标
}
</script>
<style module>
.container {
  margin-top: 10px;
  width: 100px;
  height: 100px;
  background: skyblue;
  border: 5px solid purple;
  padding: 5px;

  .box {
    width: 50px;
    height: 50px;
    background: pink;
  }
}
</style>
  • target 事件发生的元素
  • currentTarget 事件绑定的元素
  • offset X/Y 元素内部的 X/Y 坐标 (padding:0 0)
  • client X/Y 触发点相对于浏览器窗口的 X/Y
  • page X/Y 触发点相对于整个文档的 X/Y
  • screen X/Y 触发点相对于整个屏幕的 X/Y

事件委托

可以把一组元素上的事件,移动到父元素上,利用事件冒泡的机制。当子元素触发事件,冒泡到父元素中,从而达到更高的性能。
利用 data-* 的方法,给子元素绑定“唯一值”,用来区分点击了哪个元素

Details
vue
<template>
  <ul class="wrap" @click="handleClick">
    <template v-for="val in list" :key="val">
      <li :data-value="val" :class="{ [$style.grey]: selected === val }">{{ val }}</li>
    </template>
  </ul>
</template>
<script setup>
import { ref } from 'vue'

const selected = ref('1')

const list = ['1', '2', '3', '4', '5', '6']

const handleClick = (e) => {
  // 事件发生的元素
  const target = e.target
  if (target.tagName !== 'LI') return

  const value = target.dataset.value
  selected.value = value
}


</script>
<style module scoped>
.grey {
  background: var(--vp-custom-block-details-bg);
}

li {
  cursor: pointer;
}
</style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

文档加载

  • DOMContentLoaded:当 HTML 文档完全解析(Dom 树构建完成),且所有延迟脚本(<script defer src="…" /><script type="module" />)下载和执行完毕后,会触发 DOMContentLoaded 事件。它不会等待图片、子框架和异步脚本 (async) 等其他内容完成加载。
  • load:用于检测完全加载的页面。

转载请注明出处,违者必究