九.JavaScript第三座大山:DOM

思维导图是借用一位小姐姐的,感谢小姐姐了,借用地址传送门

微信图片_20210524103112.png

一.DOM事件与事件绑定

什么是事件?什么是事件绑定?

事件是浏览器默认具备的行为,不论是否基于JS绑定方法,事件都是存在的。只要相关的行为触发( 比如点击 ),相关的事件( 点击事件 ) 就会触发。所以,事件绑定是指:给元素的事件行为绑定方法,当事件触发时,方法执行。

1.1鼠标事件

鼠标事件大概可以分为三类:

  • mousedown,mousemove,mouseup: 按下,移动,释放鼠标。
  • click,dblclick: 单击,双击鼠标。
  • mouseover,mouseout:鼠标从一个元素上移入/移出(冒泡)。
  • mouseenter,mouseleave: 鼠标从一个元素上移入/移出(不冒泡)。

下面是详细的介绍:

第一类:点击事件

  • click:按下鼠标(通常是按下主按钮)时触发。
  • dblclick:在同一个元素上双击鼠标时触发。

第二类:按下事件

  • mousedown:按下鼠标键时触发。
  • mousemove:当鼠标在一个节点内部移动时触发。当鼠标持续移动时,该事件会连续触发。为了避免性能问题,建议对该事件的监听函数做一些限定,比如限定一段时间内只能运行一次。
  • mouseup:释放按下的鼠标键时触发。

注意click事件指的是,用户在同一个位置先完成mousedown动作,再完成mouseup动作。因此,触发顺序是,mousedown首先触发,mouseup接着触发,click最后触发。

第三类:移入/移出事件

  • mouseenter:鼠标光标从元素外部首次移动到元素范围之内时触发,这个事件不冒泡,而且在光标移动到后代元素上不会重复触发。通常和 mouseleave搭配使用。
  • mouseleave:鼠标离开一个节点时触发,离开父节点不会触发这个事件(详见后文)。
  • mouseover:鼠标进入一个节点时触发,进入子节点会再一次触发这个事件(详见后文)。
  • mouseout:鼠标离开一个节点时触发,离开父节点也会触发这个事件(详见后文)。

其他:

  • contextmenu:按下鼠标右键时(上下文菜单出现前)触发,或者按下“上下文菜单键”时触发。
  • wheel:滚动鼠标的滚轮时触发,该事件继承的是WheelEvent接口。

tabBar设计图.png

1.2键盘事件

键盘事件:当用户通过键盘在页面上执行操作时触发

keydown:用户按下键盘上的任意键时触发,而且按住不放的话,会重复触发此事件,返回键码keyCode

keypress:用户按下键盘上的字符键时触发,而且按住不放的话,会重复触发此事件,按下esc也会触发,更适用于使用 charCode(字符码,ASCII码)

keyup:用户释放键盘上的键时触发
input 文本框内容输入中

1.3用户界面事件

用户界面事件:当用户与页面的上的元素交互时发生,但不一定与用户操作有关的事件。

load事件:当页面加载完毕时在window上触发,当图像加载完毕时在ing元素上触发,等等,页面完全加载完毕(包括所有图像,js文件,css等外部文件资源)时触发,img只要设置了src就会下载

unload事件:当页面完全卸载时在window上触发,等等。当一个页面切换到另一个页面时就会触发,经常利用这个事件来清除引用,减少内存的泄露

error:当发生js错误时在window上触发,当图像无法加载时在img上触发

abort事件:当用户停止下载过程时,如果嵌入的内容没有加载完成,则在object元素上触发

select:当用户选择文本框(input或textarea)中的一或多个字符时触发

resize:当窗口或框架的大小发生改变时在window上触发

scroll:当用户滚动带滚动条的元素中的内容时,在该元素上触发

1.4 焦点事件事件

焦点事件:当元素获得焦点或失去焦点时触发

blur:失去焦点时触发,这个事件不会冒泡

focus:获得焦点时触发,不冒泡

focusin:在获得焦点时触发,但他冒泡,DOM3新增

focusout:在失去焦点时触发,冒泡

1.5移动端-小程序事件

touchstart: 当手指触摸屏幕的时候出发

touchmove: 当手指在屏幕移动的时候

touchend: 手指离开屏幕的时候触发

touchcancel:  当被迫中止滑动的时候触发(弹消息,来电等等);

tap:手指触摸后离开(点击)

longtap: 手指触摸后后,超过350ms离开

事件绑定的写法同组件的属性,以 key、value 的形式。

key 以bind或catch开头,然后跟上事件的类型,如bindtap, catchtouchstart

1.6表单事件

1.表单获取

通过id获取。如var form = document.getElementById(“form1”);

通过 document.forms 可以取得页面中所有的表单。如
var firstForm = document.forms[0];//取得页面中的第一个表单

通过name获取,var myForm = document.forms[“form2”];//取得页面中名称为”form2″的表单

