JavaScript内容回顾第九天

内容回顾

当一切看起来都不起作用的时候, 我就会像个石匠一样去敲打石头.
可能敲100次, 石头不会有任何反应.
但是101次, 石头可能就会裂为两半, 我知道并不是第101次起了作用, 而是前面积累所致.

士兵突击 - 许三多

一. 元素操作

1.1. 创建/插入/移除/克隆

  • document.createElement()
  • append
  • prepend
  • before
  • after
  • repaceWith
  • remove
  • cloneNode
  • 前面我们使用过 document.write 方法写入一个元素:

    • 这种方式写起来非常便捷,但是对于复杂的内容、元素关系拼接并不方便;

    • 它是在早期没有DOM的时候使用的方案,目前依然被保留了下来;

  • 那么目前我们想要插入一个元素,通常会按照如下步骤:

    • 步骤一:创建一个元素;

    • 步骤二:插入元素到DOM的某一个位置;

  • 创建元素: document.createElement(tag)

 // 真实创建一个DOM
    var h2El = document.createElement("h2")
    h2El.className = "tiile"
    h2El.classList.add("active")
    h2El.textContent = "我是标题"
  • 插入元素的方式如下:

    • node.append(…nodes or strings) —— 在 node 末尾 插入节点或字符串,

    • node.prepend(…nodes or strings) —— 在 node 开头 插入节点或字符串,

    • node.before(…nodes or strings) —— 在 node 前面 插入节点或字符串,

    • node.after(…nodes or strings) —— 在 node 后面 插入节点或字符串,

    • node.replaceWith(…nodes or strings) —— 将 node 替换为给定的节点或字符串

 // 真实创建一个DOM
    var h2El = document.createElement("h2")
    h2El.className = "tiile"
    h2El.classList.add("active")
    h2El.textContent = "我是标题"

    // 将元素插入到boxEl里面
    // append追加  添加(添加到最后面)
    boxEl.append(h2El)
    // 添加到前面
    boxEl.prepend(h2El)
    
    // 在box前面添加 兄弟元素
    boxEl.before(h2El)
    // 在box后面添加 兄弟元素
    boxEl.after(h2El)

    // 替换 box就消失了
    boxEl.replaceWith(h2El, "abc")

    // 插入到span和p元素之间
    var spanEl = document.querySelector("span")
    // 方法一: 节点
    var spanEl = boxEl.children[0]
    // 方法二: 加上标识
    var spanEl = document.querySelector(".box-first")
    // 方法三
    var spanEl = boxEl.querySelector("span")
    spanEl.after(h2El)
  • 移除元素我们可以调用元素本身的remove方法:

  • 如果我们想要复制一个现有的元素,可以通过cloneNode方法:

    • 可以传入一个Boolean类型的值,来决定是否是深度克隆;

    • 深度克隆会克隆对应元素的子元素,否则不会;

 <button class="clone-btn">复制</button>
  <button class="remove-btn">移除box</button>

  <div class="box">
    <h2>我是标题</h2>
    <p>我是文本, 哈哈哈哈</p>
  </div>

// 1. 获取元素
    var boxEl = document.querySelector(".box")
    var removeBtnEl = document.querySelector(".remove-btn")
    var cloneBtnEl = document.querySelector(".clone-btn")

    // 2. 监听btn
    removeBtnEl.onclick = function() {
      boxEl.remove()
    }

    // 复制box
    var counter = 0
    cloneBtnEl.onclick = function() {
      var newNode = boxEl.cloneNode(true)
      newNode.children[0].textContent = "我也是标题" + counter
      // console.log(newNode)
      // boxEl.after(newNode)
      document.body.append(newNode)
      counter++
    }
  • 在很多地方我们也会看到一些旧的操作方法:

    • parentElem.appendChild(node):

      • 在parentElem的父元素最后位置添加一个子元素

      • parentElem.insertBefore(node, nextSibling):

      • 在parentElem的nextSibling前面插入一个子元素;

    • parentElem.replaceChild(node, oldChild):

      • 在parentElem中,新元素替换之前的oldChild元素;
    • parentElem.removeChild(node)****:

      • 在parentElem中,移除某一个元素;

