DOM_day03

本文介绍了DOM的基本概念,强调其在前端定制化开发中的重要性。内容涵盖查询和操作元素的方法,如querySelector、querySelectorAll、getElementById等,以及样式操作、事件监听器和事件冒泡。还讨论了动态内容、事件委托、滚动事件和封装理念,提醒读者DOM操作虽然复杂,但框架如jQuery能简化这一过程。

回顾

DOM: Document Object Model

  • 可以自定义的利用JS 操作页面

HTML 和 CSS 仅仅提供了一些基础的功能, 实际开发中 无法胜任复杂的需求

学习DOM: 定制化的为项目提供功能

重点:

  • document对象: 所有操作DOM有关的API都在这里

  • 查找元素:

    • 利用css选择器 -全局
      • querySelector: 查询首个满足条件的元素
      • querySelectorAll: 查询所有满足条件的元素
    • 利用关系
      • 父: parentElemnt
      • 子: children
      • 兄: previousElementSibling
      • 弟: nextElementSibling
    • 利用某些特征 - 全局
      • id: getElementById
  • 操作元素

    • 样式操作

      • class - 适合固定样式
        • className: 直接操作 class 属性本体
        • classList: 一个集合 - 包含一些快速操作class的相关方法
      • style - 适合动态样式
    • 事件: 都是 on 开头

      • onclick: 点击
      • onmouseover: 鼠标悬浮
    • 零散的属性

      • 图片的src: 决定 img 标签呈现的内容

      • 自定义属性

        • 随便写: 必须用 getAttribute 读取 –不推荐, 旧方案
        • 固定格式: data-属性名 , 读取通过 dataset.属性名
      • 内容

        • innerHTML: 读取 文本+标签+换行符
        • innerText: 读取 文本

        如果标签中仅有文本, 则两者读取的内容无差别

        • innerHTML: 写入的内容 作为HTML代码解析展示
        • innerText: 写入的内容 作为文本展示

作业

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>作业 09:13</title>
  <link rel="stylesheet" href="./reset.css">
  <style>
    #box {
      width: 604px;
      cursor: pointer;
      overflow: hidden;
    }

    #box>ul {
      display: flex;
      background-color: black;
      user-select: none;
    }

    #box>ul>li {
      color: #acaebb;
      padding: 10px 0;
      /* 每个li 在主轴上 都占1份 */
      flex: 1;
      text-align: center;
    }

    #box>ul>li.active {
      background-color: #262626;
      color: #e5bb54;
    }

    #box>div {
      display: flex;
      transition: 0.2s;
      position: relative;
      left: 0;
    }
  </style>
</head>

<body>
  <div id="box">
    <div>
      <img src="./wzry/111.jpeg" alt="">
      <img src="./wzry/222.jpeg" alt="">
      <img src="./wzry/333.jpeg" alt="">
      <img src="./wzry/444.jpeg" alt="">
      <img src="./wzry/555.jpeg" alt="">
    </div>

    <ul>
      <li data-i="0" class="active">世界冠军杯</li>
      <li data-i="1">皮肤创作前瞻</li>
      <li data-i="2">云中专线</li>
      <li data-i="3">弈星滕王阁序</li>
      <li data-i="4">峡谷搞事团</li>
    </ul>
  </div>

  <script>
    const lis = document.querySelectorAll('#box li')

    lis.forEach(li => li.onmouseover = function () {
      lis.forEach(li => li.classList.remove('active'))
      this.classList.add('active')

      // 获取当前激活项的序号, 计算出对应的偏移量 -?00%
      // 设置给 图片所在div的 style.left
      const all = this.parentElement.children
      // 相当于 all.indexOf(this) : 从 all 数组里获取 当前this的序号
      // const i = Array.prototype.indexOf.call(all, this)
      for (let i = 0; i < all.length; i++) {
        if (all[i].classList.contains('active')) {
          const div = this.parentElement.previousElementSibling
          div.style.left = `-${i}00%`

          break
        }
      }


    })
  </script>
</body>

</html>

输入框

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>输入框事件 10:19</title>
</head>