2.提交表单

<input>的type设为submit

<button>的type设为submit

<input>的type设为image

需要注意的是提交完后一般禁用

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>表单提交</title>
</head>
<body>
<form action="" id="myForm">
  <!-- 通用提交按钮 -->
  <input type="submit" value="Submit Form">
  <!-- 自定义提交按钮 -->
  <button type="submit">Submit Form</button>
  <!-- 图像按钮 -->
  <input type="image" src="icon.png">

</form>
<script type="text/javascript">
  var form = document.getElementById("myForm");

  form.addEventListener('submit',function (event) {
    alert("点击了提交按钮!!!");
    //阻止提交哦啊事件
    event.preventDefault();
  })
</script>
</body>
</html>
复制代码
//javascript还可以编程进行表单提交,但是不会触发submit事件。如:
var form = document.getElementById("myForm");
form.submit();//提交表单
复制代码

3.重置表单

重置表单时,会把表单字段重置为页面刚加载完的初始值。重置表单有两种方式:

<input>的type设为reset

<button>的type设为reset

需要注意的时,与表单提交不同,用编程方式重置表单是会出发reset事件的。

例子:

<!DOCTYPE html>
<head>
  <meta charset="UTF-8">
  <title>表单提交</title>
</head>
<body>
<form action="" id="myForm">
  <input type="text" value="初始值为text">
  <input type="radio" name="danxuankuang"></input>
  <input type="radio" value="女" name="danxuankuang" checked></input>

  <input type="reset"> 重置</input>
  <button type="reset">重置</button>
</form>
</body>
</html>
复制代码

4.表单字段属性

每个表单对象都有elements 属性,该属性是表单中所有表单元素(字段)的集合。

表单字段共有属性

11获.PNG
5.表单字段的共有方法

focus():表单字段获取焦点。HTML5 为表单字段新增了一个 autofocus 属性。在支持这个属性的浏览器中,只要设置这个属性,不用 JavaScript 就能自动把焦点移动到相应字段。

blur():表单字段失去焦点

6.表单字段的共有事件

blur:当前字段失去焦点时触发。

change:对于 和 元素,在它们失去焦点且 value 值改变时触发;对于元素,在其选项改变时触发。

focus:当前字段获得焦点时触发。

1.7 音视频事件

HTML5 DOM 为 <audio> <video> 元素提供了方法、属性和事件。这些方法、属性和事件允许您使用 JavaScript 来操作 <audio><video>元素。

方法

  • addTextTrack() 向音频/视频添加新的文本轨道。
  • canPlayType() 检测浏览器是否能播放指定的音频/视频类型。
  • load() 重新加载音频/视频元素。
  • play() 开始播放音频/视频。
  • pause() 暂停当前播放的音频/视频。

更多详见W3C文档传送门

1.8事件绑定

1.8.1 DOM0级事件绑定

语法元素.onxxx = function(){}

原理:给当前元素对象的某些私有属性赋值一个函数。 当我们在控制台输出某个元素对象时,可以查看这些属性。事件行为触发时,浏览器JS引擎会帮助我们把方法执行。

移除绑定元素.onxxx = null,让这个属性指向一个空对象。

局限性: 由于事件作为私有属性存在,因此只能给当前元素的某个事件行为绑定一个方法,如果绑定多个则后面的会将前面的替换掉。 有些事件在元素的私有属性里是不存在的,无法给这些事件绑定方法。

1.8.1 DOM2级事件绑定

语法元素.addEventListener('xxx',function (){},[false/true]),[]可选参数,默认为false( 在冒泡阶段执行 )

原理:基于元素的原型链__proto__找到EventTarget.prototype,使用内置的addEventListener和removeEventListener进行事件绑定 / 移除绑定(在不考虑兼容性的情况下)。

底层原理:基于浏览器内置的事件池机制完成事件监听&方法绑定。

function fn1(){}
function fn2(){}
function fn3(){}
box.addEventListener('click',fn1)
box.addEventListener('click',fn2)
box.removeEventListener('click',fn1)
box.removeEventListener('click',fn2)
复制代码
面试题:window.onload和document.ready区别(JQ中的$(document).ready())

我之前看过部分JQ源码,其中包含$(document).ready()的处理

document.addEventListener(“DOMContentLoaded”, completed)

=>1)它是基于DOM2级事件中事件池监听实现事件绑定的,所以可以在相同页面中给事件绑定好多不同的方法,也就是可以使用多次

=>2)它监听的是DOMContentLoaded事件,等待DOM结构一加载完就会触发执行的

而window.onload本身基于DOM0事件绑定,而且监听的是load事件,所以页面中不仅只能用一次,而且需要等到浏览器所有资源都加载完毕才会触发执行,触发的节点要晚于DOMContentLoaded

….

所以我们之前做项目的时候,有的项目没有用JQ,我想在DOM结构加载完再去做一些事情,我就仿照着JQ写过一些公共方法