1.2. 元素大小/位置/滚动

  • clientWidth: cotentWidth + padding(不包含滚动条)

  • clientHeight: contentHeight + padding (不包含滚动条)

    // 几乎用不到

  • clientTop: border-top的宽度

  • clientLeft: border-left的宽度

    //

  • offsetWidth: 元素完整的宽度(包含border padding)

  • offsetHeight: 元素完整的高度

  • offsetLeft: 距离父元素的x

  • offsetTop: 距离父元素的y

  • scrollHeight: 整个可滚动的区域高度

  • scrollTop: 滚动部分高度

var boxEl = document.querySelector(".box")
    // 1. 获取样式(局限性很强)
    // var boxStyle = getComputedStyle(boxEl)
    // console.log(boxStyle.width, boxStyle.height)

    // 2. 获取更多信息
    console.log(boxEl.clientWidth)
    console.log(boxEl.clientHeight)

    console.log(boxEl.clientTop)
    console.log(boxEl.clientLeft)

    console.log(boxEl.offsetWidth)

    console.log(boxEl.offsetLeft)
    console.log(boxEl.offsetTop)

    console.log(boxEl.scrollHeight)
    console.log(boxEl.scrollTop)

    // window
    window.onclick = function () {
      console.log(boxEl.scrollTop)
    }

1.3. window大小/滚动

  • inner/outer
  • html
  • scrollX
  • scrollY
  • scrollBy(x, y)
  • scrollTo(x, y)
  • window的width和height

    • innerWidth、innerHeight:获取window窗口的宽度和高度(包含滚动条)
    • outerWidth、outerHeight:获取window窗口的整个宽度和高度(包括调试工具、工具栏)
    • documentElement.clientHeight、documentElement.clientWidth:获取html的宽度和高度(不包含滚动条)
  • window的滚动位置:

    • scrollX:X轴滚动的位置(别名pageXOffset)

    • scrollY:Y轴滚动的位置(别名pageYOffset)

  • 也有提供对应的滚动方法:

    • 方法 scrollBy(x,y) :将页面滚动至 相对于当前位置的 (x, y) 位置;

    • 方法 scrollTo(pageX,pageY) 将页面滚动至 绝对坐标;

    •  // window大小
          console.log(window.outerWidth)
          console.log(window.outerHeight)
        
          console.log(window.innerWidth)
          console.log(window.innerHeight)
        
          console.log(document.documentElement.clientWidth)
          console.log(document.documentElement.clientHeight)
        
          console.log(document.documentElement.offsetWidth)
          console.log(document.documentElement.offsetHeight)
        
          // 获取window的滚动区域
          window.onclick = function () {
            console.log(window.scrollX)
            console.log(window.scrollY)
          }
        
          // window.onscroll = function() {
          //   console.log(window.scrollY)
          // }
        
          window.onscroll = function () {
            var scrollY = window.scrollY
            if (scrollY > 600) {
              // scrollBtnEl.style.display = "block"
              scrollBtnEl.hidden = false
            } else {
              // scrollBtnEl.style.display = "none"
              scrollBtnEl.hidden = true
            }
          }
        
          // 点击按钮滚动某个位置
          var scrollBtnEl = document.querySelector(".scroll-btn")
          scrollBtnEl.hidden = true
          scrollBtnEl.onclick = function () {
            // 相对于原来的基础上增加100
            // window.scrollBy(0, 100)
            // 绝对值 定位到300, 每次都定位到300
            window.scrollTo(0, 0)
          }
      

1.4. 案例练习

1.4.1. 动态创建列表 prompt
<h1>动态创建列表</h1>
  <ul class="list"></ul>
var ulEl = document.querySelector(".list")

    var isFlag = true
    while (isFlag) {
      var message = prompt("请输入信息")
      if (!message) {  // 没有输入内容
        isFlag = false
      } else {
      var liEl = document.createElement("li")
      liEl.textContent = message
      ulEl.append(liEl)
      }
    }
1.4.2. 动态展示当前时间
 <h1 class="time">2022-05-19 11:14:30</h1>

 // 封装工具函数
    function padLeft(content, count, padStr) {
      count = count || 2
      padStr = padStr || "0"
      content = String(content)
      return content.padStart(count, padStr)
    }

    // 1. 获取时间元素
    var timeEl = document.querySelector(".time")

    // 2. 获取具体的时间并且格式化
    setInterval(function () {
      var date = new Date()
      var year = date.getFullYear()
      var month = padLeft(date.getMonth() + 1)
      var day = padLeft(date.getDate())
      var hour = padLeft(date.getHours())
      var minute = padLeft(date.getMinutes())
      var second = padLeft(date.getSeconds())
      // 3. 将时间放到timeEl里面
      timeEl.textContent = `${year}-${month}-${day} ${hour}:${minute}:${second}`  
    }, 1000);
    // 补充string方法 很新的方法
    // var str = "4"
    // console.log(str.padStart(2, "0"))