<body>
  <input type="text" value="世宇">

  <script>
    // 相关事件:
    const inp = document.querySelector('input')
    // 获得焦点 focus
    inp.onfocus = function () {
      console.log('focus: 获得焦点')
    }
    // 失去焦点 blur
    inp.onblur = function () {
      console.log('blur: 失去焦点');
    }
    // 内容变化 change 
    // 触发条件: 内容有修改 - 按回车 或 失去焦点时
    inp.onchange = function () {
      console.log('change: 内容变化', this.value)
    }
    // 实时变化 input
    inp.oninput = function () {
      console.log('input:', this.value)
    }

    // 键盘按键抬起 keyup
    // 事件对象: 当触发事件时, 系统会自动把 此次事件相关的信息作为参数传递给函数
    inp.onkeyup = function (e) {
      // 形参名:  event事件,  偷懒则简写成 e
      console.log('keyup')
      // console.log(arguments)
      console.log(e)
      // keyCode 代表按键的编号, 每个按键都有自己的编号
      // 回车是13
      if (e.keyCode == 13) alert("回车!")
    }
  </script>
</body>

</html>

练习

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>练习 10:49</title>
</head>

<body>
  <input type="text" placeholder="菜品名,食材名">

  <ul>
    <!-- 尝试用map 把请求到的数据中的 name 属性, 放到li标签里, 添加到 ul 里.  利用 join() 拼接,  用 innerHTML设置 -->
    <!-- <li>Gemside捷赛自动烹饪锅多功能懒人锅P18</li> -->
  </ul>

  <script>
    // 任务: 在输入框中按回车, 读取输入框中的值, 然后发请求获取数据
    const inp = document.querySelector('input')

    // 事件参数中: 编号13的按键 是回车
    inp.onkeyup = function (e) {
      if (e.keyCode == 13) {
        console.log('搜索:', this.value)
        // 从 xin666.vip 找到接口文档
        const url = `https://serverms.xin88.top/mall/search?type=1&kw=${this.value}&page=1`

        const xhr = new XMLHttpRequest()
        xhr.open('GET', url)
        xhr.onload = function () {
          const data = JSON.parse(xhr.response)
          console.log(data)

          const ul = document.querySelector('ul')
          ul.innerHTML = data.data.map((item) => {
            return `<li>${item.name}</li>`
          }).join('')
        }
        xhr.send()
      }
    }
  </script>
</body>

</html>
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>正则表达式 11:37</title>
  <style>
    ul>li {
      display: none;
    }

    /* 对 ok */
    ul.ok>li:first-child {
      display: block;
    }

    /* 错 err */
    ul.err>li:last-child {
      display: block;
    }
  </style>
</head>

<body>
  <!-- 正则表达式: regular expression  简称 RegExp -->
  <!-- 是一套通用的 对字符串进行格式验证的方案 -->
  <!-- 非JS专属, 使用正则 需要学习正则元字符 -->
  <!-- 文档: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions -->

  <input type="text">
  <ul>
    <li style="color: green;">手机号正确</li>
    <li style="color: red;">手机号错误</li>
  </ul>

  <script>
    // \d 代表 1个数字 0 ~ 9

    // 任务: 让用户输入一些文字, 我们从其中找出所有的数字
    const inp = document.querySelector('input')
    const ul = inp.nextElementSibling //下一个兄弟

    // 失去焦点
    inp.onblur = function () {
      // match: 匹配;  从字符串中匹配出 符合正则表达式要求的内容
      // /\d/ : 字面量语法  /正则/ , 把正则符号 转为JS的正则对象
      // g: 修饰符, global 全局匹配, 找到所有符合条件的
      const res = this.value.match(/\d/g)
      console.log(res)

      // 手机号格式: 11位  1开头  第二位3-9
      const reg_phone = /^1[3-9]\d{9}$/
      // 正则.test(字符串):  用正则验证字符串是否符合格式要求
      if (reg_phone.test(this.value)) {
        console.log(this.value, '是手机号');
        ul.className = 'ok'
        // ul.classList.add('ok')  //add是累加
        // 思考题: 为什么这里不用 classList.add() ???
      } else {
        ul.className = 'err'
        console.log(this.value, '非手机号');
      }
    }
  </script>
</body>

</html>

冒泡

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>事件冒泡 14:05</title>
  <style>
    #red {
      background-color: red;
      width: 500px;
      height: 500px;
    }

    #green {
      background-color: green;
      width: 300px;
      height: 300px;
    }

    #blue {
      background-color: blue;
      width: 100px;
      height: 100px;
    }
  </style>
</head>