二.事件对象和事件传播机制

1.事件对象

2.1给某个元素绑定一个鼠标点击事件,系统给事件函数传入一个对象,可以在事件函数里获取一些属性

<!DOCTYPE html>
<head>
  <meta charset="UTF-8">
  <title></title>
</head>
<body>
<div class="box" style="width: 300px;height: 400px;background-color: purple;">
<div class="box2" style='width:100px;height:100px;background-color:red;'></div>
</div>
<script>
 let box = document.querySelector(".box");
      box.onclick = function (e) {
        console.log(e);//会有一个鼠标事件对象MouseEvent
        console.log(e.type); //'click' 返回的是当前事件的类型
        console.log(e.target); //box或者box2 表示事件发生的目标元素,就是当前点击区域的最内侧的元素
        console.log(e.currentTarget); //box 表示事件源,事件是由谁注册的,currentTarget就是谁

        //与坐标相关的属性
        console.log(e.clientX, e.clientY); //坐标原点是浏览器的左上角(视窗)
        console.log(e.pageX, e.pageY); 
        /*
        坐标原点是当前页面的左上角,距离页面的最左上角多远就是哪个坐标,可以认为是client+鼠标滚动条卷去的
        */
        console.log(e.offsetX, e.offsetY); // 坐标原点是当前点击区域最内侧元素的左上角(target的左上角)
        console.log(e.screenX, e.screenY); //  坐标原点是当前显示器屏幕的左上角(包括了菜单栏之类的)

        e.preventDefault(); //表示取消事件的默认行为
        e.stopPropagation(); //表示阻止当前事件的进一步捕获或冒泡,但是同一层的不能阻止,同层的阻止可以使用e.stopImmediatePropagation();
      };
</script>
</body>
</html>
复制代码

2.2键盘事件对象

<!DOCTYPE html>
<head>
  <meta charset="UTF-8">
  <title></title>
</head>
<body>
<script>
      document.onkeydown = function (e) {
        console.log(e);//KeyboardEvent,敲键盘获取这个事件
        console.log(e.keyCode);//获取对应键盘码
      };
</script>
</body>
</html>
复制代码

2.3window.onload事件对象

      window.onload = function (e) {
        console.log(e);//Event,
      };
复制代码

2.什么是事件默认行为?如何取消默认行为?

很多元素天生就有一些默认行为:

  • a标签我们点击的时候会实现页面跳转;
  • 文本框输入的时候可以输入内容
  • 部分情况下文本框存在输入记忆
  • 鼠标按右键出现右键菜单

如何阻止默认行为?用 ev.preventDefault();

 document.oncontextmenu = function (ev) {
    // 禁止右键菜单
      ev.preventDefault();
      // return false; 这也是阻止默认行为
    }; 
复制代码

3.事件传播机制

事件的默认传播机制有三种:事件捕获,目标阶段及冒泡阶段:

事件捕获当鼠标点击或者触发dom事件时,浏览器会从最外层层级结构一直向里层查找,直到找到当前触发事件的事件源为止。(目的是为冒泡阶段提供传播的路径 => ev.path)

目标阶段触发当前事件源的相关行为 【目标】

冒泡阶段不仅当前事件源的相关行为被触发,其所有祖先元素的相关事件行为都会被触发(在这个过程中,哪一个元素的事件行为绑定了方法,方法都会被触发执行,而且顺序是由里层向外层传播) 【冒泡】

/层结构:window–>document–>html–>body–>outer–>inner–>center/

<!DOCTYPE html>
<head>
  <meta charset="UTF-8">
  <title></title>
  <style>
    .outer{
      width: 400px;
      height: 400px;
      background-color: pink;
      padding:75px 0;
    }
    .inner{
      width: 250px;
      height: 250px;
      background-color: red;
      padding:75px 0;

    }
    .center{
      width: 100px;
      height: 100px;
      background-color: orange;
    }
    .vh{
      margin: 0 auto;
      box-sizing: border-box;
    }
  </style>
</head>
<body>
  <div class="outer vh">
    outer
    <div class="inner vh">inner
      <div class="center vh">center</div>
    </div>
  </div>
<script>
document.body.onclick=function(e){
  console.log('BODY',e);
} 
var outer=document.querySelector('.outer')
outer.onclick=function(e){
  console.log('outer',e);
  
}
var inner=document.querySelector('.inner')
inner.onclick=function(e){
  console.log('inner',e);
  
}
var center=document.querySelector('.center')
center.onclick=function(e){
  //阻止冒泡传播
  e.stopPropagation()//点击center之后只出发他自己的事件
  console.log('center',e);
  
}
</script>
</body>
</html>
复制代码

分析mouseover和mouseenter

无标444题.png

三.放大镜项目

项目一:

1.项目展示:

捕获且请求.PNG

2.思路图解:

无盒子题.png

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <!--css样式-->
    <style>
      * {
        margin: 0;
        padding: 0;
      }
      .small {
        position: relative;
        width: 400px;
        height: 400px;
        border: 1px solid #888888;
        margin: 50px;
      }
      .small img {
        width: 400px;
        height: 400px;
      }
      .small .mask {
        position: absolute;
        top: 0;
        height: 0;
        width: 150px;
        height: 150px;
        background-color: rgba(244, 245, 234, 0.3);
        cursor: move;
      }
      .big {
        width: 400px;
        height: 400px;
        border: 1px solid #888888;
        position: absolute;
        top: 50px;
        left: 470px;
        background-image: url("small.jpg");
        background-size: 266%;
        /* 
        mask/small=big/img-size
        bg-size=img-size/img大小
         */
      }
    </style>
  </head>
  <body>
    <div class="box" id="box">
      <div class="small">
        <img src="small.jpg" alt="" />
        <div class="mask"></div>
      </div>
      <div class="big"></div>
    </div>

    <script>
      let small = document.querySelector(".small");
      let mask = document.querySelector(".mask");
      let big = document.querySelector(".big");
      small.addEventListener("mousemove", function (e) {
        //鼠标距离可视区左上的距离
        let ClientX = e.clientX;
        let ClientY = e.clientY;

        // small元素距离浏览器边界的距离
        let picT = small.offsetTop;
        let picL = small.getBoundingClientRect().left;

        //计算75是盒子宽高的一半
        let top = ClientY - picT - 75;
        let left = ClientX - picL - 75;
        top < 0 ? (top = 0) : top > 250 ? (top = 250) : top;

        left < 0 ? (left = 0) : left > 250 ? (left = 250) : left;
        mask.style.top = top + "px";
        mask.style.left = left + "px";
        /*
         背景图片定位
        background-position:50%,50%;
        mask移动的距离/最大距离就是盒子的百分比定位
        */
        big.style.backgroundPosition =
          (left / 250) * 100 + "%" + (top / 250) * 100 + "%";
        console.log(big);
      });
    </script>
  </body>
</html>
复制代码

项目二:思路和上面的一样

项目展示:

捕获2222左遮罩.PNG