1.4.3. 考拉倒计时展示
  • 获取当前时间
  • 获取今天的24点
  • 计算毫秒时间差
    • 转小时
    • 转分钟
    • 转秒钟
  • 计时器中
.countdown {
      /* display: flex; */
      color: #f00;
      font-size: 20px;
    }

    .countdown .time {
      background-color: #f00;
      color: #fff;
      display: inline-block;
      padding: 5px;
      border-radius: 3px;
    }

  <div class="countdown">
    <span class="time hour">03</span>
    <span class="split">:</span>
    <span class="time minute">25</span>
    <span class="split">:</span>
    <span class="time second">45</span>
  </div>

封装的js

// 封装工具函数
function formatPadLeft(content, count, padStr) {
  count = count || 2
  padStr = padStr || "0"
  content = String(content)
  return content.padStart(count, padStr)
}

正式的js代码

 // 1. 获取元素
    var hourEl = document.querySelector(".hour")
    var minutEl = document.querySelector(".minute")
    var secondEl = document.querySelector(".second")

     // 获取倒计时小时-分钟-秒钟
      // 11:53 => 24:00:00
      var endDate = new Date()
      endDate.setHours(24)
      endDate.setMinutes(0)
      endDate.setSeconds(0)
      endDate.setMilliseconds(0)

    setInterval(function () {
      var newDate = new Date()
      var intervalTime = Math.floor((endDate.getTime() - newDate.getTime()) / 1000)
      // console.log(intervalTime)

      // 55089596: x小时x分钟x秒钟
      // 125: x百x十x个
      // var num = 125
      var hour = Math.floor(intervalTime / 3600)
      var minute = Math.floor(intervalTime / 60) % 60
      var second = intervalTime % 60

      // 2. 设置内容
      hourEl.textContent = formatPadLeft(hour)
      minutEl.textContent = formatPadLeft(minute)
      secondEl.textContent = formatPadLeft(second)
    }, 1000)

二. 事件处理

2.1. 事件处理方案

  • 如何进行事件监听呢?

    • 事件监听方法一: 在script中直接监听 (很少用)

    • 事件监听方法二: DOM属性, 通过元素on来监听事件

    • 事件监听方法三:通过EventTarget中的addEventLisstener监听

  •  // 1. 获取元素对象
        var btn2El = document.querySelector(".btn2")
        var btn3El = document.querySelector(".btn3")
      
        // 2. onclick属性
        // function handleClick01() {
        //   console.log("按钮2发生了点击")
        // }
        //
        // function handleClick02() {
        //   console.log("按钮2发生了点击")
        // }
      
        // btn2El.onclick = function() {
        //   console.log("按钮2发生了点击")
        // }
        // btn2El.onclick = handleClick01
        // btn2El.onclick = handleClick02
        //
        // var obj = {
        //   name: "why"
        // }
        // obj.name = "kobe"
        
        // 3. addEventListener(推荐)
        btn3El.addEventListener("click", function() {
          console.log("第一个btn3的事件监听")
        })
        btn3El.addEventListener("click", function() {
          console.log("第二个btn3的事件监听")
        })
        btn3El.addEventListener("click", function() {
          console.log("第三个btn3的事件监听")
        })
    

2.2. 事件捕获冒泡

  • 事件流的不同传播

    • 捕获
    • 冒泡
  • addEventListener(“click”, fn, true)

  • 事实上对于事件有一个概念叫做事件流, 为什么会产生事件流呢?
  • 我们可以想到一个问题: 当我们浏览器上对着一个元素点击时, 你点击的不仅仅是这个元素本身

    • 这是因为我们HTML元素是存在父子元素叠加层级的
    • 比如一个span元素放在div元素上, div元素是放在body元素上的, body元素是放在html元素上的
    • 我们会发现默认情况下事件是从内层的span向外依次传递的顺序,这个顺序我们称为事件冒泡(Event Bubble)
    • 事实上, 还有另外一种监听事件流的方式就是从外层到内层, 这种称之为事件捕获(Event Canture)
    • 为什么会产生两种不同的处理流呢?
    • 这是因为早期浏览器开发时, 不管ie还是Netscape公司都发现了这个问题
    • 但是他们采用了完全相反的事件流来对事件进行传递
    • ie采用了事件冒泡的方式吗Netscape采用了事件捕获的方式
  • 如果我们都监听, 那么会按照如下的顺序来执行

    • 捕获阶段(Capturing phase)

    • 事件 (从Window)向下走近元素

    • 目标阶段(Target phase)

    • 事件到达目标元素

    • 冒泡阶段(Bubbling phase)

    • 事件从元素上开始冒泡

    • 事实上, 我们可以通过event对象来获取当前阶段

    • eventPhase

    • 开发中通常会使用事件冒泡, 所以事件捕获了解即可