<body>
  <div id="red">
    <div id="green">
      <div id="blue"></div>
    </div>
  </div>

  <script>
    // 事件冒泡: 子元素上触发事件, 会通知父元素, 触发相同的事件
    // 触发事件的当事元素: target

    red.onclick = function (e) {
      // 事件参数中的target: 代表触发事件的当事元素
      console.log('red clicked!');
      console.log('当事元素:', e.target)
    }

    green.onclick = function (e) {
      console.log('green clicked!');
      console.log('当事元素:', e.target)
    }

    blue.onclick = function (e) {
      console.log('blue clicked!');
      console.log('当事元素:', e.target)
      // 阻止冒泡
      // stop:停止  propagation:传播
      e.stopPropagation()
    }
  </script>
</body>

</html>

委托

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>事件委托 14:24</title>
  <link rel="stylesheet" href="./reset.css">
  <style>
    li {
      padding: 10px;
    }

    .active {
      background-color: orange;
    }
  </style>
</head>

<body>
  <!-- 
    事件委托: 子元素把事件的处理操作, 委托给父元素
   -->
  <ul>
    <li>亮亮</li>
    <li>铭铭</li>
    <p>世宇11</p>
    <li>泡泡</li>
    <p>世宇22</p>
    <li>小新</li>
  </ul>

  <script>
    // const lis = document.querySelectorAll('li')

    // lis.forEach(li => li.onclick = function () {
    //   this.classList.add('active')
    // })

    const ul = document.querySelector('ul')

    ul.onclick = function (e) {
      console.log('ul clicked!', e.target)
      // 注意: 委托模式下 所有子元素 及 自身 都会触发事件, 必须利用判断, 选择对哪些元素进行操作
      console.dir(e.target)

      // 判断标签名是 li , 再激活;  过滤掉不是 li 的元素
      if (e.target.localName == 'li') {
        // 当事元素 添加激活
        e.target.classList.add('active')
      }
    }

    // 取舍:
    // 事件委托适合为动态新增的子元素添加事件
  </script>
</body>

</html>

动态

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>动态新增 14:48</title>
  <style>
    li.active {
      padding: 10px;
      margin-bottom: 4px;
      background-color: orange;
    }
  </style>
</head>

<body>
  <button>新增元素</button>

  <ul>
    <li>泡泡</li>
  </ul>

  <script>
    const btn = document.querySelector('button')
    const ul = btn.nextElementSibling

    btn.onclick = function () {
      ul.innerHTML += '<li>亮亮</li>'
    }

    // 1. 用循环方式,给li加事件. 点击后激活. 试一试新增的元素是否有效果, 为什么?

    // 此代码执行时, 只有泡泡被查询到 然后添加了事件
    const lis = document.querySelectorAll('li')
    lis.forEach(li => li.onclick = function () {
      this.classList.add('active')
    })


    // 2. 再试试 用委托方式 实现 li的点击激活, 试试新增元素是否有效果, 为什么?
    // 父元素不变, 所有的子元素 不分新旧, 触发的事件都会被父元素处理
    ul.onclick = function (e) {
      e.target.classList.add('active')
    }
  </script>
</body>

</html>
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>练习 15:21</title>

  <link rel="stylesheet" href="./reset.css">
  <style>
    ul {
      display: flex;
    }

    ul>li {
      margin: 4px;
      padding: 5px;
      user-select: none;
    }

    ul>li.active {
      color: orange;
      border-bottom: 2px solid orange;
    }
  </style>
</head>

<body>
  <ul>
    <li></li>
  </ul>

  <script>
    const url = 'http://douyu.xin88.top/api/cate/recList'

    const ul = document.querySelector('ul')

    // 发请求. 获取数据. 转为 li 添加到 ul 标签里
    const xhr = new XMLHttpRequest()
    xhr.open('get', url)
    xhr.onload = function () {
      const data = JSON.parse(xhr.response)
      console.log(data)

      ul.innerHTML = data.data.map((item, index) => {
        // 序号0 的, 添加激活样式, 其他的不加
        return `<li class="${index == 0 ? 'active' : ''}">${item.name}</li>`
      }).join('')

    }
    xhr.send()

    // 书写css样式, 首个li要激活,  点击哪个li 哪个激活

    // 问题: 网络请求是异步操作, 还没请求到 就开始找li 必然找不到
    // const lis = document.querySelectorAll('li')
    // lis.forEach(li => li.onclick = function () {
    //   this.classList.add('active')
    // })

    // 动态新增元素: 用委托
    ul.onclick = function (e) {
      // 当事元素不是 li , 则终止操作
      if (e.target.localName != 'li') return

      //查找激活项, 去掉其激活状态
      const ac = document.querySelector('ul>li.active')
      ac.classList.remove('active')

      e.target.classList.add('active')
    }

  </script>
</body>

</html>