<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      body {
        background-color: #d8e7fa;
      }
      ul {
        margin: 0;
        padding: 0;
        list-style: none;
      }
      .item_area {
        position: relative;
        width: 400px;
        height: 480px;
        border: 1px solid #888888;
        margin: 50px;
      }
      .item_area .pic {
        margin-bottom: 15px;
      }
      .item_area .pic img {
        width: 400px;
        height: 400px;
      }
      .item_area .pic .cover {
        position: absolute;
        top: 0;
        height: 0;
        width: 150px;
        height: 150px;
        background-color: rgba(244, 245, 234, 0.3);
        /* background-image: url("http://m.qpic.cn/psc?/V53ysRyG2hb34N3Z4SMG2cU0tw2YGwud/TmEUgtj9EK6.7V8ajmQrEF3A.cd5SVBGQ98wblceKrKmGMuFRajAVrG8epoZ3ThAD3WX0R87tWNSsqShuhIkPSQXevJNxYNsOFhbDP0IwDQ!/b&bo=ngGeAQAAAAABFzA!&rf=viewer_4"); */
      }
      .item_area .list {
        display: flex;
      }
      /* 居中了 */
      .item_area .list li {
        margin: auto;
      }
      .item_area .list .current {
        border: 2px solid red;
      }
      .item_area .list img {
        width: 50px;
        height: 50px;
        display: block;
      }
      .item_area .detail {
        width: 400px;
        height: 400px;
        border: 1px solid #888888;
        position: absolute;
        top: 0;
        left: 420px;
        background-image: url("http://m.qpic.cn/psc?/V53ysRyG2hb34N3Z4SMG2cU0tw2YGwud/TmEUgtj9EK6.7V8ajmQrEE**OyApeGoKZrvniaEY4v8roNrqMw4s9kl20nHeRQq4tOrb*JryiRYPqWUOwHgtr1.AqYUuNFvC31czu9Xus9c!/b&bo=IAMgAyADIAMBGT4!&rf=viewer_4");
        background-size: 266%;
      }
    </style>
  </head>
  <body>
    <div class="item_area">
      <div class="pic">
        <img
          src="http://m.qpic.cn/psc?/V53ysRyG2hb34N3Z4SMG2cU0tw2YGwud/TmEUgtj9EK6.7V8ajmQrEE**OyApeGoKZrvniaEY4v8roNrqMw4s9kl20nHeRQq4tOrb*JryiRYPqWUOwHgtr1.AqYUuNFvC31czu9Xus9c!/b&bo=IAMgAyADIAMBGT4!&rf=viewer_4"
        />
        <div class="cover"></div>
      </div>
      <ul class="list">
        <li>
          <img
            class="current"
            src="http://m.qpic.cn/psc?/V53ysRyG2hb34N3Z4SMG2cU0tw2YGwud/TmEUgtj9EK6.7V8ajmQrEE**OyApeGoKZrvniaEY4v8roNrqMw4s9kl20nHeRQq4tOrb*JryiRYPqWUOwHgtr1.AqYUuNFvC31czu9Xus9c!/b&bo=IAMgAyADIAMBGT4!&rf=viewer_4"
          />
        </li>
        <li>
          <img
            src="http://m.qpic.cn/psc?/V53ysRyG2hb34N3Z4SMG2cU0tw2YGwud/TmEUgtj9EK6.7V8ajmQrEFFUJb*SjxiRjUm5CqPgRHQJvLtt6K4qJWr7HWFlxoW8T3tSgtql1w9aUeMSAnMdBb3v1d7UHHwCo2lJenl7EvE!/b&bo=IAMgAyADIAMBGT4!&rf=viewer_4"
          />
        </li>
        <li>
          <img
            src="http://m.qpic.cn/psc?/V53ysRyG2hb34N3Z4SMG2cU0tw2YGwud/TmEUgtj9EK6.7V8ajmQrEIVorZ64*1gdI.lE1*VJ9XFIFCwwKWIwIeAtPrFjVrgsifWR.StUcRpd7GDUWAH5MKRhdv5VWYchM9WmD8KnSy4!/b&bo=IAMgAyADIAMBGT4!&rf=viewer_4"
          />
        </li>
        <li>
          <img
            src="http://m.qpic.cn/psc?/V53ysRyG2hb34N3Z4SMG2cU0tw2YGwud/TmEUgtj9EK6.7V8ajmQrEHk79Ewm5AdRJ.UAIqlM3him5yyJJ2KJrIKgTzNPyX9VaNS0vr4*B2jk7NQfldcx2gXxVDDlgbE07Rf4q4cYirg!/b&bo=IAMgAyADIAMBGT4!&rf=viewer_4"
          />
        </li>
        <li>
          <img
            src="http://m.qpic.cn/psc?/V53ysRyG2hb34N3Z4SMG2cU0tw2YGwud/TmEUgtj9EK6.7V8ajmQrEMivVmlhpCyP3T2uilzKFl4kXOuRpkmfUOp7z1xEeFAG9u7.sTQwGZtj6VZd11b7IUeqEkX8ejHXRo5tsXOrscI!/b&bo=9AH0AfQB9AEBGT4!&rf=viewer_4"
          />
        </li>
        <li>
          <img
            src="http://m.qpic.cn/psc?/V53ysRyG2hb34N3Z4SMG2cU0tw2YGwud/TmEUgtj9EK6.7V8ajmQrEIsKTjdjju63APyAqUvb9nxp8CfdVIbJSOURXmeQ3cz107vyVN9t25WjMI9JYZs1f5bc9SkI1agXK29WoT9tlcY!/b&bo=IAMgAyADIAMBGT4!&rf=viewer_4"
          />
        </li>
      </ul>
      <div class="detail"></div>
    </div>
    <script>
      let list = document.querySelector(".list");
      let imgs = list.querySelectorAll("img");
      let img = document.querySelector(".pic img");
      let pic = document.querySelector(".item_area .pic");
      let cover = document.querySelector(".cover");
      let detail = document.querySelector(".detail");
      list.addEventListener("mousemove", function (e) {
        if (e.target.tagName == "IMG") {
          img.src = e.target.src;
          detail.style.backgroundImage = `url("${e.target.src}")`;
          imgs.forEach((item) => {
            item.className = "";
          });
          e.target.className = "current";
        }
      });
      pic.addEventListener("mousemove", function (e) {
        let ClientX = e.clientX;
        let ClientY = e.clientY;

        // 元素距离浏览器边界的距离
        let picT = pic.getBoundingClientRect().top;
        let picL = pic.getBoundingClientRect().left;
        //计算75是盒子宽高的一半
        let top = ClientY - picT - 75;
        let left = ClientX - picL - 75;
        top < 0 ? (top = 0) : top > 250 ? (top = 250) : top;

        left < 0 ? (left = 0) : left > 250 ? (left = 250) : left;
        cover.style.top = top + "px";
        cover.style.left = left + "px";
        detail.style.backgroundPosition =
          (left / 250) * 100 + "%" + (top / 250) * 100 + "%";
      });
    </script>
  </body>
</html>

复制代码

项目三:盒子脱宅效果

鼠标移入盒子,左键按住盒子可以随意拖动

项目思路和一些问题:

  鼠标移入到盒子按下之后移动鼠标盒子跟着动
  mousedown:开始拖拽
  mousemove:随时计算盒子最新位置,让盒子跟着鼠标移动
  mouseup:结束
鼠标只有按下的时候,才触发mousemove和mouseup