2.3. 事件对象event

  • 两个方法:
    • preventDefault: 阻止默认行为
    • stopPropagation: 阻止事件传递
  • type: 事件的类型

  • target: 当前事件发生的元素 (常用)

  • currentTarget: 当前处理事件的元素 (常用)

  • eventPhase: 事件所处的阶段

  • offsetX, offsetY: 事件发生在元素内的位置

  • clientX, clientY: 事件发生在客户端内的位置

  • pageX, pageY: 事件发生在客户端相对于document的位置

  • screenX, screenY: 事件发生相对于屏幕的位置

 var divEl = document.querySelector("div")
    var btnEl = document.querySelector(".btn")
    // btnEl.onclick = function(e) {
    //   console.log("按钮发生了点击", e)
    // }

    // divEl.onclick = function(e) {
    //   console.log("div发生了点击", e)
    // }

    divEl.onclick = function(event) {
      // 偶尔会使用
      // console.log("事件类型", event.type)
      // console.log("事件阶段", event.eventPhase)

      // 比较少使用
      // console.log("事件元素中的位置", event.offsetX, event.offsetY)
      // console.log("事件客户端中的位置", event.clientX, event.clientY)
      // console.log("事件页面中的位置", event.pageX, event.pageY)
      // console.log("事件相对于屏幕的位置", event.screenX, event.screenY)

      // target, currentTarget
      console.log(event.target)
      console.log(event.currentTarget)
      console.log(event.target === event.currentTarget)
    }
 // 阻止默认行为
    // var aEl = document.querySelector("a")
    // aEl.onclick = function(event) {
    //   // alert("a元素发生了点击")
    //   console.log("a元素发生了点击")
    //   // 阻止默认行为 a就是跳转
    //   event.preventDefault()
    // }

    // 阻止事件进一步传递
    var btnEl = document.querySelector("button")
    var spanEl = document.querySelector("span")
    var divEl = document.querySelector(".box")


    // div的事件捕获
    divEl.addEventListener("click", function(e) {
      console.log("div的事件捕获监听")
      // e.stopPropagation()
    }, true)

    spanEl.addEventListener("click", function() {
      console.log("span的事件捕获监听")
    }, true)

    btnEl.addEventListener("click", function(e) {
      console.log("btn的事件捕获监听")
      e.stopPropagation()
    }, true)

    divEl.addEventListener("click", function () {
        console.log("div的事件冒泡监听")
      })

      spanEl.addEventListener("click", function () {
        console.log("span的事件冒泡监听")
      })

      btnEl.addEventListener("click", function () {
        console.log("btn的事件冒泡监听")
      })

2.4. 事件函数中的this

  • 处理的元素
  • 在函数中, 我们也可以通过this来获取当前的发生元素

  • 这是因为浏览器内部, 调用event.handler是绑定到当前的currentTarget上的

var divEl = document.querySelector("div")
    var btnEl = document.querySelector("button")
    divEl.onclick = function(event) {
      console.log(this)
      console.log(event.currentTarget)
      console.log(divEl)
      console.log(this === divEl)  // true
    }
    // divEl.addEventListener("click", function() {
    //   console.log(this)
    // })

2.5. EventTarget的使用

  • addEventListener
  • removeEventListener
  • dispatchEvent
  • 我们会发现,所有的节点、元素都继承自EventTarget

    • 事实上Window也继承自EventTarget;
  • 那么这个EventTarget是什么呢?

    • EventTarget是一个DOM接口,主要用于添加、删除、派发Event事件;
  • EventTarget常见的方法:

    • addEventListener:注册某个事件类型以及事件处理函数;

    • removeEventListener:移除某个事件类型以及事件处理函数;

    • dispatchEvent:派发某个事件类型到EventTarget上;

 var btnEl = document.querySelector("button")
    // var foo = function() {
    //   console.log("监听到按钮的点击")
    // }
    // btnEl.addEventListener("click", foo)

    // 需求: 过5s秒后, 将这个事件监听移除掉
    // setTimeout(function() {
    //   btnEl.removeEventListener("click", foo)
    // }, 5000)


    // 无法移除的
    btnEl.addEventListener("click", function() {
      console.log("监听的事件")
    })

    setTimeout(function() {
      btnEl.removeEventListener("click", function() {

      })
    }, 5000)