事件监听器

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>事件监听器 16:20</title>
</head>

<body>
  <button>点我</button>

  <script>
    const btn = document.querySelector('button')

    // onclick属性: 同时只能赋1个值
    btn.onclick = function () {
      console.log(11)
    }
    // 后赋值的 覆盖 先赋值
    btn.onclick = function () {
      console.log(22)
    }

    // 实际开发时, 往往会团队合作 或 工期比较长
    // 如何给1个元素绑定多个事件, 互不影响?
    // 事件监听器:
    // add添加 event事件 listener监听器
    // 参数1: 事件名 -- 注意 没有on前缀
    // 参数2: 触发时调用的函数
    btn.addEventListener('click', function () {
      console.log(33)
    })

    btn.addEventListener('click', function () {
      console.log(44)
    })

    // 删除事件监听器
    function show() {
      alert("Hello")
      // 只执行一次, 就删除
      // remove:删除
      btn.removeEventListener('click', show)
    }

    btn.addEventListener('click', show)
  </script>
</body>

</html>

滚动事件

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>页面滚动监听 16:36</title>
  <style>
    body,
    ul {
      margin: 0;
      padding: 0;
    }
  </style>
</head>

<body>
  <!-- 
    滚动监听: 监听页面滚动的距离, 进行判断后, 执行一些操作
    例如: 显示额外的组件, 触底加载更多数据...
   -->
  <ul></ul>

  <script>
    const ul = document.querySelector('ul')

    for (let i = 0; i < 200; i++) {
      ul.innerHTML += `<li>${i}</li>`
    }

    // 滚动事件: scroll
    addEventListener('scroll', function () {
      // 读取滚动距离顶部的偏移量
      // 由于浏览器的 版本兼容性问题: 如果body中读不到, 就从后面的读
      const offsetY = document.body.scrollTop || document.documentElement.scrollTop

      console.log('滚动...', offsetY);

      // 判断触底: 获取整个页面的高度 和 滚动的高度对比
      console.log('页面可视区域高度:', innerHeight);
      //页面内容
      console.log('body高:', document.body.clientHeight);

      // 设定距离底部50像素 算触底
      const y = offsetY + innerHeight + 50
      if (document.body.clientHeight < y) {
        console.log("触底!")
      }
    })

    // 逻辑短路语法:
    // 逻辑或的设定: 从左到右第一个真值, 如果没有真的,则是最后一个
    var a = '' || 0 || false || 44 || null || undefined
    console.log(a)

    var a = '' || 0 || false || null || undefined
    console.log(a)
  </script>
</body>

</html>

封装理念

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>封装DOM</title>
</head>

<body>
  <ul>
    <li>泡泡</li>
    <li>铭铭</li>
    <li>亮亮</li>
  </ul>

  <script>
    // DOM是必须掌握的技能, 但是原生DOM书写太繁琐, 可以利用封装技巧, 把复杂的DOM操作进行简化

    class Query {
      constructor(sel) {
        const els = document.querySelectorAll(sel)
        console.log('els:', els)
        // 把查询到的内容 移动到 构造的对象里
        for (const key in els) {
          // this[0] = li
          // this[1] = li
          // this[2] = li
          // this['length'] = 3
          this[key] = els[key]
        }
      }

      css(name, value) {
        for (let i = 0; i < this.length; i++) {
          const li = this[i]
          li.style[name] = value
        }
      }
    }

    // new: 触发构造方法 constructor
    // const q = new Query('li')
    // console.log('q:', q)
    // q.css('color', 'red')
    // new Query('li').css('color', 'red')

    // $ :比较特别, 显眼, 换别的词也行
    function $(sel) {
      return new Query(sel)
    }

    // 期望效果: 
    // 查询到所有的 li 元素, 设置 style.color = 'red'
    $('li').css('color', 'red')
    $('li').css('border', '2px solid blue')

    // 扩展: 仿造css, 制作一个click 方法, 自动遍历 为 li 添加onclick 事件 
    $('li').click(function () {
      this.style.background = 'orange'
    })

    // 套路总结:
    // 换原型: DOM默认查询出来的原型太简单, 提供的方法太少, 所以用起来不方便
    // 解决: 自己做一套新的原型, 包含更多好用的方法
  </script>
</body>

</html>

总结

DOM: 是程序员必须掌握的技能! 定制化开发网站全靠DOM

  • 但是: 原生DOM操作书写过于复杂
  • 高手帮你封装了框架 jQuery, 大大简化了 DOM操作的代码
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值