鼠标焦点丢失问题:
=>鼠标移动过快,鼠标会脱离盒子,在盒子外面鼠标的移动无法触发mousemove,盒子不会更新位置
=>盒子外面松开鼠标,也不会触发mouseup导致mousemove没有移出,鼠标重新进入盒子,不管是否按下都会移动
 <解决方案>:
          ie和火狐:setCapture和releaseCapture可以实现把元素和鼠标绑定在一起(或者移除绑定的效果),来防止鼠标焦点丢失。
          谷歌中解决方案:把mousedown事件绑定给盒子,把move和up绑定事件给document,并改变this指向,
          解除的时候也要改变this指向;适用于所有浏览器鼠标焦点解决方案
复制代码

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      .box {
        width: 100px;
        height: 100px;
        background: blue;
        cursor: move;
        position: absolute;
        top: 0;
        left: 0;
      }
    </style>
  </head>
  <body>
    <div class="box" id="box"></div>
    <script>
      let box = document.getElementById("box");
      box.addEventListener("mousedown", down);
      // 按下鼠标执行的操作
      function down(ev) {
        //记录鼠标按下的位置和盒子起始的位置(把值记录在元素的自定义属性上,便于调用)
        // offsetLeft相对于父级参照物的左偏移量
        this.boxL = this.offsetLeft;
        this.boxT = this.offsetTop;
        //this.startX按下的时候鼠标的位置
        this.startX = ev.clientX;
        this.startY = ev.clientY;
        console.log(ev.clientX, ev.clientY);
        this._MOVE = move.bind(this);
        this._up = up.bind(this);
        console.log(this._up, this._MOVE);

        document.addEventListener("mousemove", this._MOVE);
        document.addEventListener("mouseup", this._up);
      }
      function move(ev) {
        let curL = ev.clientX - this.startX + this.boxL,
          curT = ev.clientY - this.startY + this.boxT;
        this.style.top = curT + "px";
        this.style.left = curL + "px";
      }
      function up(ev) {
        console.log(this); //box

        document.removeEventListener("mousemove", this._MOVE);
        document.removeEventListener("mouseup", this._up);
        // console.log(this._up, this._MOVE);
        // document.removeEventListener("mousemove", move.bind(this));
        // document.removeEventListener("mouseup", up.bind(this));
      }

      box.addEventListener("drag", function (e) {
        console.log(e.offsetX, e.offsetY);
      });
    </script>
  </body>
</html>
复制代码

三.DOM操作

3.1 获取dom元素

1.通过id获取指定元素

document.getElementById(“box”);

由于id不可以重复, 对象返回, 找不到就返回Null;注意点

2.通过class名称/name名称/标签名称

document.getElementsByClassName(“father”);
document.getElementsByName(“test”);
document.getElementsByTagName(“div”);
由于class可以重复, 所以找到了就返回一个存储了标签对象的数组, 找不到就返回一个空数组

3.通过选择器获取(重点记住这两种方法)
querySelector只会返回根据指定选择器找到的第一个元素