// eventtarget就可以实现类似于事件总线
    window.addEventListener("coderwhy", function() {
      console.log("监听到coderwhy事件")
    })

  // 5秒后派发coderwhy事件
  setTimeout(function() {
    window.dispatchEvent(new Event("coderwhy"))
  }, 5000)

2.6. 事件委托(delegation)

  • 案例一: ul中li点击active
  • 案例二: 排他的思想
  • 案例三: 多个按钮的区分
    • data-*
  • 事件冒泡在某种情况下可以帮助我们实现强大的事件处理模式 – 事件委托模式**(也是一种设计模式)** 那么这个模式是怎么样的呢?

    • 因为当子元素被点击时,父元素可以通过冒泡可以监听到子元素的点击;

    • 并且可以通过event.target获取到当前监听的元素;

<ul>
  <li>1</li>
  <li>2</li>
  <li>3</li>
  <li>4</li>
  <li>5</li>
  <li>6</li>
  <li>7</li>
  <li>8</li>
  <li>9</li>
  <li>10</li>
</ul>
var liEls = document.querySelectorAll('li');
  // for(var i = 0; i < liEls.length; i++) {
  
  // }
  // 1. 每一个函数都监听自己的点击, 并且有自己的处理函数 (自己的函数)
  // for (var liEl of liEls) {
  //   liEl.onclick = function(event) {
  //     // 方法一
  //     // this.classList.add("active")
  //     // 方法二
  //     event.currentTarget.classList.add("active")
  //   }
  // }
  
  // 2. 同一在ul中监听
  // var ulEl = document.querySelector("ul")
  // ulEl.onclick = function(event) {
  //   // console.log("点击了某一个li", e.target)
  //   event.target.classList.add("active")
  // }
  
  // 3. 新需求: 点击li变成active, 其他的取消active
  // var ulEl = document.querySelector("ul")
  // ulEl.onclick = function (event) {
  //   // 将之前的active移除掉
  //   for (var i = 0; i < ulEl.children.length; i++) {
  //     var liEl = ulEl.children[i]
  //     if (liEl.classList.contains("active")) {
  //       ulEl.children[i].classList.remove("active")
  //     }
  //   }
  //   // 给点击元素添加active
  //   event.target.classList.add("active")
  // }
  
  // var ulEl = document.querySelector("ul")
  // ulEl.onclick = function (event) {
  
  //   var activeLiEl = ulEl.querySelector(".active")
  //   // 找到active的li移除掉
  //   // activeLiEl &&  activeLiEl.classList.remove("active")  // 或者用if
  //   if (activeLiEl) {
  //     activeLiEl.classList.remove("active")
  //   }
  
  //   event.target.classList.add("active")
  // }
  
  
  // 另一个方法
  var ulEl = document.querySelector('ul');
  var activeLiEl = null;
  ulEl.onclick = function (event) {
    // 1. 变量记录的方式
    if (activeLiEl && event.target !== ulEl) {
      activeLiEl.classList.remove('active');
    }
    // 2. 点击的元素添加active
    if (event.target !== ulEl) {
      event.target.classList.add('active');
    }
    
    // 3. 记录最新的active对应的li
    activeLiEl = event.target;
  };
 <div class="box">
    <!-- 因为不加上data的话是非标准拿出来麻烦所以加上data -->
    <button data-action="remove">移除</button>
    <button data-action="new">新建</button>
    <button data-action="search">搜索</button>
    <button>1111</button>
  </div>

 var boxEl = document.querySelector(".box")
    boxEl.onclick = function(event) {
      var btnEl = event.target
      var action = btnEl.dataset.action
      switch(action) {
        case "remove":
          console.log("点击了移除按钮")
          break
        case "new":
          console.log("点击了新建按钮")
          break
        case "search":
          console.log("点击了搜索按钮")
          break
        default:
            console.log("点击了其他")
      }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

coderyhh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值