document.querySelector(“#box”);
document.querySelector(“.father”);
document.querySelector(“div>form”);

querySelectorAll会返回指定选择器找到的所有元素

let oDivs = document.querySelectorAll(“.father”);

获取指定元素的所有子元素:

法一:let oDiv = document.querySelector("div");
    console.log(oDiv.children);
    // children属性获取到的是指定元素中所有的子元素

法二:let oDiv = document.querySelector("div");
     console.log(oDiv.childNodes);
      // childNodes属性获取到的是指定元素中所有的节点
     //只想获取元素节点
    for(let node of oDiv.childNodes){
        // console.log(node.nodeType);
        // if(node.nodeType === 1){
        if(node.nodeType === Node.ELEMENT_NODE){
            console.log(node);
        }
    }
复制代码

什么是节点?
DOM对象(document), 这个对象以树的形式保存了界面上所有的内容
HTML页面每一部分都是由节点(标签(元素),文本,属性)

节点怎么数.png

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div>
      <h1>1</h1>
      <h2>2</h2>
      <p class="item">3</p>
      <p>4</p>
      <span>5</span>
    </div>
    <script>
      /*
        获取指定元素的子节点或子元素
        */
      let oDiv = document.querySelector("div");
      //获取指定节点中的第一个子节点
      console.log(oDiv.firstChild); //#text
      //获取指定元素中的第一个子元素
      console.log(oDiv.firstElementChild); //<h1>1</h1>

      //获取指定节点中最后一个子节点
      console.log(oDiv.lastChild); //#text
      //  获取指定元素中最后一个子元素
      console.log(oDiv.lastElementChild); //<span>5</span>

      /*通过子元素获取父元素/父节点*/
      let item = document.querySelector(".item");
      console.log(item.parentElement); //<div>...<div/>
      console.log(item.parentNode); //<div>...<div/>
      //兼容性写法,支持火狐 let parentEle = item.parentElement || item.parentNode;
      // console.log(parentEle);

      //获取相邻上一个节点
      console.log(item.previousSibling); //#text
      //获取相邻上一个元素
      console.log(item.previousElementSibling); //<h2>2</h2>

      //获取相邻下一个节点
      console.log(item.nextSibling); //#text
      // 获取相邻下一个元素
      console.log(item.nextElementSibling); //<p>4</p>
    </script>
  </body>
</html>
复制代码

3.2 元素节点增删改查

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div>
      <h1>1</h1>
      <h2>2</h2>
      <p class="item">3</p>
      <p>4</p>
      <span>5</span>
    </div>
    <script>
      //   1.创建节点
      let oSpan = document.createElement("span");
      console.log(oSpan); //<span><span/>
      console.log(typeof oSpan); //object

      // 2.添加节点(appendChild方法会将指定的元素添加到最后;)
      let oDiv = document.querySelector("div");
      oDiv.appendChild(oSpan);
      let oA = document.createElement("a");
      oDiv.appendChild(oA);

      // 3.插入节点
      // 获取第一个p,然后在p前面加一个span
      let oP = document.querySelector("p");
      oDiv.insertBefore(oSpan, oP);

      // 4.删除节点
      // 注意点: 在js中如果想要删除某一个元素, 只能通过对应的父元素来删除
      //         元素是不能够自杀的
      console.log(oSpan.parentNode); //div...div
      oSpan.parentNode.removeChild(oSpan);

      // 5.克隆节点
      // 注意点: cloneNode方法默认不会克隆子元素, 如果想克隆子元素需要传递一个true
      //   let newDiv = oDiv.cloneNode(); //<div><div/>
      let newDiv = oDiv.cloneNode(true); //<div>...<div/>
      console.log(newDiv);
    </script>
  </body>
</html>
复制代码

3.3 元素属性操作

无论是通过document创建还是查询出来的标签,系统都会将元素包装成一个对象返回给我们,系统在包装这个对象的时候会自动将元素的属性都包装到这个对象中,所以只要拿到这个对象就可以拿到标签属性,操作标签属性.

  <img src="images/1.jpg" alt="我是alt222" title="我是title" />
    <script>
      // 1.如何获取元素属性
      let oImg = document.querySelector("img");
      console.log(oImg.alt);
      console.log(oImg.getAttribute("alt"));
      //   注意点: 通过对象.属性名称的方式无法获取到自定义属性的取值
      //   通过getAttribute方法可以获取到自定义属性的取值

      // 2.如何修改或新增元素属性(setAttribute方法如果属性不存在就是新增, 如果属性存在就是修改)
      oImg.title = "新的title";
      oImg.setAttribute("title", "新的title222");
      oImg.setAttribute("myname", "图片");

      // 3.如何删除元素属性
      oImg.alt = "";
      oImg.removeAttribute("alt");
    </script>
复制代码

3.4 元素内容操作

  <div>
      我是div
      <h1>我是标题</h1>
      <p>我是段落</p>
    </div>
    <script>
      // 1.获取元素内容
      /*
         1.innerHTML获取的内容包含标签, innerText/textContent获取的内容不包含标签
         2.innerHTML/textContent获取的内容不会去除两端的空格, innerText获取的内容会去除两端的空格
      */

      let oDiv = document.querySelector("div");
      console.log(oDiv.innerHTML); //含标签,不去空格
      console.log(oDiv.innerText); //不含标签,去空格
      console.log(oDiv.textContent); //不含标签,不去空格

      // 2.设置元素内容
      /*
         新的内容都会覆盖原有的内容
      区别:
         如果通过 innerHTML 设置数据, 数据中包含标签, 会转换成标签之后再添加
         如果通过 innerText/textContent 设置数据, 数据中包含标签, 不会转换成标签, 会当做一个字符串直接设置
      */
      oDiv.innerHTML = "<span>我是 span</span>";
      console.log(oDiv);
      /*
      <div>
         <span>我是 span</span>
      </div>
      */
      oDiv.innerText = "<span>我是 span</span>";
      console.log(oDiv);
      /*
         <div><span>我是 span</span></div>
      */
    </script>
复制代码

3.5操作元素样式

      // 1.设置元素样式
      let oDiv = document.querySelector("div");
      // 第一种方式:给元素添加类名设置写好了的样式
      oDiv.className = "box";

      // 第二种方式
      // 注意点: 过去CSS中通过-连接的样式, 在JS中都是驼峰命名
      // 注意点: 通过JS添加的样式都是行内样式, 会覆盖掉同名的CSS样式
      oDiv.style.width = "300px";
      oDiv.style.height = "300px";
      oDiv.style.backgroundColor = "blue";

      // 2.获取元素样式
      //  行内样式的属性通过style属性获取到;
      console.log(oDiv.style.width);
      /*
      CSS设置的属性值必须通过getComputedStyle方法来获取;
      getComputedStyle方法接收一个参数, 这个参数就是要获取的元素对象;
      getComputedStyle方法返回一个对象, 这个对象中就保存了CSS设置的样式和属性值;
      */
      let style = window.getComputedStyle(oDiv);
      console.log(style.width);
复制代码

3.6 操作元素事件

1.什么是事件?

用户和浏览器之间的交互行为我们就称之为事件, 比如:点击,移入/移出

2.如何给元素绑定事件?

在JavaScript中所有的HTML标签都可以添加事件;
元素.事件名称 = function(){};
当对应事件被触发时候就会自动执行function中的代码

    let oBtn = document.querySelector("button");
    oBtn.onclick = function () {
        alert("按钮被点击了");
    }
    // 注意点: 如果给元素添加了和系统同名的事件, 我们添加的事件不会覆盖系统添加的事件
    let oA = document.querySelector("a");
    oA.onclick = function () {
        alert("a标签被点击了");
        // 以下代码的含义: 用我们添加的事件覆盖掉系统同名的事件
        return false;
    }
复制代码

3.7 js定时器

    <div>
     <button id="start">开始</button>
     <button id="close">结束</button>
   </div>
   <script>
     //   **定时器的关闭与启动**
     let startBtn = document.querySelector("#start");
     let id = null;
     //点击之后开启定时器
     startBtn.onclick = function () {
       this.disabled = true;
       id = setInterval(function () {
         console.log("定时器在运行");
       }, 1000);
     };
     let closeBtn = document.querySelector("#close");
     //   点击之后关闭定时器;
     closeBtn.onclick = function () {
       startBtn.disabled = false;
       clearInterval(id);
     };
复制代码

四.事件委托

事件委托,通俗的说就是将元素的事件委托给它的父级或者更外级的元素处理,它的实现机制就是事件冒泡。

事件委托的优点:

1.只需要将同类元素的事件委托给父级或者更外级的元素,不需要给所有的元素都绑定事件,减少内存占用空间,提升性能。

2.动态新增的元素无需重新绑定事件

需要注意的点:

事件委托的实现依靠的冒泡,因此不支持事件冒泡的事件就不适合使用事件委托。
不是所有的事件绑定都适合使用事件委托,不恰当使用反而可能导致不需要绑定事件的元素也被绑定上了事件。

简单版本:

  <body>
    <ul>
      <li>新闻</li>
      <li>娱乐</li>
      <li>图片</li>
      <li>视频</li>
      <li>音乐</li>
      <li>购物</li>
    </ul>
    <script>
      let ul = document.querySelector("ul");

      ul.addEventListener("click", function (e) {
        if (e.target.tagName.toLowerCase() === "li") {
          fn(e);
        }
      });
      function fn(e) {
        e.target.style.color = "red";
        e.target.style.backgroundColor = "blue";
      }
    </script>
  </body>
复制代码

高级版本:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>06-JavaScript-元素内容操作</title>
  </head>
  <body>
    <ul>
      <li>新闻</li>
      <li>娱乐</li>
      <li>图片</li>
      <li>视频</li>
      <li>音乐</li>
      <li>购物</li>
    </ul>
    <script>
      function eventDelegate(parentSelector, targetSelector, events, foo) {
        // 触发执行的函数
        function triFunction(e) {
          // 兼容性处理
          var event = e || window.event;

          // 获取到目标阶段指向的元素
          var target = event.target || event.srcElement;

          // 获取到代理事件的函数
          var currentTarget = event.currentTarget;

          // 处理 matches 的兼容性
          if (!Element.prototype.matches) {
            Element.prototype.matches =
              Element.prototype.matchesSelector ||
              Element.prototype.mozMatchesSelector ||
              Element.prototype.msMatchesSelector ||
              Element.prototype.oMatchesSelector ||
              Element.prototype.webkitMatchesSelector ||
              function (s) {
                var matches = (
                    this.document || this.ownerDocument
                  ).querySelectorAll(s),
                  i = matches.length;
                while (--i >= 0 && matches.item(i) !== this) {}
                return i > -1;
              };
          }

          // 遍历外层并且匹配
          while (target !== currentTarget) {
            // 判断是否匹配到我们所需要的元素上
            if (target.matches(targetSelector)) {
              var sTarget = target;
              // 执行绑定的函数,注意 this
              foo.call(sTarget, Array.prototype.slice.call(arguments));
            }

            target = target.parentNode;
          }
        }

        // 如果有多个事件的话需要全部一一绑定事件
        events.split(".").forEach(function (evt) {
          // 多个父层元素的话也需要一一绑定
          Array.prototype.slice
            .call(document.querySelectorAll(parentSelector))
            .forEach(function ($p) {
              $p.addEventListener(evt, triFunction);
            });
        });
      }
      eventDelegate("ul", "li", "click", function () {
        console.log(this);
        this.style.backgroundColor = "red";
      });
    </script>
  </body>
</html>
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享