04-WebApl

04-WebApl_第1张图片 04-WebApl_第2张图片

Web Apis 和 JS基础关联性

JS 的组成

04-WebApl_第3张图片 04-WebApl_第4张图片

API 和 Web api

​ MDN 详细 API : https://developer.mozilla.org/zh-CN/docs/Web/API

  1. API 是为我们程序提供的一个接口 , 帮助我们实现某种功能
  2. Web API 主要针对 于浏览器提供的接口 , 是浏览器提供的一套操作浏览器功能页面元素的 API ( BOM 和 DOM ) . 主要针对于浏览器做交互效果
  3. Web API 一般都有输入和输出 ( 函数的传参和返回值 ) , Web API 很多都是方法 ( 函数 )
  4. 学习 Web API 可以结合前面学习内置对象方法的学习思路

DOM★

04-WebApl_第5张图片04-WebApl_第6张图片

DOM 文档对象模型 , 处理 HTML 和 XML 文件的标准编程接口 , 通过这些接口可以改变网页内容 . 结构和样式

DOM 树

04-WebApl_第7张图片
  • 文档 : 一个页面就是一个文档 , DOM中使用 document 表示
  • 元素 : 页面中的所有标签都是元素 , DOM 中使用 element 表示
  • 节点 : 网页中的所有内容都是节点 ( 标签 , 属性 , 文本 , 注释等 ) , DOM 中使用 node 表示

DOM 把以上内容都看做是对象

我们获取过来的DOM元素是一个对象(object),所以称为 文档对象模型

获取元素

getElementByid() 和 document.querySelector(“选择器”) 返回的是元素

根据 id 获取getElementByid()

  • get 获得 element 元素 by 通过 驼峰命名法

  • 文档下面找元素 : document.getElementById("time")

  • 参数 id 是大小写敏感的字符串 , 必须加引号 ("time")

  • 返回的是一个元素对象 object

      var timer = document.getElementById("time");
      console.log(timer);
      console.log(typeof timer); // object
      // console .dir 打印我们返回的元素对象,更好的查看里面的属性和方法
      console.dir(timer)

根据标签名获取 getElementsByTagName()

获取某一类元素或者某一类标签

  • 返回的是获取过来元素对象的集合 , 以伪数组的形式存放
  • 要依次打印里面的元素对象 , 可以用遍历
  • 如果页面中只有一个 li 元素 , 返回的还是伪数组的形式
  • 如果页面中没有这个元素 , 返回的是空伪数组的形式
      // 1. 返回的对象
      var lis = document.getElementsByTagName("li");
      console.log(lis);
      console.log(lis[0]); // 获取索引为0的元素
      // 2.要依次打印里面的元素对象 , 可以用遍历
      for (var i = 0; i < lis.length; i++) {
        console.log(lis[i]);
      }

还可以获取某个元素 ( 父元素 ) 内部所有指定标签名的子元素

element.getElementsByTagName (“标签名”)

注意 : 父元素必须是单个对象 ( 必须指明是哪一个元素的对象 ) , 获取的时候不包括父元素自己

    <ol id="ol">
      <li>1</li>
      <li>2</li>
      <li>3</li>
      <li>4</li>
    </ol>
    
      // 方法一 父元素必须是单个对象 必须得加索引
      // var ol = document.getElementsByTagName("ol"); //[ol]; // 伪数组
      // console.log(ol[0].getElementsByTagName("li"));
      // 方法二 元素必须是单个对象 给父元素取个 id 名
      var ol = document.getElementById("ol"); //ol;
      // element.getElementsByTagName ("标签名")
      console.log(ol.getElementsByTagName("li"));

通过 HTML5 新增的方法获取

缺点 : 不兼容低版本浏览器 IE 6 7 8 , 移动端不限制

  • document.getElementsByClassName("类名") 根据类名返回元素对象集合
  • document.querySelector("选择器") 根据指定选择器返回第一个元素对象 , 里面选择器需要加符号#nav .box
  • document.querySelectorAll ( "选择器" ) 返回指定选择器所有元素对象集合
      //1. getElementsByClassName("类名")根据类名获得某些元素集合
      var boxs = document.getElementsByClassName("box");
      console.log(boxs);
      //2. document.querySelector("选择器")根据选择器返回的第一个元素对象
      var firstBox = document.querySelector(".box"); // 盒子1
      console.log(firstBox);
      var nav = document.querySelector("#nav"); // 首页,产品
      console.log(nav);
          var li = document.querySelector("li"); // 首页
          console.log(li);
      //3. querySelectorAll(选择器) 返回指定选择器的所有元素对象集合
      var allBox = document.querySelectorAll(".box");
      console.log(allBox);
      var lis=document.querySelectorAll("li")
      console.log(lis);

获取特殊元素 body html

  • 获取 body 元素

document.body; // 返回body 对象

      // 1. 获取body 元素
      var bodyEle = document.body;
      console.log(bodyEle);
      console.dir(bodyEle);
  • 获取 html 元素

document.documentElement; 返回 html 对象

      // 2. 获取html 元素
      var htmlEle = document.documentElement;
      console.log(htmlEle);
      console.dir(htmlEle)

事件基础

概述

事件是可以被 JAveScript 侦测到的行为

简单理解 : 触发 — 响应机制

网页中的每个元素都可以产生某些可以触发 JAveScript 的事件 , 例如可以在用户点击某按钮时产生一个事件 , 然后去执行某些操作

事件三要素

事件有三部分组成 : 事件源 事件类型 事件处理程序

  • 事件源 : 事件被触发的对象 按钮
  • 事件类型 : 触发条件 点击(onclick),标经过,键盘按下
  • 事件处理程序 : 通过一个函数赋值的方式
      // 事件源
      var btn = document.getElementById("btn");
     // 事件源.事件类型=事件处理程序
      btn.onclick = function () {
        alert("点秋香");
      };
04-WebApl_第8张图片

事件执行步骤

  1. 获取事件源
  2. 注册事件 ( 绑定事件 )
  3. 添加事件处理程序 ( 采取函数赋值形式 )
      // 点击div 控制台输出 我被选中了
      // 1. 获取事件源
      var div = document.querySelector("div");
      // 2. 绑定事件 onclick
      // 3. 添加事件处理程序
      div.onclick=function(){
          console.log("我被点击了");
      }

常见的鼠标事件

04-WebApl_第9张图片

操作元素★

JavaScript 的 DOM 操作可以改变网页内容 , 结构样式 , 我们可以利用 DOM 操作元素来改变元素里面的内容 , 属性等 . 注意以下都是属性

改变元素内容(获取或设置)

element . innerText

div.innerText = "内容";

从起始位置到终止位置的内容 , 但它去除 html 标签 , 同时空格和换行也会去掉 (不保留格式) , 非标准

案例 - 点击按钮 div 里面的内容发生变化

      // 当我们点击了按钮 div 里面的文字会发生变化
      // 1.获取元素
      var btn = document.querySelector("button");
      var div = document.querySelector("div");
      // 2.绑定事件 3.添加事件处理程序
      btn.onclick = function () {
        // div.innerText = "2022-02-02";
        // 修改 div 里面的内容 , 调用时间函数赋值到div
        div.innerText = getDate();
      };
      // 封装一个时间函数
      function getDate() {
        var date = new Date();
        var year = date.getFullYear();
        var month = date.getMonth() + 1;
        var dates = date.getDate();
        var arr = ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"];
        var day = date.getDay();
        var h = date.getHours();
        h = h < 10 ? "0" + h : h; // 不足10 补0
        var m = date.getMinutes();
        m = m < 10 ? "0" + m : m;
        var s = date.getSeconds();
        s = s < 10 ? "0" + s : s;
        return "今年是:" + year + "年" + month + "月" + dates + "日 " + arr[day] + "\t" + h + ":" + m + ":" + s;
      }

元素可以不用添加事件

      // 1. 获取元素
      var p = document.querySelector("p");
      // 修改 p 里面的内容 , 调用时间函数赋值到 p
      // 打开页面后直接显示时间,不用添加点击事件
      p.innerText = getDate();
04-WebApl_第10张图片

element . innerHTML

div.innerHTML = "内容";

起始位置到终止位置的全部内容 , 包括 html 标签 , 同时保留空格和换行 (保留格式) W3C标准

innerText 和 innerHTML 区别

这两个属性是可读写的 , 可以获取元素里面的内容

      console.log(p.innerText); // 我是文字 123 不保留格式不识别html 标签
      console.log(p.innerHTML); // 我是文字 123
  • 获取内容时的区别:

​ innerText会去除空格和换行 (非标准的 ),而innerHTML会保留空格和换行 ( W3C 标准的)

  • 设置内容时的区别:

​ innerText不会识别html,而innerHTML会识别

常用元素的属性操作

  1. innerText innerHTML 改变元素内容
  2. src href
  3. id alt title

获取属性的值

元素对象.属性名

设置属性的值

元素对象.属性名 = 值

  <body>
    <button id="ldh">刘德华</button>
    <button id="zxy">张学友</button>
    <img src="./images/ldh.jpg" alt="" title="刘德华" />
    <script>
      // 1. 获取元素
      var zxy = document.getElementById("zxy");
      var ldh = document.getElementById("ldh");
      var img = document.querySelector("img");
      // 2. 绑定事件 3. 处理程序
      zxy.onclick = function () {
        // 修改图片地址属性
        img.src = "./images/zxy.jpg";
        // 修改图片提示文本属性
        img.title = "张学友";
      };
      ldh.onclick = function () {
        img.src = "./images/ldh.jpg";
        img.title = "刘德华";
      };
    </script>
  </body>
04-WebApl_第11张图片

案例 - 分时问候并显示不同图片案例

案例分析

  1. 根据系统不同的时间来判断 , 所以需要用到内置对象
  2. 利用多分支语句来设置不同图片
  3. 需要一个图片 , 并且根据时间修改图片 , 就需要用到操作 src 属性
  4. 需要一个 div 元素 , 显示不同问候语 , 修改元素内容即可
  <body>
    <img src="./images/s.gif" alt="" />
    <div>上午好</div>
    <script>
      // 1. 获取元素
      var img = document.querySelector("img");
      var div = document.querySelector("div");
      // 2.日期内置对象 得到当前小时数
      var date = new Date();
      var h = date.getHours();
      // 3.判断小时数改变图片和文字信息
      if (h < 12) {
        img.src = "./images/s.gif";
        div.innerHTML = "上午好";
      } else if (h < 18) {
        img.src = "./images/x.gif";
        div.innerHTML = "下午好";
      } else {
        img.src = "./images/w.gif";
        div.innerHTML = "晚上好";
      }
    </script>
  </body>

表单元素的属性操作

利用 DOM 可以操作如下表单元素的属性 :

type , value , checked , selected , disabled

获取属性的值

元素对象.属性名

设置属性的值

// this 值向的是事件函数的调用者 btn

元素对象.属性名 = 值

表单元素中有一些属性如:disabled、checked、selected,元素对象的这些属性的值是布尔型。

  <body>
    <button>按钮</button>
    <input type="text" value="输入内容" />
    <script>
      // 1.获取元素
      var btn = document.querySelector("button");
      var input = document.querySelector("input");
      // 2. 绑定事件 3.事件处理程序
      btn.onclick = function () {
        // 表单里面的值是通过 value 开修改的
        input.value = "我被点击了";
        // 按钮点过一次后就禁用 谁禁用就给谁加
        // btn.disabled = true;
        this.disabled = true;
        // this 值向的是事件函数的调用者 btn
      };
    </script>
  </body>

案例 - 显示密码案例

案例分析

  1. 核心思路 : 点击眼睛按钮 , 把密码框类型改为文本框就可以看见里面的密码
  2. 一个按钮两个状态 , 点击一次 , 切换为文本框 , 继续点击一次切换为密码框
  3. 算法 : 利用一个 flag 变量 , 来判断 flag 的值 , 如果是 1 就切换为文本框 . flag 设置为 0 , 如果是 0 就切换为密码框 . flag 设置为 1.
 <style>
      .box {
        position: relative;
        width: 400px;
        border-bottom: 1px solid #ccc;
        margin: 100px auto;
      }
      .box input {
        width: 370px;
        height: 30px;
        outline: none;
        border: 0;
      }
      .box img {
        position: absolute;
        top: 3px;
        right: 3px;
        width: 24px;
      }
    </style>
  </head>
  <body>
    <!-- 布局 先放一个 div 里面放input和label
      div 给一个下边框 , input 去掉边框和轮廓线
      label 里面放 眼睛图片 ,用定位移动位置 -->
    <div class="box">
      <label for="">
        <img src="./images/close.png" alt="" id="eye" />
      </label>
      <input type="password" name="" id="pwd" />
    </div>

    <script>
      // 1.获取元素
      var eye = document.getElementById("eye");
      var pwd = document.getElementById("pwd");
      // 2. 绑定事件 3.处理程序
      var flag = 0;
      eye.onclick = function () {
        if (flag == 0) {
          // 判断条件
          pwd.type = "text";
          eye.src = "./images/open.png";
          flag = 1; // 赋值
        } else {
          pwd.type = "password";
          eye.src = "./images/close.png";
          flag = 0; // 赋值
        }
      // flag = !flag; // true false
      };
    </script>
  </body>
04-WebApl_第12张图片

样式属性操作

我们可以通过 JS 修改元素的大小、颜色、位置等样式。

常用方式

  1. element . style 行内样式操作
  2. element . className 类各样式操作
方式1:通过操作style属性

元素对象.style.样式属性 = 值; 适用于修改样式少的

注意 :

  • JS 里面的样式采取驼峰命名法 , 如 fontSize backgroundColor
  • JS 修改 style 样式操作 , 产生的是行内样式 . css 权重比较高
  <body>
    <div></div>
    <script>
      // 1.获取元素
      var div = document.querySelector("div");
      // 2.注册事件 3 处理程序
      div.onclick = function () {
        //   div.style 属性名 采用驼峰命名法
        this.style.backgroundColor = "green";
        this.style.width = "300px"; // this 指向函数的调用者
      };
    </script>
  </body>

案例 - 淘宝二维码

案例分析

  1. 核心思路 : 利用样式的显示和影藏完成 , display : none 隐藏元素 diplay : black 显示元素
  2. 点击按钮 , 就让这个二维码盒子影藏起来即可
  <body>
    <div class="box">
      淘宝二维码
      <img src="./images/tao.png" alt="" />
      <i class="close-btn">×</i>
      <script>
        // 1.获取元素
        var btn = document.querySelector(".close-btn");
        var box = document.querySelector(".box");
        // 2.绑定事件 3.程序处理
        btn.onclick = function () {
          // 修改样式隐藏
          box.style.display = "none";
        };
      </script>
    </div>
  </body>
04-WebApl_第13张图片

案例 - 循环精灵图

案例分析

  1. 首先精灵图的排列有规律
  2. 核心思路 : 利用 for 循环 , 修改精灵图的背景位置backgroun-position
  3. 计算每个精灵图 y 轴的坐标
  4. 让循环里面的 i 索引号 * - 44 就是每个图的 y 坐标 , 精灵图坐标是负数
    <script>
      // 1. 获取元素 所有的小li
      var lis = document.querySelectorAll("li");
      console.log(lis);

      for (var i = 0; i < lis.length; i++) {
        // 让索引号 乘以 -44 就是每个li 的背景y坐标  index就是我们的y坐标
        var index = i * -44; // 修改每个 li 的坐标 注意字符串拼接
        lis[i].style.backgroundPosition = "0 " + index + "px";
      }
    </script>
04-WebApl_第14张图片

获得焦点 onfocus

失去焦点 onblur

      var text = document.querySelector("input");
      text.onfocus = function () {
        console.log("得到焦点");
      };
      text.onblur = function () {
        console.log("失去焦点");
      };

案例 - 显示隐藏文本框内容

案例分析

  1. 首先表单需要 2 个新事件 , 获得焦点 onfocus 和失去焦点 onblur
  2. 如果获得焦点 , 判断表单里面内容是否为默认文字 , 如果是默认文字 , 就清空表单内容 , 文字颜色变深
  3. 如果失去焦点 , 判断表单内容是否为空 , 如果为空 , 则表单内容改为默认文字 , 文字颜色变浅
  <body>
    <input type="text" value="手机" />
    <script>
      var text = document.querySelector("input");
      text.onfocus = function () {
        // 判断里面的内容是不是默认文字
        if (text.value === "手机") {
          text.value = ""; // 是默认文字就清空表单
        }
        // 获得焦点需要把文本框里面的文字颜色变黑
        text.style.color = "#333";
      };
      text.onblur = function () {
        // 判断表单内容是否为空
        if (text.value === "") {
          text.value = "手机"; // 为空内容就改为默认文字
        }
        // 失去焦点要把文本框里面的颜色变浅色
        text.style.color = "#999";
      };
    </script>
  </body>
04-WebApl_第15张图片
方式2:通过操作className属性

元素对象.className = 值; 适用于修改样式较多的

因为class是关键字,所以使用className。

className 会直接更改元素的类名 , 会覆盖原先的类名

this.className = "change"; 需要在css 中写一个 class 类名样式

this.className = "first change"; 如果想要保留原来的类名 , 可以采用在第一个类名后面追加你要的类名

文本

    <style>
      div {
        width: 100px;
        height: 100px;
        text-align: center;
        line-height: 100px;
        background-color: pink;
      }
      .change {
        background-color: green;
        color: #fff;
        font-size: 25px;
        margin-top: 100px;
      }
    </style>
  </head>
  <body>
    <div>文本</div>
    <script>
      // 1.获取元素
      var div = document.querySelector("div");
      // 2.绑定事件 3.事件处理程序
      div.onclick = function () {
        // 在 css中写一个class 样式
        // 修改元素的 className 更改元素样式 适用于条件较多的情况下
        this.className = "change";
        // 如果想要保留原来的类名 , 可以采用在第一个类名后面追加你要的类名
        this.className = "first change";
      };
    </script>
  </body>

案例 - 密码长度验证信息

案例分析

  1. 首先判断的事件是表单失去焦点 onblur
  2. 如果输入正确则提示正确的信息 , 颜色为绿色 , 小图标变化
  3. 如果输入不是6到16位 , 则提示错误信息 , 颜色为红色 , 小图标变化
  4. 如果里面变化样式较多 , 我们采取 className 修改样式
div {
        width: 600px;
        margin: 100px auto;
      }
      .message {
        display: inline-block;
        font-size: 12px;
        color: #999;
        background: url(./images/mess.png) no-repeat left center;
        padding-left: 20px;
      }
      /* 错误提示的信息 */
      .wrong {
        color: red;
        background-image: url(./images/wrong.png);
      }
      /* 正确提示的信息 */
      .right {
        color: green;
        background-image: url(./images/right.png);
      }
    </style>
  </head>
  <body>
    <div class="register">
      <input type="password" class="ipt" />
      <p class="message">请输入6~16位密码</p>
    </div>
    <script>
      // 1.获取元素
      var ipt = document.querySelector(".ipt");
      var message = document.querySelector(".message");
      // 2.绑定事件 onblur 3.处理程序
      ipt.onblur = function () {
        // 根据表单里面值的长度 ipt.value.
        // if (this.value.length <= 16 && this.value.length >= 6)
        if (this.value.length < 6 || this.value.length > 16) {
          console.log("错误");
          // 用className 更改样式
          // 不满足条件时
          message.className = "message wrong";
          message.innerHTML = "您输入的位数不对要求6~16位";
        } else {
          // 满足条件时
          message.className = "message right";
          message.innerHTML = "您输入的正确";
        }
      };
    </script>
  </body>
04-WebApl_第16张图片

案例 - 开关灯

知识点

  1. 通过操作 style 属性设置 body 的背景颜色
  2. 通过 innerHML 设置 div 内的文字
  3. 小技巧 : flag = 0 开灯 , flag = 1 光灯.
<body>
    <button>关灯</button>
    <script>
      // 1.获取元素
      var btn = document.querySelector("button");
      //   var bodyEle = document.body;
      // 2.绑定事件 3.事件处理程序
      var flag = 0; // flag 要写在函数体外面
      btn.onclick = function () {
        if (flag == 0) {
          // bodyEle.style.backgroundColor = "black";
          document.body.style.backgroundColor = "black";
          this.innerHTML = "开灯";
          flag = 1;
        } else {
          // bodyEle.style.backgroundColor = "#fff";
          document.body.style.backgroundColor = "#fff";
          this.innerHTML = "关灯";
          flag = 0;
        }
      };
    </script>
  </body>
04-WebApl_第17张图片 04-WebApl_第18张图片 04-WebApl_第19张图片

排他细想★

首先排除其他人 , 然后才设置自己的样式 , 这种排除其他人的思想我们称为排他思想

如果有同一组元素,我们想要某一个元素实现某种样式, 需要用到循环的排他思想算法:

  1. 所有元素全部清除样式(干掉其他人)
  2. 给当前元素设置样式 (留下我自己)
  3. 注意顺序不能颠倒,首先干掉其他人,再设置自己
案例 - 按钮排他思想★

核心思想 : 先用 for 循环把所有按钮背景颜色去掉 , 在设置让当前元素背景颜色

内循环干掉他人 ,外循环留下自己

知识点

  1. 排他思想 , 双重 for 循环
  2. 通过操作 style 属性修改样式

注意 : 双重 for 循环 里面两个 i , 它们在不同函数里面 , 在不同作用域中

外循环不能用 btns[i] 涉及闭包

<script>
      // 1.获取元素
      var btns = document.getElementsByTagName("button");
      // btns 得到的是伪数组 ,里面的每一个元素 btns[i]
      // 用 for 循环给每个数组绑定事件
      for (var i = 0; i < btns.length; i++) {
        btns[i].onclick = function () {
          // (1) 先把所有的按钮背景颜色去掉 ,干掉所有人
          for (var i = 0; i < btns.length; i++) {
            btns[i].style.backgroundColor = "";
          }
          // (2) 然后才让当前元素的元素背景颜色改为绿色 留下自己
          this.style.backgroundColor = "green";
            // 不能用btns[i] 涉及闭包
        };
      }
      //内循环干掉他人 ,外循环留下自己
    </script>
04-WebApl_第20张图片
案例 - 百度换肤

① 这个案例练习的是给一组元素注册事件
② 给4个小图片利用循环注册点击事件
③ 当我们点击了这个图片,让我们页面背景改为当前的图片
④ 核心算法: 把当前图片的src 路径取过来,给 body 做为背景即可

知识点

  1. 点击后用 for 循环给 body 用 style 设置背景图片位置 url
  2. 把单前点击图片的位置赋值到 body 里面 注意字符串拼接
      // 1. 获取元素 获取百度下面的img
      var imgs = document.querySelector(".baidu").querySelectorAll("img");
      // 2.循环绑定事件
      for (var i = 0; i < imgs.length; i++) {
        imgs[i].onclick = function () {
          // 点击图片 ,把当前图片的 src 地址给 body
          // 当前图片地址 this.src 用字符串拼接
          document.body.style.backgroundImage = "url(" + this.src + ")";
          document.body.style.backgroundImage=`url(${this.src})`
        };
      }
    </script>
案例 - 表格隔行变色★

案例分析

① 用到新的鼠标事件 鼠标经过 onmouseover 鼠标离开 onmouseout
核心思路:鼠标经过 tr 行,当前的行变背景颜色, 鼠标离开去掉当前的背景颜色
③ 注意: 第一行(thead里面的行)不需要变换颜色,因此我们获取的是 tbody 里面的行

知识点

  1. 用循环给行绑定鼠标经过和离开事件 ,
  2. 鼠标经过和离开给当前行 通过 className 属性添加一个样式
    <script>
      // 1.获取元素 获取的是 tbody 里面所有的行
      var trs = document.querySelector("tbody").querySelectorAll("tr");
      console.log(trs);
      // 2.利用循环绑定注册事件
      for (var i = 0; i < trs.length; i++) {
        // 3.鼠标经过 onmouseover
        trs[i].onmouseover = function () {
          this.className = "bg";
        };
        // 4.鼠标离开 onmouseout
        trs[i].onmouseout = function () {
          this.className = "";
        };
      }
    </script>
04-WebApl_第21张图片
案例 - 表单全选取消全选案例★

业务需求:

  1. 点击上面全选复选框,下面所有的复选框都选中(全选)
  2. 再次点击全选复选框,下面所有的复选框都不中选(取消全选)
  3. 如果下面复选框全部选中,上面全选按钮就自动选中
  4. 如果下面复选框有一个没有选中,上面全选按钮就不选中
  5. 所有复选框一开始默认都没选中状态

方法一

案例分析

  • 需要一个变量 count , 存储的是当前已勾选的下方小复选框转态的个数
  • 判断: count == j_tbs.length 将全选框勾选上 ; 否则 , 将全选框设置为未勾选的转态
      var j_cbAll = document.querySelector("#j_cbAll");
      var j_tbs = document.querySelectorAll("#j_tb input");
      var count = 0;
      j_cbAll.onclick = function () {
        for (var i = 0; i < j_tbs.length; i++) {
          j_tbs[i].checked = this.checked;
        }
        // 全选框点击完, count 重新计数
        if (this.checked) {
          count = j_tbs.length;
        } else {
          count = 0;
        }
      };

      for (var i = 0; i < j_tbs.length; i++) {
        j_tbs[i].onclick = function () {
          if (this.checked) {
            count++;
          } else {
            count--;
          }
          if (count == j_tbs.length) {
            j_cbAll.checked = true;
          } else {
            j_cbAll.checked = false;
          }
        };
      }

方法二

      // 1. 全选和取消全选做法:  让下面所有复选框的checked属性(选中状态) 跟随 全选按钮即可
      // 获取元素
      var j_cbAll = document.querySelector("#j_cbAll");
      var j_tbs = document.querySelectorAll("#j_tb input");

      j_cbAll.onclick = function () {
        for (var i = 0; i < j_tbs.length; i++) {
          j_tbs[i].checked = this.checked;
        }
      };
      // 点击下方小复选框 , 达到某一条件后 , 将全选框设置成勾选转态 , 否则设置未勾选
      for (var i = 0; i < j_tbs.length; i++) {
        j_tbs[i].onclick = function () {
          // 遍历小复选框 但凡发现有一个小复选框未勾选 , 立马可以得出结论,全选框一定是未勾选转态
          // 标识这全选框的勾选转态, 默认是已勾选
          var flag = true;
          for (var i = 0; i < j_tbs.length; i++) {
            if (j_tbs[i].checked == false) {
              flag = false;
              break; // 优化 检测到false 就不继续检测
            }
          }
          // 当遍历已结束,还没有发现有一个小复选框是未勾选时,说明小复选框已全部被勾选
          j_cbAll.checked = flag;
        };
      }

案例分析

① 全选和取消全选做法: 让下面所有复选框的checked属性(选中状态) 跟随 全选按钮即可
② 下面复选框需要全部选中, 上面全选才能选中做法: 给下面所有复选框绑定点击事件,每次点击,都要循环查看下面所有的复选框是否有没选中的,如果有一个没选中的, 上面全选就不选中。
③ 可以设置一个变量,来控制全选是否选中

知识点

  1. 给上面的复选框绑定点击事件 , 处理程序是用 for 循环遍历下面的复选框 , 把上面按钮的选中状态的值赋值给下面的复选框 , 起到同步效果 考核点 : for 循环和表单属性 checked
  2. 用双重 for 循环来控制上面复选框的选中状态 , 内循环判断下面所有复选框是否全部选中 , 外循环控制上面复选框的值为 true 还是 false . 考核点 : 双重 for 循环和表单属性 checked
  3. 小技巧 : flag = true 全选 ; flag = false 不选 ;
      全选和取消全选做法
      // 1. 全选和取消全选做法:  让下面所有复选框的checked属性(选中状态) 跟随 全选按钮即可
      // 获取元素
      var j_cbAll = document.getElementById("j_cbAll"); // 全选按钮
      var j_tbs = document.getElementById("j_tb").getElementsByTagName("input"); // 下面所有的复选框
      // 绑定事件
      j_cbAll.onclick = function () 
        // console.log(j_cbAll.checked); // 打印上面复选框转态
        // this.checked 它可以得到当前复选框的选中状态如果是true 就是选中,如果是false 就是未选中
        // 用for 循环进行遍历 下面的复选框
        for (var i = 0; i < j_tbs.length; i++) {
          // 点击后要把当前复选框的默认选中转态的值传给下面的复选框 (同步)
          j_tbs[i].checked = this.checked;
        }
      };
     

      下面复选框需要全部选中, 上面全选才能选中做法:
      // 2. 下面复选框需要全部选中, 上面全选才能选中做法: 给下面所有复选框绑定点击事件,每次点击,都要循环查看下面所有的复选框是否有没选中的,如果有一个没选中的,上面全选就不选中。
      for (var i = 0; i < j_tbs.length; i++) {
        j_tbs[i].onclick = function () {
          // flag 控制全选按钮是否被选中
          var flag = true;
          // 每次点击下面的复选框都要循环检查者4个小按钮是否全被选中
          for (var i = 0; i < j_tbs.length; i++) {
            if (!j_tbs[i].checked) {
              // 判断条件 如果有四个中有一未选中
              flag = false; // j_cbAll 默认选中转态就为false
              break; // 退出for循环 这样可以提高执行效率 因为只要有一个没有选中,剩下的就无需循环判断了
            }
          }
          j_cbAll.checked = flag;
        };
      }
04-WebApl_第22张图片

自定义属性的操作

获取属性值
  • element.属性 获取属性值。 console.log(div.id);
  • element.getAttribute(‘属性’); console.log(div.getAttribute("index"));

区别:
element.属性 获取内置属性值(元素本身自带的属性)img.src
element.getAttribute(‘属性’); 主要获得自定义的属性 (标准) 我们程序员自定义的属性

设置属性值
  • element.属性 = ‘值’ 设置内置属性值。
      div.id = "test";
      div.className="navs" // 写的className class 关键字
  • element.setAttribute(‘属性’, ‘值’);
      div.setAttribute("index", 2);
      div.setAttribute("class","footer") // class比较特殊 就是写的class

区别:
element.属性 设置内置属性值
element.setAttribute(‘属性’); 主要设置自定义的属性 (标准), 还可以修改内置属性

移除属性

element.removeAttribute(‘属性’);

 div.removeAttribute("index")
案例 - tab 栏切换(重点案例)

案例分析

① Tab栏切换有2个大的模块
② 上面的模块选项卡,点击某一个,当前这一个底色会是红色,其余不变(排他思想) 修改类名的方式
③ 下面的模块内容,会跟随上面的选项卡变化。所以下面模块变化写到点击事件里面。
④ 规律:下面的模块显示内容和上面的选项卡一一对应,相匹配。
⑤ 核心思路: 给上面的tab_list 里面的所有小li 添加自定义属性,属性值从0开始编号。
⑥ 当我们点击tab_list 里面的某个小li,让tab_con 里面对应序号的 内容显示,其余隐藏(排他思想)

知识点

  1. 用外 for 循环给每个 li 设置自定义属性索引 , lis[i].setAttribute("index", i); , 给每个 li 绑定点击事件 , 内 for 循环把每个 li lis[i].className = ""; 属性设置为空清除 , 外循环给单前点击的 li 添加 this.className = "current"; 属性 (排他思想)
  2. 用变量把当前点击的索引保存下来 var index = this.getAttribute("index");
  3. 用内循环把下面所有盒子的样式设置为隐藏 items[i].style.display = "none";
  4. 把当前 li 索引对应的盒子样式设置为显示 items[index].style.display = "block"; (排他思想)
    <script>
      // 获取元素
      var lis = document.querySelectorAll("li");
      var items = document.querySelectorAll(".item");
      // items 的返回值是一个数组 , 它的索引号正好和 li 的索引号数量相等
      // 1. 上面 tab 栏
      for (var i = 0; i < lis.length; i++) {
        // for 循环绑定点击事件
        lis[i].setAttribute("index", i); // 用for 循环动态添加自定义属性
        //  1.上面的模块选项卡,点击某一个,当前这一个底色会是红色,其余不变(排他思想) 修改类名的方式;
        lis[i].onclick = function () {
          for (var i = 0; i < lis.length; i++) {
            lis[i].className = ""; // 干掉所有人 清除所有li class 这个类名
          }
          // var currentLi=document.querySelector(".current")
          // currentLi.className=""
          this.className = "current"; // 留下自己

          // 2.下面的显示内容模块
          var index = this.getAttribute("index"); // 获取到当前 li 的自定义索引
          // 所有的 items 设置隐藏
          for (var i = 0; i < items.length; i++) {
            items[i].style.display = "none";
          }
          items[index].style.display = "block"; // 当前点击索引对应的 item 设置为显示
        };
      }
    </script>
04-WebApl_第23张图片 04-WebApl_第24张图片

H5自定义属性

自定义属性目的:是为了保存并使用数据。有些数据可以保存到页面中而不用保存到数据库中。

自定义属性获取是通过getAttribute(‘属性’) 获取。

但是有些自定义属性很容易引起歧义,不容易判断是元素的内置属性还是自定义属性。

H5给我们新增了自定义属性:

  • 设置 H5 自定义属性

H5规定自定义属性data-开头做为属性名并且赋值。
比如


或者使用 JS 设置
element.setAttribute(‘data-index’, 2) div.setAttribute("data-index", 20);

  • 获取 H5 自定义属性
  1. 兼容性获取 element.getAttribute(‘data-index’); div.getAttribute("data-index")
  2. H5新增 element.dataset.index 或者 element.dataset[‘index’] ie 11才开始支持 就像对象中获取属性一样
      div.setAttribute("data-index", "1");
      div.setAttribute("data-time", "20");
      div.setAttribute("data-list-name", "slf");
      console.log(div.getAttribute("data-index"));
      console.log(div.getAttribute("data-time"));
      console.log(div.getAttribute("data-list-name"));

注意 :

getAttribute 什么开头的属性都可以获取

dataset 只能获取 data- 开头的

      <div data-index="10" data-time="20" data-list-name="andy"></div>
      console.log(div.dataset.index); // 10
      console.log(div.dataset["time"]); // 20
      // 如果自定义属性里面有多个连接的单词 ,我们获取的时候采取 驼峰命名法
      console.log(div.dataset.listName);
      console.log(div.dataset["listName"]);      

节点操作

为什么学节点操作

获取元素通常使用两种方式:

  1. 利用 DOM 提供的方法获取元素

document.getElementById()
document.getElementsByTagName()
document.querySelector 等
逻辑性不强、繁琐

  1. 利用节点层级关系获取元素

利用父子兄节点关系获取元素
逻辑性强, 但是兼容性稍差

这两种方式都可以获取元素节点,我们后面都会使用,但是节点操作更简单

节点概述

​ 网页中的所有内容都是节点(标签、属性、文本、注释等),在DOM 中,节点使用 node 来表示。

​ HTML DOM 树中的所有节点均可通过 JavaScript 进行访问,所有 HTML 元素(节点)均可被修改,也可以创建或删除

04-WebApl_第25张图片

​ 一般地,节点至少拥有nodeType(节点类型)、nodeName(节点名称)和nodeValue(节点值)这三个基本属性。

  • 元素节点 nodeType 为 1
  • 属性节点 nodeType 为 2
  • 文本节点 nodeType 为 3 (文本节点包含文字、空格、换行等)

我们在实际开发中,节点操作主要操作的是元素节点

节点层级

利用 DOM 树可以把节点划分为不同的层级关系,常见的是父子兄层级关系。

04-WebApl_第26张图片

父节点★

node.parentNode

  • parentNode 属性可返回某节点的父节点,注意是最近的一个父节点
  • 如果指定的节点没有父节点则返回 null
      var span = document.querySelector(".son");
      console.log(span.parentNode);

子节点★

      console.log(ul.childNodes); 
      console.log(ul.childNodes[0]);
      console.log(ul.childNodes[0].nodeType); // 3 文本节点
      console.log(ul.childNodes[1].nodeType); // 1 元素节点

parentNode.childNodes 返回包含指定节点的子节点的集合,该集合为即时更新的集合。
注意:返回值里面包含了所有的子节点,包括元素节点文本节点等。
如果只想要获得里面的元素节点,则需要专门处理。 所以我们一般不提倡使用childNodes

father.children // 推荐 , 返回的是元素节点
father.childNodes // 存在兼容性问题

第一个子节点&最后一个子节点
father.children[0];  // 推荐 ,返回的是元素节点
father.children[father.children.length-1]; // 推荐
// 返回所有节点
father.firstChild;
father.lastChild;
// 兼容性问题 , 返回元素节点
father.firstElementChild; 
father.lastElementChild;
var ul = document. querySelector(‘ul’);
for(var i = 0; i < ul.childNodes.length;i++) {
if (ul.childNodes[i].nodeType == 1) {
// ul.childNodes[i] 是元素节点
 console.log(ul.childNodes[i]);
}
}

parentNode.children 是一个只读属性,返回所有的子元素节点。它只返回子元素节点,其余节点不返回 (这个是我们==重点掌握==的)。
虽然children 是一个非标准,但是得到了各个浏览器的支持,因此我们可以放心使用

实际开发中 :

  1. 如果想要第一个子元素节点,可以使用 parentNode.chilren[0]
  2. 如果想要最后一个子元素节点,可以使用 parentNode.chilren[parentNode.chilren.length - 1]

兄弟节点★

// 返回的是元素节点 ,推荐 ie 9 以上支持
son.nextElementSibling; 
son.previousElementSibling;
// 返回的是所有节点
son.nextSibling;
son.previousElementSibling;

长的有兼容性

案例 - 新浪下拉菜单 ★

案例分析

① 导航栏里面的li 都要有鼠标经过效果,所以需要循环注册鼠标事件
② 核心原理: 当鼠标经过li 里面的 第二个孩子 ul 显示, 当鼠标离开,则ul 隐藏

知识点

  1. 用子节点获取元素 var lis = nav.children;
  2. 循环给每个 li 绑定鼠标事件
  3. 获取子元素节点 lis[i].children[1]
  4. 设置样式为显示 this.children[1].style.display = "block";
    <script>
      // 1. 获取元素
      var nav = document.querySelector(".nav");
      var lis = nav.children; // 得到四个小li 伪数组
      // 2.循环绑定事件
      for (var i = 0; i < lis.length; i++) {
        lis[i].onmouseover = function () {
          // 获取子元素节点 设置样式显示
          this.children[1].style.display = "block";
        };
        lis[i].onmouseout = function () {
          this.children[1].style.display = "none";
        };
      }
    </script>
04-WebApl_第27张图片

兄弟节点

下一个兄弟元素节点(有兼容性问题)

1. node.nextSibling

nextSibling 返回当前元素的下一个兄弟元素节点,找不到则返回null。同样,也是包含所有的节点。包含元素节点或者文本节点等等

2. node.previousSibling

previousSibling 返回当前元素上一个兄弟元素节点,找不到则返回null。同样,也是包含所有的节点。

      console.log(div.nextSibling); // 下一个兄弟节点 包含元素节点或者文本节点等等
      console.log(div.previousSibling);

3. node.nextElementSibling

nextElementSibling 返回当前元素下一个兄弟元素节点,找不到则返回null。

node.previousElementSibling

previousElementSibling 返回当前元素上一个兄弟节点,找不到则返回null。

注意:这两个方法有兼容性问题, IE9 以上才支持。

      console.log(div.nextElementSibling); // 下一个兄弟元素节点
      console.log(div.previousElementSibling); 

解决方案 : 封装一个函数

   function getNextElementSibling(element) {
      var el = element;
      while (el = el.nextSibling) {
        if (el.nodeType === 1) { // 判断条件节点类型是否位为元素节点
            return el;
        }
      }
      return null;
    }  

创建节点

document. Element('tagName')

document.createElement() 方法创建由 tagName 指定的 HTML 元素。因为这些元素原先不存在,
是根据我们的需求动态生成的,所以我们也称为动态创建元素节点。

添加节点

    1. node.appendChild(child)

    node 父亲, child 子级

node.appendChild() 方法将一个节点添加到指定父节点的子节点列表末尾。类似于 CSS 里面的
after 伪元素

    1. node.insertBefore(child, 指定元素)

node.insertBefore() 方法将一个节点添加到父节点的指定子节点前面。类似于 CSS 里面的 before
伪元素

页面要添加一个新的元素 :1.创建元素 2.添加元素

      // 1.创建节点
      var li = document.createElement("li");
      // 2.添加节点 node.appendChild(child)
      var ul = document.querySelector("ul");
      ul.appendChild(li); // 父亲下面的儿子最后面添加节点 追加元素
      // 3.添加节点 node.insertBefore(child ,指定元素前面)
      var lili = document.createElement("li"); //
      ul.insertBefore(lili, ul.children[0]);

案例 - 简单发布留言案例 ★

案例分析

① 核心思路: 点击按钮之后,就动态创建一个li,添加到ul 里面。
② 创建li 的同时,把文本域里面的值通过li.innerHTML 赋值给 li
③ 如果想要新的留言后面显示就用 appendChild 如果想要前面显示就用insertBefore

知识点

  1. 双分支语句 if 判断一下值不是为空才执行
  2. 点击按钮后动态创建一个子元素 li var li = document.createElement("li");
  3. 把文本域的值赋值给 li li.innerHTML = text.value;
  4. 把 li 添加到父元素下面第一个子元素的前面 ul.insertBefore(li, ul.children[0]);
    <script>
      // 1. 获取元素
      var btn = document.querySelector("button");
      var text = document.querySelector("textarea");
      var ul = document.querySelector("ul");
      // 2.绑定事件
      btn.onclick = function () {
        if (text.value == "") {
          // 判断条件
          alert("没有输入内容");
          return false; //后面的代码不会执行 , 所以不用else
        } 
          // (1)创建元素
          var li = document.createElement("li");
          // 先有li 才能赋值
          li.innerHTML = text.value;
          // ul.appendChild(li); // 添加到父元素孩子后面
          // (2)添加元素
          ul.insertBefore(li, ul.children[0]); // 添加到第一个子元素前边
      };
    </script>
04-WebApl_第28张图片

删除节点

node.removeChild(child)
node.removeChild () 方法从 DOM 中删除一个子节点,返回删除的节点

    <script>
      var ul = document.querySelector("ul");
      var btn = document.querySelector("button");
      btn.onclick = function () {
        // 如果ul里面的孩子长度为 0说明都删完了
        // if (!ul.children.length) 
        if (ul.children.length == 0) {
          // 让按钮禁用
          this.disabled = true;
        } else {
          // 删除节点 node.removeChild(child)
          ul.removeChild(ul.children[0]);
        }
      };
    </script>

案例 - 删除留言案例

知识点

  1. 文本域的值过来的同时添加一个链接 li.innerHTML = text.value + '删除'; 链接设置阻止跳转 href="javascript:;"
  2. 循环给 li 添加点击事件 , 处理程序是 删除的是当前链接的父亲 li

04-WebApl_第29张图片

    <script>
      // 1. 获取元素
      var btn = document.querySelector("button");
      var text = document.querySelector("textarea");
      var ul = document.querySelector("ul");
      // 2.绑定事件
      btn.onclick = function () {
        //  if (!text.value)
        if (text.value == "") {
          // 判断条件
          alert("没有输入内容");
          return false;
        } else {
          // (1)创建元素
          var li = document.createElement("li");
          // 先有li 才能赋值
          li.innerHTML = text.value + '删除';
          // ul.appendChild(li); // 添加到父元素孩子后面
          // (2)添加元素
          ul.insertBefore(li, ul.children[0]); // 添加到第一个子元素前边

          // (3)删除元素 删除的是当前链接的 li 它的父亲
          var as = document.querySelectorAll("a");
          for (var i = 0; i < as.length; i++) {
            as[i].onclick = function () {
              // node.removechild(child)
              // 删除的是当前链接的父亲 li
              ul.removeChild(this.parentNode);
              // 删除父节点下面的子节点 (子节点是当前按钮的父亲)
            };
          }
          
        }
      };
    </script>
04-WebApl_第30张图片

复制节点

node.cloneNode()
node.cloneNode() 方法返回调用该方法的节点的一个副本。

注意:

  1. 如果括号参数为空或者为 false ,则是浅拷贝,即只克隆复制节点本身,不克隆里面的子节点。
  2. 如果括号参数为 true ,则是深拷贝,会复制节点本身以及里面所有的子节点

案例 - 动态生成表格★

案例分析

① 因为里面的学生数据都是动态的,我们需要js 动态生成。 这里我们模拟数据,自己定义好数据。 数据我们采取对象形式存储。
② 所有的数据都是放到tbody里面的行里面。
③ 因为行很多,我们需要循环创建多个行(对应多少人)
④ 每个行里面又有很多单元格(对应里面的数据),我们还继续使用循环创建多个单元格,并且把数据存入里面(双重for循环)
⑤ 最后一列单元格是删除,需要单独创建单元格。
⑥ 最后添加删除操作,单击删除,可以删除当前行。

知识点

  1. 用双重 for 循环 : 外循环添加行 , 内循环添加列
  2. 创建行 var tr = document.createElement("tr");
  3. 把行插到 tbody 中 tbody.appendChild(tr);
  4. 用 for in 遍历数组里的对象
  5. 创建单元格 var td = document.createElement("td");
  6. 把单元格添加到当前行里面 列 tr.appendChild(td);
  7. 把对象里面的属性值去过来给 td td.innerHTML = datas[i][k];
  8. 循环创建有删除的单元格 var td = document.createElement("td");
  9. 给当前单元格添加删除链接 td.innerHTML = '删除';
  10. 创建到当前行后面 tr.appendChild(td);
  11. 先遍历所有链接在删除当前点击行 tbody.removeChild(this.parentNode.parentNode); *点击a 删除a 所在的行 链接的爸爸的爸爸
    <script>
      // 1.先去准备好学生的数据
      var datas = [
        {
          name: "魏璎珞",
          subject: "JavaScript",
          score: 100,
        },
        {
          name: "弘历",
          subject: "JavaScript",
          score: 98,
        },
        {
          name: "傅恒",
          subject: "JavaScript",
          score: 99,
        },
        {
          name: "明玉",
          subject: "JavaScript",
          score: 88,
        },
        {
          name: "大猪蹄子",
          subject: "JavaScript",
          score: 0,
        },
      ];
      // 2. 往tbody 里面创建行: 有几个人(通过数组的长度)我们就创建几行
      var tbody = document.querySelector("tbody");
      for (var i = 0; i < datas.length; i++) {
        // 外面的 for 循环控制行
        // 1.创建 tr 行
        var tr = document.createElement("tr");
        // 把行插入到 tbody 中
        tbody.appendChild(tr);
        // 行里面创建单元格 td 单元格的数量取决于每个对象属性的个数
        for (var k in datas[i]) {
          // datas 数组里面的第i个对象
          // 2.创建单元格 (跟数据有关系的3个单元格)
          var td = document.createElement("td");
          // 把单元格添加到当前行里面 列
          // 把对象里面的属性的值 给 td 单元格
          td.innerHTML = datas[i][k];
          tr.appendChild(td);
        } // 里面的for 循环控制列
        // 3. 循环创建有删除的单元格 ,
        var td = document.createElement("td");
        td.innerHTML = '删除';
        // 创建到当前行后面
        tr.appendChild(td);
      }
      // 4删除操作
      var as = document.querySelectorAll("a");
      for (var i = 0; i < as.length; i++) {
        as[i].onclick = function () {
          // 点击a 删除a 所在的行 链接的爸爸的爸爸
          tbody.removeChild(this.parentNode.parentNode);
        };
      }

      // for(var k in obj) {
      //     k 得到的是属性名
      //     obj[k] 得到是属性值
      // }
    </script>
04-WebApl_第31张图片

三种动态创建元素区别

  • document.write()
  • element.innerHTML
  • document.createElement()

区别

  1. document.write 是直接将内容写入页面的内容流,但是文档流执行完毕,则它会导致页面全部重绘 (把之前的页面清空)
  2. innerHTML 是将内容写入某个 DOM 节点,不会导致页面全部重绘
  3. innerHTML 创建多个元素效率更高(不要拼接字符串,采取数组形式拼接),结构稍微复杂
  4. createElement() 创建多个元素效率稍低一点点,但是结构更清晰
    总结:不同浏览器下,innerHTML 效率要比 creatElement 高

innerTHML和createElement效率对比

innerHTML字符串拼接方式(效率低)

<script>
    function fn() {
        var d1 = +new Date();
        var str = '';
        for (var i = 0; i < 1000; i++) {
            document.body.innerHTML += '
'
; } var d2 = +new Date(); console.log(d2 - d1); } fn(); </script>

createElement方式(效率一般)

<script>
    function fn() {
        var d1 = +new Date();

        for (var i = 0; i < 1000; i++) {
            var div = document.createElement('div');
            div.style.width = '100px';
            div.style.height = '2px';
            div.style.border = '1px solid red';
            document.body.appendChild(div);
        }
        var d2 = +new Date();
        console.log(d2 - d1);
    }
    fn();
</script>

innerHTML数组方式(效率高)⭐️⭐️

<script>
    function fn() {
        var d1 = +new Date();
        var array = [];
        for (var i = 0; i < 1000; i++) {
            array.push('
'
); } document.body.innerHTML = array.join(''); var d2 = +new Date(); console.log(d2 - d1); } fn(); </script>

DOM重点核心★

关于dom操作,我们主要针对于元素的操作。主要有创建属性操作事件操作

创建

  1. document.write
  2. innerHTML
  3. createElement

  1. appendChild // node.appendChild ( child )
  2. insertBefore // node.inserBefore ( child , 指定元素 )

  1. removeChild // node.removeChild ( child )

删除节点的父元素 ,亲父子关系

主要修改dom的元素属性,dom元素的内容、属性, 表单的值等

  1. 修改元素属性: src、href、title等 // img. src=xxx
  2. 修改普通元素内容: innerHTML 、innerText // innerHTML=xxx
  3. 修改表单元素: value、type、disabled等 // input . value = “你好”
  4. 修改元素样式: style、className // div . style .color = “red” div .className =“current”

主要获取查询 dom 的元素

  1. DOM提供的API 方法: getElementById、getElementsByTagName 古老用法 不太推荐
  2. H5提供的新方法: querySelector、querySelectorAll 提倡
  3. 利用节点操作获取元素: 父(parentNode)、子(children)、兄(previousElementSibling、nextElementSibling) 提倡

属性操作

主要针对于自定义属性

  1. setAttribute:设置dom的属性值
  2. getAttribute:得到dom的属性值
  3. removeAttribute移除属性

事件操作

给元素注册事件, 采取 事件源.事件类型 = 事件处理程序

04-WebApl_第32张图片

事件高级

04-WebApl_第33张图片04-WebApl_第34张图片

绑定事件的两种方式

注册事件有两种方式:传统方式方法监听注册方式

  • 传统注册方式

    • 利用 on 开头的事件 onclick
    • btn.onclick = function() {}
    • 特点: 注册事件的唯一性
    • 同一个元素同一个事件只能设置一个处理函数,最后注册的处理函数将会覆盖前面注册的处理函数
  • 方法监听注册方式

    • w3c 标准 推荐方式
    • addEventListener() 它是一个方法
    • E9 之前的 IE 不支持此方法,可使用 attachEvent() 代替
    • 特点:同一个元素同一个事件可以注册多个监听器
    • 按注册顺序依次执行

事件监听

addEventListener()事件监听(IE9以后支持)

该方法接收三个参数 :

  • type : 事件字符串 , 如 click mouseover , 注意不带 on
  • listener : 事件处理函数 , 事件发生时 , 会调用该监听函数
  • useCapture : 可选参数 , 是一个布尔值 , 默认是 false . 学完DOM事件流再进一步学习
      btns[0].addEventListener("click", function () {
        console.log(11);
      });
attacheEvent()事件监听(IE678支持)(了解)
  • eventNameWithOn:事件类型字符串,比如 onclick 、onmouseover 这里要带 on
  • callback: 事件处理函数,当目标触发事件时回调函数被调用
      btns[2].attachEvent("onclick",function(){
        console.log(123);
      })
事件监听兼容性解决方案 (了解)

封装一个函数,函数中判断浏览器的类型:

function addEventListener(element, eventName, fn) {
 // 判断当前浏览器是否支持 addEventListener 方法
 if (element.addEventListener) {
 element.addEventListener(eventName, fn); // 第三个参数 默认是false
 } else if (element.attachEvent) {
 element.attachEvent('on' + eventName, fn);
 } else {
 // 相当于 element.onclick = fn; 传统方式
 element['on' + eventName] = fn;
} 

删除事件(解绑事件)

1. 传统注册方式

eventTarget.onclick = null;

      divs[0].onclick = function () {
        alert("11");
        divs[0].onclick = null; // 删除事件
      };

2.方法监听注册方式

① eventTarget.removeEventListener(type, listener[, useCapture]);
② eventTarget.detachEvent(eventNameWithOn, callback);

        // 删除事件 removeEventListener
      divs[1].addEventListener("click", fn);
      function fn() {
        divs[1].removeEventListener("click", fn);
      }

      div2[2].attachEvent("onclick", fn1);
      function fn1() {
        alert(44);
        // 删除事件 detachEvent
        div[2].detachEvent("onclick", fn1);
      }

**删除事件兼容性解决方案 **(了解)

function removeEventListener(element, eventName, fn) {
 // 判断当前浏览器是否支持 removeEventListener 方法
 if (element.removeEventListener) {
 element.removeEventListener(eventName, fn); // 第三个参数 默认是false
 } else if (element.detachEvent) {
 element.detachEvent('on' + eventName, fn);
 } else {
 element['on' + eventName] = null;
} 

DOM事件流

基于 DOM 树型结构触发事件的顺序 , 称为事件流

事件发生时会在元素节点之间按照特定的顺序传播 , 这个传播过程DOM 事件流

DOM 事件流会经历3个阶段:

  1. 捕获阶段 (从上往下 , 从大到小)
    • Document 接受到点击事件 , 没有绑定任何事件就会向下传播
    • HTML 接受到点击事件 , 没有绑定任何事件就会向下传
    • body 接受到点击事件 , 没有绑定任何事件就会向下传
    • div 接受到点击事件
  2. 当前目标阶段
    • div 接受到点击事件去执行
  3. 冒泡阶段 (下上往上 , 从小到大)
    • div 执行完又会往上
    • body 又接受到了点击事件 , 没有绑定任何事件就会向上传
    • HTML 又接受到了点击事件 , 没有绑定任何事件就会向上传
    • Document 接受到点击事件 , 没有绑定任何事件就结束了
04-WebApl_第35张图片
  • 事件冒泡 : IE 最早提出 , 事件开始时由最具体的元素接受 , 然后逐渐向上传播到 DOM 最顶层节点的过程
  • 事件捕获 : 网景最早提出 , 由 DOM 最顶层节点开始 , 然后逐级向下传播到最具体的元素接受的过程
04-WebApl_第36张图片

注意

  1. JS 代码中只能执行捕获或者冒泡其中的一个阶段。
  2. onclick 和 attachEvent 只能得到冒泡阶段。
  3. addEventListener(type, listener[, useCapture])第三个参数如果是 true 表示在事件捕获阶段调用事件处理程序;如果是 false(不写默认就是false),表示在事件冒泡阶段调用事件处理程序。
  4. 实际开发中我们很少使用事件捕获,我们更关注事件冒泡。
  5. 有些事件是没有冒泡的,比如 onblur、onfocus、onmouseenter、onmouseleave
  6. 事件捕获和冒泡同时存在先执行 捕获

代码演示

事件冒泡

    <div class="father">
        <div class="son">son盒子</div>
    </div>
    <script>
        // onclick 和 attachEvent(ie) 在冒泡阶段触发
        // 冒泡阶段 如果addEventListener 第三个参数是 false 或者 省略 
        // son -> father ->body -> html -> document
        var son = document.querySelector('.son');
		// 给son注册单击事件
        son.addEventListener('click', function() {
            alert('son');
        }, false);
		// 给father注册单击事件
        var father = document.querySelector('.father');
        father.addEventListener('click', function() {
            alert('father');
        }, false);
		// 给document注册单击事件,省略第3个参数
        document.addEventListener('click', function() {
            alert('document');
        })
    </script>

事件捕获

    <div class="father">
        <div class="son">son盒子</div>
    </div>
    <script>
        // 如果addEventListener() 第三个参数是 true 那么在捕获阶段触发
        // document -> html -> body -> father -> son
         var son = document.querySelector('.son');
		// 给son注册单击事件,第3个参数为true
         son.addEventListener('click', function() {
             alert('son');
         }, true);
         var father = document.querySelector('.father');
		// 给father注册单击事件,第3个参数为true
         father.addEventListener('click', function() {
             alert('father');
         }, true);
		// 给document注册单击事件,第3个参数为true
        document.addEventListener('click', function() {
            alert('document');
        }, true)
    </script>

04-WebApl_第37张图片

事件对象

什么是事件对象

事件发生后,跟事件相关的一系列信息数据的集合都放到这个对象里面,这个对象就是事件对象。

事件对象只有有了事件才会存在 , 它是系统给我们自动创建的 , 不需要我们传递参数

比如:

  1. 谁绑定了这个事件。
  2. 鼠标触发事件的话,会得到鼠标的相关信息,如鼠标位置。
  3. 键盘触发事件的话,会得到键盘的相关信息,如按了哪个键。
事件对象的使用

事件触发发生时就会产生事件对象 (even),并且系统会以实参的形式传给事件处理函数。

所以,在事件处理函数中声明1个形参用来接收事件对象。

      div.onclick = function (e) {
        // 这个 event 就是事件对象
      };
      div.addEventListener("click", function (event) {
        // 这个 event 就是事件对象
      });

这个 event 是个形参,系统帮我们设定为事件对象,不需要传递实参过去。
当我们事件时, event 对象就会被系统自动创建,并依次传递给事件监听器(事件处理函数)。

事件对象的兼容性处理

事件对象本身的获取存在兼容问题:

  1. 标准浏览器中是浏览器给方法传递的参数,只需要定义形参 e 就可以获取到。
  2. 在 IE6~8 中,浏览器不会给方法传递参数,如果需要的话,需要到 window.event 中获取查找。

解决:
e = e || window.event;

只要“||”前面为false, 不管“||”后面是true 还是 false,都返回 “||” 后面的值。
只要“||”前面为true, 不管“||”后面是true 还是 false,都返回 “||” 前面的值。
    <div>123</div>
    <script>
        var div = document.querySelector('div');
        div.onclick = function(e) {
                // 事件对象
                e = e || window.event;
                console.log(e);
        }
    </script>
事件对象的属性和方法
04-WebApl_第38张图片
e.target 和 this 的区别
  • this 返回的是绑定事件的对象 ( 元素 )
  • e.target 返回的是触发事件的对象 ( 元素 )
常情况下terget 和 this是一致的,
但有一种情况不同,那就是在事件冒泡时(父子元素有相同事件,单击子元素,父元素的事件处理函数也会被触发执行),
	这时候this指向的是父元素,因为它是绑定事件的元素对象,
	而target指向的是子元素,因为他是触发事件的那个具体元素对象。

事件冒泡下的e.target和this

      // 区别: e.target 返回的是触发事件的对象 , this 返回的是绑定事件的对象
      var ul = document.querySelector("ul");
      ul.addEventListener("click", function (e) {
        console.log(this); // ul this指向绑定事件的元素 ul
        console.log(e.target); // li e.target 指向触发事件的的那个对象 li
      });

阻止默认行为

e.defaultPrevented()

注意 : 方法要加小括号

html中一些标签有默认行为,例如a标签被单击后,默认会进行页面跳转。

    <a href="http://www.baidu.com">百度</a>
    <script>
        // 2. 阻止默认行为 让链接不跳转 
        var a = document.querySelector('a');
        a.addEventListener('click', function(e) {
             e.preventDefault(); //  dom 标准写法
        });
        // 3. 传统的注册方式
        a.onclick = function(e) {
            // 普通浏览器 e.preventDefault();  方法
            e.preventDefault();
            // 低版本浏览器 ie678  returnValue  属性
            e.returnValue = false;
            // 我们可以利用return false 也能阻止默认行为 没有兼容性问题
            return false;
        }
    </script>
      // 传统方法
      a.onclick = function () {
        return false;
      };

阻止事件冒泡

事件冒泡

  • 解释方式1:基于DOM树结构,事件从下往上传播
  • 解释方式2:父标签中套子标签,并且父标签和子标签绑定相同的事件,子标签触发事件,父标签也会触发事件

e.stopPropagation();

  • 标准写法:利用事件对象里面的 stopPropagation()方法

e.cancelBubble = true;

  • 非标准写法:IE 6-8 利用事件对象 cancelBubble 属性

注意 : 方法要加小括号

      son.addEventListener("click",function (e) {
          alert("son");
          e.stopPropagation(); // 阻止冒泡
          e.cancelBubble=true; // 非标准
        },false
      );

阻止事件冒泡的兼容性处理

if(e && e.stopPropagation){
 e.stopPropagation();
}else{
 window.event.cancelBubble = true;
}

事件委托

什么是事件委托

事件委托也称为事件代理,在 jQuery 里面称为事件委派。

把本该绑定到当前元素的事件,绑定给他的父元素

说白了就是,不给子元素注册事件,给父元素注册事件,把处理代码在父元素的事件中执行。

事件委托的原理

不是每个子节点单独设置事件监听器,而是事件监听器设置在其父节点上,然后利用冒泡原理影响设置每个子节点。

给父元素注册事件,利用事件冒泡,当子元素的事件触发,会冒泡到父元素,然后去控制相应的子元素。

事件委托的作用
  • 我们只操作了一次 DOM ,提高了程序的性能。

  • 可以动态创建新的子元素绑定事件

    <ul>
        <li>知否知否,点我应有弹框在手!</li>
        <li>知否知否,点我应有弹框在手!</li>
        <li>知否知否,点我应有弹框在手!</li>
        <li>知否知否,点我应有弹框在手!</li>
        <li>知否知否,点我应有弹框在手!</li>
    </ul>
    <script>
        // 事件委托的核心原理:给父节点添加侦听器, 利用事件冒泡影响每一个子节点
        // 原理 点击li,因为li有事件冒泡 .li传给了ul , ul有点击事件
        var ul = document.querySelector('ul');
        ul.addEventListener('click', function(e) {
            // e.target 这个可以得到我们点击的对象
            e.target.style.backgroundColor = 'pink';
        })
    </script>

案例 - 动态添加子元素

知识点

  1. 给按钮绑定监听事件 .addEventListener
  2. 处理程序 : 点击按钮后创建一个 li document.createElement("li");
  3. 把输入框的值赋值给li innerHTML value
  4. 判断一下输入框的值是否为空 li.innerHTML.length == 0
  5. 把创建好的 li 插入到父元素的子元素中
    <script>
      // 给按钮添加事件监听
      document.querySelector("button").addEventListener("click", function () {
        // 创建一个 li
        var li = document.createElement("li");
        // 把输入框的值赋值给 li
        li.innerHTML = document.querySelector("input").value;
        if (li.innerHTML.length == 0) {
          alert("请输入有效内容");
        } else {
          // 把 li 插入到 ul 的子元素中
          document.querySelector("ul").appendChild(li);
        }
      });
    </script>
04-WebApl_第39张图片

常用鼠标事件

04-WebApl_第40张图片
禁止选中文字和禁止右键菜单

1.禁止鼠标右键菜单
contextmenu主要控制应该何时显示上下文菜单,主要用于程序员取消默认的上下文菜单

    document.addEventListener("contextmenu", function (e) {
      e.preventDefault();
    });

2.禁止鼠标选中(selectstart 开始选中)

    document.addEventListener("selectstart", function (e) {
      e.preventDefault();
    });
鼠标事件对象

event对象代表事件的状态,跟事件相关的一系列信息的集合。现阶段我们主要是用鼠标事件对象
MouseEvent 和键盘事件对象 KeyboardEvent。

04-WebApl_第41张图片
    <script>
        // 鼠标事件对象 MouseEvent
        document.addEventListener('click', function(e) {
            // 1. client 鼠标在可视区的x和y坐标
            console.log(e.clientX);
            console.log(e.clientY);
            console.log('---------------------');

            // 2. page 鼠标在页面文档的x和y坐标
            console.log(e.pageX);
            console.log(e.pageY);
            console.log('---------------------');

            // 3. screen 鼠标在电脑屏幕的x和y坐标
            console.log(e.screenX);
            console.log(e.screenY);
        })
    </script>
案例 - 跟随鼠标的小天使

案例分析

① 鼠标不断的移动,使用鼠标移动事件: mousemove
② 在页面中移动,给document注册事件
③ 图片要移动距离,而且不占位置,我们使用绝对定位即可
④ 核心原理: 每次鼠标移动,我们都会获得最新的鼠标坐标, 把这个x和y坐标做为图片的top和left 值就可以移动图片

知识点

  1. 给文档绑定监听事件 , 鼠标移动 mouseover
  2. 获取鼠标相对于文档 x , y 的坐标 e.pageX
  3. 把坐标值赋值给图片定位的数值 , 记得拼接单位 pic.style.left = x +"px"
  <body>
    <img src="images/angel.gif" alt="" />
    <script>
      varlocation pic = document.querySelector("img");
      document.addEventListener("mousemove", function (e) {
        // 1. mousemove只要我们鼠标移动1px 就会触发这个事件
        // 2.核心原理: 每次鼠标移动,我们都会获得最新的鼠标坐标, 把这个x和y坐标做为图片的top和left 值就可以移动图片
        var x = e.pageX;
        var y = e.pageY;
        console.log("x坐标是" + x, "y坐标是" + y);
        //3 . 千万不要忘记给left 和top 添加px 单位
        pic.style.left = x - 50 + "px"; // 减去图片大小的一半就可以居中
        pic.style.top = y - 40 + "px";
      });
    </script>
  </body>

常用键盘事件

事件除了使用鼠标触发,还可以使用键盘触发, 注意给文档 document 添加键盘事件

键盘事件 触发条件
onkeyup 某个键盘按键被松开时触发
onkeydown 某个按键被按下时触发 , 不区分大小写 . 只有大写
onkeypress 某个按键被按下时触发 但它不识别功能键 如 shift window 箭头键

注意: onkeypress 和前面2个的区别是,它不识别功能键,比如左右箭头,shift 等。

三个事件的执行顺序 keydownkeypresskeyup

    <script>
        // 常用的键盘事件
        //1. keyup 按键弹起的时候触发 
        document.addEventListener('keyup', function() {
            console.log('我弹起了');
        })

        //3. keypress 按键按下的时候触发  不能识别功能键 比如 ctrl shift 左右箭头啊
        document.addEventListener('keypress', function() {
                console.log('我按下了press');
        })
        //2. keydown 按键按下的时候触发  能识别功能键 比如 ctrl shift 左右箭头啊
        document.addEventListener('keydown', function() {
                console.log('我按下了down');
        })
        // 4. 三个事件的执行顺序  keydown -- keypress -- keyup
    </script>
键盘事件对象属性 说明
keyCode 返回该键的ASCII值

注意:

  • onkeydown 和 onkeyup 不区分字母大小写, 默认是大 写,onkeypress 区分字母大小写。
  • 在我们实际开发中,我们更多的使用keydown和keyup, 它能识别所有的键(包括功能键)
  • Keypress 不识别功能键,但是keyCode属性能区分大小写,返回不同的ASCII值
案例 - 模拟京东按键输入内容

案例分析

① 核心思路: 检测用户是否按下了s 键,如果按下s 键,就把光标定位到搜索框里面
② 使用键盘事件对象里面的keyCode 判断用户按下的是否是s键
③ 搜索框获得焦点: 使用 js 里面的 focus() 方法

知识点

  1. 获取输入框 , 给文档绑定键盘事件 keyup
  2. if 判断一下按下去的键是不是 s if (e.keyCode === 83)
  3. 获得搜索框焦点 search.focus();
    <script>
      var search = document.querySelector("input");
      // 用keyup 当键盘弹起后在进行判断 , 这样就不会把字符输入到输入框中了
      document.addEventListener("keyup", function (e) {
        console.log(e.keyCode); // 83
        if (e.keyCode === 83) {
          search.focus();
        }
      });
    </script>
案例 - 京东快递单号查询

案例分析

① 快递单号输入内容时, 上面的大号字体盒子(con)显示(这里面的文字
② 同时把快递单号里面的值(value)获取过来赋值给 con盒子(innerText)做为内容
③ 如果快递单号里面内容为空,则隐藏大号字体盒子(con)盒子
④ 注意: keydown 和 keypress 在文本框里面的特点: 他们两个事件触发的时候,文字还没有落入文本框中。
⑤ keyup事件触发的时候, 文字已经落入文本框里面了
⑥ 当我们失去焦点,就隐藏这个con盒子
⑦ 当我们获得焦点,并且文本框内容不为空,就显示这个con盒子

知识点

  1. keyup
  2. this.value == “”
  3. con.style.display = “none”;
  4. con.innerText = this.value;
  5. “blur”
  6. “focus”
    <script>
      // 快递单号输入内容时, 上面的大号字体盒子(con)显示(这里面的字号更大)
      // 表单检测用户输入: 给表单添加键盘事件
      // 同时把快递单号里面的值(value)获取过来赋值给 con盒子(innerText)做为内容
      // 如果快递单号里面内容为空,则隐藏大号字体盒子(con)盒子
      var con = document.querySelector(".con");
      var jd_input = document.querySelector(".jd");
      jd_input.addEventListener("keyup", function () {
        // console.log('输入内容啦');
        if (this.value == "") {
          con.style.display = "none";
        } else {
          con.style.display = "block";
          con.innerText = this.value;
        }
      });
      // 当我们失去焦点,就隐藏这个con盒子
      jd_input.addEventListener("blur", function () {
        con.style.display = "none";
      });
      // 当我们获得焦点,就显示这个con盒子
      jd_input.addEventListener("focus", function () {
        if (this.value !== "") {
          con.style.display = "block";
        }
      });
    </script>

BOM★

04-WebApl_第42张图片 04-WebApl_第43张图片

什么是BOM

​ BOM(Browser Object Model)即浏览器对象模型,它提供了独立于内容而与浏览器窗口进行交互的对象,其核心对象是 window。

​ BOM 由一系列相关的对象构成,并且每个对象都提供了很多方法与属性

​ BOM 缺乏标准,JavaScript 语法的标准化组织是 ECMA,DOM 的标准化组织是 W3C,BOM 最初是Netscape 浏览器标准的一部分。

04-WebApl_第44张图片

BOM的构成

04-WebApl_第45张图片

顶级对象window

window 对象是浏览器的顶级对象,它具有双重角色。

  1. 它是 JS 访问浏览器窗口的一个接口

  2. 它是一个全局对象。定义在全局作用域中的变量函数都会变成 window 对象的属性方法

在调用的时候可以省略 window,前面学习的对话框都属于 window 对象方法,如 alert()、prompt() 等。
注意:window下的一个特殊属性 window.name

this 指向 window 打印name 输出是 this.name=""

在全局对象中 this 指向window

window对象的常见事件

页面(窗口)加载事件(2种)

onload

window.onload = function () {};
或者
window.addEventListener("load", function () {});

window.onload 是窗口 (页面)加载事件,当文档内容完全加载完成会触发该事件(包括图像、脚本文件、CSS 文件等), 就调用的处理函数。

注意

  1. 有了 window.onload 就可以把 JS 代码写到页面元素的上方,因为 onload 是等页面内容全部加载完毕,再去执行处理函数。
  2. window.onload 传统注册事件方式 只能写一次,如果有多个,会以最后一个 window.onload 为准。
  3. 如果使用 addEventListener 则没有限制 , 同一个元素同一个事件可以注册多个监听器

DOMContentLoaded

document.addEventListener("DOMContentLoaded", function () {
    alert(33);
  });

DOMContentLoaded 是 DOM 加载完毕 (标签) , 不包含图片 flash css 等就可以执行 , 加载速度的比 onload 要快一些

load 等页面内容全部加载完毕 , 包含页面 dom 元素 图片 flash css 等

页面加载顺序

  1. 先发送请求 http://www.baidu.com
  2. 服务器把HTML返回给浏览器
  3. 浏览器接收到HTML后要解析成DOM树 (DOM加载完毕触发DOMContentLoad事件 , 给document绑定)
  4. 如果遇到图片就会发请求请求这些数据 (页面的第三方资源处理完毕后触发 load事件 , 给window绑定)
    <script>
        window.addEventListener('load', function() {
            var btn = document.querySelector('button');
            btn.addEventListener('click', function() {
                alert('点击我');
            })
        })
        window.addEventListener('load', function() {
            alert(22);
        })
        document.addEventListener('DOMContentLoaded', function() {
            alert(33);
        })
    </script>
调整窗口大小事件
window.onresize=function(){}
window.addEventListener("resize", function () {})

​ window.onresize 是调整窗口大小加载事件, 当触发时就调用的处理函数。

        window.addEventListener("resize", function () {
          if (window.innerWidth <= 800) {
            div.style.display = "none";
          } else {
            div.style.display = "block";
          }
        });

注意:

  1. 只要窗口大小发生像素变化,就会触发这个事件。

  2. 我们经常利用这个事件完成响应式布局。 window.innerWidth 当前屏幕的宽度

定时器(两种)

window 对象给我们提供了 2 个非常好用的方法-定时器。

  • setTimeout()

  • setInterval()

setTimeout() 炸弹定时器

开启定时器

window.setTimeout(调用函数, [延迟的毫秒数]);

      // 1.直接写函数
      setTimeout(function () {
        console.log("时间到了");
      }, 2000);
      // 2.写函数名
      function callback() {
        console.log("爆炸了");
      }
      var timer1 = setTimeout(callback, 3000);
      var timer2 = setTimeout(callback, 5000);

setTimeout() 方法用于设置一个定时器,该定时器在定时器到期后执行调用函数。

注意:

  1. window 可以省略。
  2. 这个调用函数可以直接写函数,或者写函数名或者采取字符串‘函数名()'三种形式。第三种不推荐
  3. 延迟的毫秒数省略默认是 0,如果写,必须是毫秒。
  4. 因为定时器可能有很多,所以我们经常给定时器赋值一个标识符

setTimeout() 这个调用函数我们也称为回调函数 callback普通函数是按照代码顺序直接调用。而这个函数,需要等待时间,时间到了才去调用这个函数,因此称为回调函数
简单理解: 回调,就是回头调用的意思。上一件事干完,再回头再调用这个函数。
以前我们讲的 element.onclick = function(){} 或者 element.addEventListener(“click”, fn); 里面的 函数也是回调
函数。

案例 - 5秒钟关闭广告

案例分析

  1. 核心思路 : 5秒钟之后 , 就把这个广告隐藏起来
  2. 用定时器 setTimeout
    <img src="./images/1.jpg" alt="" class="ad" />
    <script>
      var ad = document.querySelector(".ad");
      setTimeout(function () {
        ad.style.display = "none";
      }, 5000);
停止定时器 clearTimeout

window.clearTimeout(timeoutID)

clearTimeout()方法取消了先前通过调用 setTimeout() 建立的定时器。

      btn.addEventListener("click", function () {
        clearTimeout(timer);
      });
setInterval() 闹钟定时器

开启定时器

window.setInterval(回调函数, [间隔的毫秒数]);

setInterval() 方法重复调用一个函数,每隔这个时间,就去调用一次回调函数。

      setInterval(function () {
        console.log("重复调用");
      }, 2000);

setTimeout() 和 setInterval()区别

  • setTimeout 延时时间到了就去调用这个回调函数 , 值调用一次就结束了这个定时器
  • setInterval 每隔这个延时时间就去调用这个回调函数 , 会调用很多次 , 重复调用这个函数
案例 - 京东秒杀倒计时

案列分析

① 这个倒计时是不断变化的,因此需要定时器来自动变化(setInterval)
② 三个黑色盒子里面分别存放时分秒
③ 三个黑色盒子利用innerHTML 放入计算的小时分钟秒数
④ 第一次执行也是间隔毫秒数,因此刚刷新页面会有空白
⑤ 最好采取封装函数的方式, 这样可以先调用一次这个函数,防止刚开始刷新页面有空白问题

    <div>
        <span class="hour">1</span>
        <span class="minute">2</span>
        <span class="second">3</span>
    </div>
    <script>
        // 1. 获取元素(时分秒盒子) 
        var hour = document.querySelector('.hour'); // 小时的黑色盒子
        var minute = document.querySelector('.minute'); // 分钟的黑色盒子
        var second = document.querySelector('.second'); // 秒数的黑色盒子
        var inputTime = +new Date("2021-11-29 00:00:00"); // 返回的是用户输入时间总的毫秒数

        countDown(); // 我们先调用一次这个函数,防止第一次刷新页面有空白 

        // 2. 开启定时器
        setInterval(countDown, 1000);
		
        function countDown() {
            var nowTime = +new Date(); // 返回的是当前时间总的毫秒数
            var times = (inputTime - nowTime) / 1000; // times是剩余时间总的秒数 
            var h = parseInt(times / 60 / 60 % 24); //时
            h = h < 10 ? '0' + h : h;
            hour.innerHTML = h; // 把剩余的小时给 小时黑色盒子
            var m = parseInt(times / 60 % 60); // 分
            m = m < 10 ? '0' + m : m;
            minute.innerHTML = m;
            var s = parseInt(times % 60); // 当前的秒
            s = s < 10 ? '0' + s : s;
            second.innerHTML = s;
        }
    </script>
04-WebApl_第46张图片
停止定时器 setInterval

clearInterval ( intervalID );

clearInterval ()方法取消了先前通过调用 setInterval()建立的定时器。

timer 应设置全局变量这样其他函数才可以访问这个变量 , 变量赋值 null 合适 , 如果不赋值就是 undefined

    <script>
      var begin = document.querySelector(".begin");
      var stop = document.querySelector(".stop");
      var timer = null; // 全局变量 null 是一个空对象 只有设为全局变量 其他函数才能访问这个变量 ,变量赋值 null 合适
      begin.addEventListener("click", function () {
        timer = setInterval(function () {
          console.log("你好吗");
        }, 1000);
      });
      stop.addEventListener("click", function () {
        clearInterval(timer);
      });
    </script>
案例 - 发送短信倒计时

案例分析

① 按钮点击之后,会禁用 disabled 为true
② 同时按钮里面的内容会变化, 注意 button 里面的内容通过 innerHTML修改
③ 里面秒数是有变化的,因此需要用到定时器
④ 定义一个变量,在定时器里面,不断递减
⑤ 如果变量为0 说明到了时间,我们需要停止定时器,并且复原按钮初始状态。

<body>
    手机号码: <input type="number"> <button>发送</button>
    <script>
        var btn = document.querySelector('button');
        var time = 3; // 定义剩下的秒数
        btn.addEventListener('click', function() {
            btn.disabled = true;
            var timer = setInterval(function() {
                if (time == 0) {
                    // 清除定时器和复原按钮
                    clearInterval(timer);
                    btn.disabled = false;
                    btn.innerHTML = '发送';
                } else {
                    btn.innerHTML = '还剩下' + time + '秒';
                    time--;
                }
            }, 1000);

        })
    </script>
</body>
this 指向问题

this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,一般情况下this的最终指向的是那个调用它的对象。

04-WebApl_第47张图片

  1. 全局作用域或者普通函数中this指向全局对象window(注意定时器里面的this指向window)定时器的调用者是window
  2. 字面量定义了方法 , 方法调用中谁调用this指向谁
  3. 绑定事件的时候 , 事件处理函数中的 this 指向事件的绑定者
  4. 构造函数中this 指向创造出来的对象 , 在调用的对象的时候才能知道
    <script>
      // 1.全局作用域中 普通函数中(定时器中this 指向window)
      function a() {
        console.log(this); // this 指向window
      }
      a();
      setTimeout(function () {
        console.log(this); // this 指向window
      }, 1000);
      // 2.对象方法中的 this 表示这个对象
      var person = {
        name: "张三",
        age: 18,
        eat: function (foodName) {
          console.log(this); // this 指向这个对象 person
        },
      };
      person.eat("苞谷");
      // 3. 绑定事件的时候,事件处理函数中的this 表示绑定事件的调用者
      document.querySelector("button").addEventListener("click", function () {
        console.log(this); // this 指向调用者 btn
      });
      // 4.构造函数中 , this 表示创建出来的实例对象 , 在调用对象的时候才知道
      function Cat(name, age) {
        this.name = name;
        this.age = age;
        this.xx = function () {
          console.log(this);
        };
      }
      var c1 = new Cat("黑马", 11); // 表示创建出来的实例对象 C1
      var c2 = new Cat("白马", 11); // 表示创建出来的实例对象 C2
    </script>

04-WebApl_第48张图片

JS 执行机制

同步和异步

JavaScript 语言的一大特点就是单线程,也就是说,同一个时间只能做一件事

console.log(1); 
setTimeout(function () {
 console.log(3); 
}, 1000);
console.log(2); 
// 1
// 2
// 3

利用多核 CPU 的计算能力,HTML5 提出 Web Worker 标准,允许 JavaScript 脚本创建多个线程。于是,JS 中出现了同步异步

  • 同步

前一个任务结束后再执行后一个任务,程序的执行顺序与任务的排列顺序是一致的、同步的。

  • 异步

可以同时进行多个任务 , 上一个任务等待时间较长可以进行下个任务 .

他们的本质区别: 这条流水线上各个流程的执行顺序不同。

console.log(1);
setTimeout(function () {
 console.log(3);
}, 0);
console.log(2);
// 1
// 2
// 3
同步任务和异步任务执行过程

同步任务

同步任务都在主线程上执行,形成一个执行栈
异步任务
JS 的异步是通过回调函数实现的。( 回调函数属于异步任务 )
一般而言,异步任务有以下三种类型:

  • 普通事件,如 click、resize 等
  • 资源加载,如 load、error 等
  • 定时器,包括 setInterval、setTimeout 等

异步任务相关回调函数添加到任务队列中(任务队列也称为消息队列)。

JS 执行机制
  • 先执行执行栈中的同步任务
  • 异步任务(回调函数)放入任务队列中。
  • 一旦执行栈中的所有同步任务执行完毕,系统就会按次序读取任务队列中的异步任务,于是被读取的异步任务结束等待状态,进入执行栈,开始执行。
04-WebApl_第49张图片

JS 把所有任务分为同步任务和异步任务 , 同步任务放到执行栈中 , 回调函数作为异步任务处理放到任务队列中 , 执行机制是先把执行栈中的同步任务全部执行完毕 , 再到任务队列中看有没有异步任务 , 如果有的话就把异步任务放到执行栈中再去执行 , 先执行同步任务再执行异步任务.

04-WebApl_第50张图片
  • 栈相关知识点: 变量和方法进栈 , 栈中的代码执行完毕才去看队列中是否有任务
  • 队列相关知识点 : 先进先出
  • web api 区域: 通过异步调用的函数会放到 web api 区域中
  • loop 循环 : 当栈中的任务执行完毕后会触发轮循 .使用循环一直检查队列中是否有任务 , 如果有任务就放到栈中执行

location 对象

什么是 location 对象

window 对象给我们提供了一个 location 属性用于获取或设置窗体的 URL,并且可以用于解析 URL 。 因为这个属性返回的是一个对象,所以我们将这个属性也称为 location 对象

URL
image-20211129161435684

统一资源定位符 (Uniform Resource Locator, URL) 是互联网上标准资源的地址。互联网上的每个文件都有一个唯一的 URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它。
URL 的一般语法格式为:

protocol://host[:port]/path/[?query]#fragment
http://www.itcast.cn/index.html?name=andy&age=18#link
04-WebApl_第51张图片
location 对象的属性
location 对象属性 返回值
location . href 获取或设置整个 URL
location . host 返回主机域名 www.baidu.com
location . port 返回端口号 如果未返回 空字符串
location . pathname 返回路径
location . search 返回参数 ? 问号后面的参数
location . hash 返回片段 #后面内容常见于锚点链接

重点记住: hrefsearch

案例-5秒钟后跳转页面

案例分析

① 利用定时器做倒计时效果
② 时间到了,就跳转页面。 使用 location.href
③封装函数 , 定时器开启之前调用一次 , 避免出现空白

    <button>点击</button>
    <div></div>
    <script>
      var btn = document.querySelector("button");
      var div = document.querySelector("div");
      btn.addEventListener("click", function () {
        location.href = "http://www.mi.com";
          // 点击跳转页面
      });
      var timer = 5;
      countDown(); // 先调用一次这个函数,防止第一次刷新页面有空白
      setInterval(countDown, 1000); // 开启定时器
      function countDown() { // 封装函数
        if (timer == 0) {
          location.href = "http://www.mi.com";
        } else {
          div.innerHTML = "您将在" + timer + "秒后返回主页";
          timer--;
        }
      }
    </script>
  </body>
04-WebApl_第52张图片
案例-获取 URL 参数数据

案例分析

① 第一个登录页面,里面有提交表单, action 提交到 index.html页面
② 第二个页面,可以使用第一个页面的参数,这样实现了一个数据不同页面之间的传递效果
③ 第二个页面之所以可以使用第一个页面的数据,是利用了URL 里面的location.search参数
④ 在第二个页面中,需要把这个参数提取。
⑤ 第一步去掉? 利用 substr
⑥ 第二步 利用=号分割 键 和 值 split(‘=‘)
⑦ 第一个数组就是键 第二个数组就是值

    <script>
      console.log(location.search); // ?uname=slf
      // 1. 先去掉? substr(起始位置, 截取几个字符,默认所有)
      var params = location.search.substr(1); // uname=slf
      // 2. 利用等号把字符串分割成数组 split split("=")
      var arr = params.split("="); //  ['uname', 'slf']
      // 把数据写入 div 中
      var div = document.querySelector("div");
      div.innerHTML = arr[1] + "欢迎您";
    </script>
04-WebApl_第53张图片
location 对象的方法
location 对象方法 返回值
location . assign() 跟 href 一样 ,可以跳转页面 (也称为重定向页面)
location . replace() 替换当前页面 , 因为不记录历史 , 所以不能后退页面
location . reload() 重新加载页面 , 相当于刷新按钮或者 f5 如果参数为true 强制刷新 ctrl + f5
    <script>
      var btn = document.querySelector("button");
      btn.addEventListener("click", function () {
        location.assign("http://www.mi.com"); // 记录浏览历史 , 可以后退
        location.replace("http://www.mi.com"); // 不记录浏览历史 , 不可以后退
        location.reload(); // 重新刷新页面
      });
    </script>

navigator 对象 (了解)

navigator 对象包含有关浏览器的信息,它有很多属性,我们最常用的是 userAgent,该属性可以返回由客户机发送服务器的 user-agent 头部的值。
下面前端代码可以判断用户那个终端打开页面,实现跳转

if((navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|
Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS
|Symbian|Windows Phone)/i))) {
 window.location.href = ""; //手机
} else {
 window.location.href = ""; //电脑
}

history 对象

window 对象给我们提供了一个 history 对象,与浏览器历史记录进行交互。该对象包含用户(在浏览器窗口中)访问过的 URL。

history 对象方法 作用
back() 后退
forward () 前进
go(参数) 前进后退功能 , 1前进一个页面,-1后退一个页面

一般在一些 OA 办公系统中会遇到

    <script>
      var btn = document.querySelector("button");
      btn.addEventListener("click", function () {
        history.back();
        history.forward();
      });
    </script>

PC端网页特效

04-WebApl_第54张图片04-WebApl_第55张图片

元素偏移量 offset 系列

offset 概述

offset 翻译过来就是偏移量, 我们使用 offset系列相关属性可以动态的得到该元素的位置(偏移)、大小等。

  1. 获得元素距离带有定位父元素的位置
  2. 获得元素自身的大小(宽度高度)
  3. 注意:返回的数值都不带单位

不包含 offset带来的偏移量

offset 系列常用属性

04-WebApl_第56张图片
<body>
    <div class="father">
        <div class="son"></div>
    </div>
    <div class="w"></div>
    <script>
        // offset 系列
        var father = document.querySelector('.father');
        var son = document.querySelector('.son');
        // 1.可以得到元素的偏移 位置 返回的不带单位的数值  
        console.log(father.offsetTop);
        console.log(father.offsetLeft);
        // 它以带有定位的父亲为准  如果么有父亲或者父亲没有定位 则以 body 为准
        console.log(son.offsetLeft);
        var w = document.querySelector('.w');
        // 2.可以得到元素的大小 宽度和高度 是包含padding + border + width 
        console.log(w.offsetWidth);
        console.log(w.offsetHeight);
        // 3. 返回带有定位的父亲 否则返回的是body
        console.log(son.offsetParent); // 返回带有定位的父亲 否则返回的是body
        console.log(son.parentNode); // 返回父亲 是最近一级的父亲 亲爸爸 不管父亲有没有定位
    </script>
</body> 

offsetParent 和 parentNode 区别

  • parentNode 返回的是最近一级的父亲
  • offsetParent 返回的是带有定位的父亲 , 否则返回 body

offset 与 style 区别

offset
  • offset 可以得到任意样式表中的样式值

  • offset 系列获得的数值是没有单位

  • offsetWidth 包含padding+border+width

  • offsetWidth 等属性是只读属性,只能获取不能赋值

  • 所以,我们想要获取元素大小位置,用offset更合适

style
  • style 只能得到行内样式表中的样式值

  • style.width 获得的是带有单位的字符串

  • style.width 获得不包含padding和border 的值

  • style.width 是可读写属性,可以获取也可以赋值

  • 所以,我们想要给元素更改值,则需要用style改变

案例 - 获取鼠标在盒子内的坐标

案例分析

① 我们在盒子内点击,想要得到鼠标距离盒子左右的距离。
首先 标(e.pageX, e.pageY)
其次得到盒子在页面中的距离 ( box.offsetLeft, box.offsetTop)
④ 用鼠标距离页面的坐标减去盒子在页面中的距离,得到 鼠标在盒子内的坐标
⑤ 如果想要移动一下鼠标,就要获取最新的坐标,使用鼠标移动事件 mousemove

04-WebApl_第57张图片
    <script>
      var box = document.querySelector(".box");
      box.addEventListener("click", function (e) {
        // console.log("鼠标距离body右边框" + e.pageX);
        // console.log("鼠标距离body上边框" + e.pageY);
        // console.log("盒子距离页面右边距" + box.offsetLeft);
        // console.log("盒子距离页面上边距" + box.offsetTop);
        var x = e.pageX - this.offsetLeft;
        var y = e.pageY - this.offsetTop;
        this.innerHTML = "x坐标是" + x + "y坐标是" + y;
      });
    </script>

案例 - 模态框拖拽

案例分析

弹出框,我们也称为模态框。

返回顶部
③ 在页面中拖拽的原理: 鼠标按下并且移动, 之后松开鼠标
④ 触发事件是鼠标按下 mousedown, 鼠标移动mousemove 鼠标松开 mouseup
拖拽过程: 鼠标移动过程中,获得最新的值赋值给模态框的left和top值, 这样模态框可以跟着鼠标走了
⑥ 鼠标按下触发的事件源是 最上面一行,就是 id 为 title
⑦ 鼠标的坐标 减去 鼠标在盒子内的坐标, 才是模态框真正的位置。
⑧ 鼠标按下,我们要得到鼠标在盒子的坐标。
⑨ 鼠标移动,就让模态框的坐标 设置为 : 鼠标坐标 减去盒子坐标即可,注意移动事件写到按下事件里面。
⑩ 鼠标松开,就停止拖拽,就是可以让鼠标移动事件解除

04-WebApl_第58张图片
    <script>
        // 1. 获取元素
        var login = document.querySelector('.login');
        var mask = document.querySelector('.login-bg');
        var link = document.querySelector('#link');
        var closeBtn = document.querySelector('#closeBtn');
        var title = document.querySelector('#title');
        // 2. 点击弹出层这个链接 link  让mask 和login 显示出来
        link.addEventListener('click', function() {
                mask.style.display = 'block';
                login.style.display = 'block';
            })
            // 3. 点击 closeBtn 就隐藏 mask 和 login 
        closeBtn.addEventListener('click', function() {
                mask.style.display = 'none';
                login.style.display = 'none';
            })
            // 4. 开始拖拽
            // (1) 当我们鼠标按下, 就获得鼠标在盒子内的坐标
        title.addEventListener('mousedown', function(e) {
            var x = e.pageX - login.offsetLeft;
            var y = e.pageY - login.offsetTop;
            // (2) 鼠标移动的时候,把鼠标在页面中的坐标,减去 鼠标在盒子内的坐标就是模态框的left和top值
                document.addEventListener('mousemove', move) // 加给 document 不加给 title 是因为鼠标快速移动出title 框会停止移动 

            function move(e) {
                login.style.left = e.pageX - x + 'px';
                login.style.top = e.pageY - y + 'px';
            }
            // (3) 鼠标弹起,就让鼠标移动事件移除
            document.addEventListener('mouseup', function() {
                document.removeEventListener('mousemove', move);
            })
        })
    </script>

问题 : 绑定鼠标移动和事件移出鼠标弹起的时候为什么要用 document?

案例 - 京东放大镜

案例分析

  1. 黄色的遮挡层跟随鼠标功能。
  2. 把鼠标坐标给遮挡层不合适。因为遮挡层坐标以父盒子为准。
  3. 首先是获得鼠标在盒子的坐标。
  4. 之后把数值给遮挡层做为left 和top值。
  5. 此时用到鼠标移动事件,但是还是在小图片盒子内移动。
  6. 发现,遮挡层位置不对,需要再减去盒子自身高度和宽度的一半。
  7. 遮挡层不能超出小图片盒子范围。
  8. 如果小于零,就把坐标设置为0
  9. 如果大于遮挡层最大的移动距离,就把坐标设置为最大的移动距离
  10. 遮挡层的最大移动距离:小图片盒子宽度 减去 遮挡层盒子宽度
04-WebApl_第59张图片 04-WebApl_第60张图片
window.addEventListener('load', function() {
    var preview_img = document.querySelector('.preview_img');
    var mask = document.querySelector('.mask');
    var big = document.querySelector('.big');
    // 1. 当我们鼠标经过 preview_img 就显示和隐藏 mask 遮挡层 和 big 大盒子
    preview_img.addEventListener('mouseover', function() {
        mask.style.display = 'block';
        big.style.display = 'block';
    })
    preview_img.addEventListener('mouseout', function() {
            mask.style.display = 'none';
            big.style.display = 'none';
        })
        // 2. 鼠标移动的时候,让黄色的盒子跟着鼠标来走
    preview_img.addEventListener('mousemove', function(e) {
        // (1). 先计算出鼠标在盒子内的坐标
        var x = e.pageX - this.offsetLeft;
        var y = e.pageY - this.offsetTop;
        // console.log(x, y);
        // (2) 减去盒子高度 300的一半 是 150 就是我们mask 的最终 left 和top值了
        // (3) 我们mask 移动的距离
        var maskX = x - mask.offsetWidth / 2;
        var maskY = y - mask.offsetHeight / 2;
        // (4) 如果x 坐标小于了0 就让他停在0 的位置
        // 遮挡层的最大移动距离
        var maskMax = preview_img.offsetWidth - mask.offsetWidth;
        if (maskX <= 0) {
            maskX = 0;
        } else if (maskX >= maskMax) {
            maskX = maskMax;
        }
        if (maskY <= 0) {
            maskY = 0;
        } else if (maskY >= maskMax) {
            maskY = maskMax;
        }
        mask.style.left = maskX + 'px';
        mask.style.top = maskY + 'px';
        // 3. 大图片的移动距离 = 遮挡层移动距离 * 大图片最大移动距离 / 遮挡层的最大移动距离
        // 大图
        var bigIMg = document.querySelector('.bigImg');
        // 大图片最大移动距离
        var bigMax = bigIMg.offsetWidth - big.offsetWidth;
        // 大图片的移动距离 X Y
        var bigX = maskX * bigMax / maskMax;
        var bigY = maskY * bigMax / maskMax;
        bigIMg.style.left = -bigX + 'px';
        bigIMg.style.top = -bigY + 'px';

    })

})

元素可视区 client 系列

client概述

client 翻译过来就是客户端,我们使用 client 系列的相关属性来获取元素可视区的相关信息。通过 client系列的相关属性可以动态的得到该元素的边框大小元素大小等。

04-WebApl_第61张图片
      console.log(div.clientWidth); // 200px 不包含边框
      console.log(div.offsetWidth); // 220px 包含边框
      console.log(div.clientTop); // 上边框 10px

立即执行函数

立即执行函数 (function(){})()  或者 (function(){}())

主要作用: 创建一个独立的作用域 , 里面所有变量都是局部变量。 避免了命名冲突问题

        // 1.	立即执行函数: 不需要调用,立马能够自己执行的函数
        function fn() {
            console.log(1);
        }
        fn();
        // 2. 写法 也可以传递参数进来
        // 1.(function() {})()    或者  2. (function(){}());
        (function(a, b) {
            console.log(a + b);
            var num = 10;
        })(1, 2); // 第二个小括号可以看做是调用函数
        (function sum(a, b) {
            console.log(a + b);
            var num = 10; // 局部变量
        }(2, 3));

下面三种情况都会刷新页面都会触发 load 事件

1.a标签的超链接

2.F5或者刷新按钮(强制刷新)

3.前进后退按钮

但是 火狐中,有个特点,有个“往返缓存”,这个缓存中不仅保存着页面数据,还保存了DOM和JavaScript的状态;实际上是将整个页面都保存在了内存里。

所以此时后退按钮不能刷新页面。

pageshow 照顾了火狐浏览器 , 后退也可以重新加载页面 , 不管是否来自缓存

pageshow会在load事件触发后触发;根据事件对象中的persisted来判断是否是缓存中的页面触发的 pageshow 事件

注意这个事件给window添加。

元素滚动 scroll 系列

scroll 概述

scroll 翻译过来就是滚动的,我们使用 scroll 系列的相关属性可以动态的得到该元素的实际大小滚动距离等。

04-WebApl_第62张图片 04-WebApl_第63张图片

页面被卷去的头部

如果浏览器的高(或宽)度不足以显示整个页面时,会自动出现滚动条。当滚动条向下滚动时,页面上面被隐藏掉的高度,我们就称为页面被卷去的头部。滚动条在滚动时会触发 onscroll事件。

scroll

案例 - 仿淘宝固定右侧侧边栏

需求

  1. 原先侧边栏是绝对定位

  2. 当页面滚动到一定位置,侧边栏改为固定定位

  3. 页面继续滚动,会让 返回顶部显示出来

案例分析

① 需要用到页面滚动事件 scroll 因为是页面滚动,所以事件源是 document
② 滚动到某个位置,就是判断页面被卷去的上部值。
页面被卷去的头部:可以通过window.pageYOffset 获得 如果是被卷去的左侧 window.pageXOffset
⑤ 其实这个值 可以通过盒子的 offsetTop 可以得到,如果大于等于这个值,就可以让盒子固定定位了

⑥ 页面被卷上去的距离大于主体部分被卷去的头部 . 就显示出来返回按钮 , 否则就隐藏

04-WebApl_第64张图片 04-WebApl_第65张图片
    <script>
        //1. 获取元素
        var sliderbar = document.querySelector('.slider-bar');
        var banner = document.querySelector('.banner');
        // banner.offestTop 就是被卷去头部的大小 一定要写到滚动的外面
        var bannerTop = banner.offsetTop
            // 当我们侧边栏固定定位之后应该变化的数值
        var sliderbarTop = sliderbar.offsetTop - bannerTop;
        // 获取main 主体元素
        var main = document.querySelector('.main');
        var goBack = document.querySelector('.goBack');
        var mainTop = main.offsetTop;
        // 2. 页面滚动事件 scroll
        document.addEventListener('scroll', function() {
            // window.pageYOffset 页面被卷去的头部
            // console.log(window.pageYOffset);
            // 3 .当我们页面被卷去的头部大于等于了 172 此时 侧边栏就要改为固定定位
            if (window.pageYOffset >= bannerTop) {
                sliderbar.style.position = 'fixed';
                sliderbar.style.top = sliderbarTop + 'px';
            } else {
                sliderbar.style.position = 'absolute';
                sliderbar.style.top = '300px';
            }
            // 4. 当我们页面滚动到main盒子,就显示 goback模块
            if (window.pageYOffset >= mainTop) {
                goBack.style.display = 'block';
            } else {
                goBack.style.display = 'none';
            }

        })
    </script>

页面被卷去的头部兼容性解决方案 (了解)

需要注意的是,页面被卷去的头部,有兼容性问题,因此被卷去的头部通常有如下几种写法:

  1. 声明了 DTD,使用 document.documentElement.scrollTop
  2. 未声明 DTD,使用  document.body.scrollTop
  3. 新方法 window.pageYOffset和 window.pageXOffset,IE9 开始支持
function getScroll() {
    return {
      left: window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft||0,
      top: window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0
    };
 } 
使用的时候  getScroll().left

三大系列总结

从外到内一层的减少

04-WebApl_第66张图片 04-WebApl_第67张图片

他们主要用法:

1.offset系列 经常用于获得元素位置    offsetLeft  offsetTop

2.client经常用于获取元素大小  clientWidth clientHeight

3.scroll 经常用于获取滚动距离 scrollTop  scrollLeft

4.注意页面滚动的距离通过 window.pageXOffset  获得

mouseenter 和mouseover的区别

  • 当鼠标移动到元素上时就会触发mouseenter 事件
  • 类似 mouseover,它们两者之间的差别
  • mouseover 鼠标经过自身盒子会触发,经过子盒子还会触发。mouseenter  只会经过自身盒子触发
  • 之所以这样,就是因为mouseenter不会冒泡
  • mouseenter搭配鼠标离开 mouseleave  同样不会冒泡

mouseover 为什么会触发两次 ?

鼠标经过父盒子触发一次 , 经过子盒子 , 子盒子得到鼠标经过 , 没有绑定事件 , 子盒子会冒泡到父盒子 , 父盒子绑定了事件又会触发一次

04-WebApl_第68张图片 04-WebApl_第69张图片

动画函数封装

动画实现原理

核心原理:通过定时器 setInterval() 不断移动盒子位置。

实现步骤:

  1. 获得盒子当前位置
  2. 让盒子在当前位置加上1个移动距离
  3. 利用定时器不断重复这个操作
  4. 加一个结束定时器的条件
  5. 注意此元素需要添加定位,才能使用element.style.left
    <script>
      // 动画原理
      // 1. 获得盒子当前位置
      // 2. 让盒子在当前位置加上1个移动距离
      // 3. 利用定时器不断重复这个操作
      // 4. 加一个结束定时器的条件
      // 5. 注意此元素需要添加定位, 才能使用element.style.left
      var div = document.querySelector("div");
      var timer = setInterval(function () {
        if (div.offsetLeft >= 400) {
          // 停止动画 本质是停止定时器
         return clearInterval(timer);
        }
        div.style.left = div.offsetLeft + 1 + "px";
      }, 30);
    </script>

动画函数简单封装

注意函数需要传递2个参数 , obj 目标对象 , target 目标移动到的位置

    <script>
      // 简单动画函数封装obj目标对象 target 目标位置
      function animate(obj, target) {
        var timer = setInterval(function () {
          if (obj.offsetLeft >= target) {
            // 停止动画 本质是停止定时器
             return clearInterval(timer);
          }
          obj.style.left = obj.offsetLeft + 1 + "px";
        }, 30);
      }

      var div = document.querySelector("div");
      var span = document.querySelector("span");
      // 调用函数
      animate(div, 300);
      animate(span, 200);
    </script>

动画函数给不同元素记录不同定时器

原理 : 利用给对象添加属性方法来实现赋值的操作 ;

原理 : 利用 JS 是一门动态语言,可以很方便的给当前对象添加属性。

避免了 var 声明变量 , 不用去内存中开辟空间 , 节约了内存资源

通过元素名赋值 , 使每个元素都有各自的定时器

var obj={};
obj.name="andy"

按钮点击越来越快?

连续点击会不断的开启新的定时器 , 所以速度会越来越快 , 解决方案是只让有一个定时器 , 运行之前先清除以前的定时器 , 只保留当前的一个定时器来运行.

clearInterval(obj.timer);

代码演示

 function animate(obj, target) {
            // 当我们不断的点击按钮,这个元素的速度会越来越快,因为开启了太多的定时器
            // 解决方案就是 让我们元素只有一个定时器执行
            // 先清除以前的定时器,只保留当前的一个定时器执行
            clearInterval(obj.timer);
            obj.timer = setInterval(function() {
                if (obj.offsetLeft >= target) {
                    // 停止动画 本质是停止定时器
                   return clearInterval(obj.timer);
                }
                obj.style.left = obj.offsetLeft + 1 + 'px';

            }, 30);
        }

04-WebApl_第70张图片

案例回调函数

      function animate(obj, target, callback) {
        clearInterval(obj.timer); // 调用之前清除定时器
        obj.timer = setInterval(function () {
          if (obj.offsetLeft == target) {
            // if (callback) { // 存在callback 就执行这个函数
            //   callback();
            // }
            callback && callback(); // 逻辑与判断 回调函数
            return clearInterval(obj.timer);
          }
          obj.style.left = obj.offsetLeft + 1 + "px";
        });
      }
      var box = document.querySelector(".box");
      var box2 = document.querySelector(".box2");
      // 页面加载完 box 调用动画函数,并且有回调函数
      animate(box, 100, function () {
        box.style.backgroundColor = "red";
      });
      // 点击按钮 box2 调用动画函数,没有回调函数
      var btn = document.querySelector("button");
      btn.addEventListener("click", function () {
        animate(box2, 300, function () {
          box2.style.backgroundColor = "green";
        });
      });

缓动动画原理

缓动动画就是让元素运动速度有所变化,最常见的是让速度慢慢停下来

思路:

  1. 让盒子每次移动的距离慢慢变小,速度就会慢慢落下来。
  2. 核心算法: (目标值 - 现在的位置)   /  10    做为每次移动的距离步长
  3. 停止的条件是: 让当前盒子位置等于目标位置就停止定时器
  4. 注意步长值写到定时器的里面
  5. 把我们步长值改为整数 不要出现小数的问题

缓动动画多个目标值之间移动

可以让动画函数从 800 移动到 500。

当我们点击按钮时候,判断步长是正值还是负值

​ 1.如果是正值,则步长往大了取整

​ 2.如果是负值,则步长 向小了取整

案例

案例分析

  1. 让盒子每次移动的距离慢慢变小, 速度就会慢慢落下来。

​ 2. 核心算法:(目标值 - 现在的位置) / 10 做为每次移动的距离 步长

​ 3. 停止的条件是: 让当前盒子位置等于目标位置就停止定时器

<script>
        // 缓动动画函数封装obj目标对象 target 目标位置
        // 思路:
        // 1. 让盒子每次移动的距离慢慢变小, 速度就会慢慢落下来。
        // 2. 核心算法:(目标值 - 现在的位置) / 10 做为每次移动的距离 步长
        // 3. 停止的条件是: 让当前盒子位置等于目标位置就停止定时器
        function animate(obj, target) {
            // 先清除以前的定时器,只保留当前的一个定时器执行
            clearInterval(obj.timer);
            obj.timer = setInterval(function() {
                // 步长值写到定时器的里面
                // 把我们步长值改为整数 不要出现小数的问题
                // var step = Math.ceil((target - obj.offsetLeft) / 10);
                var step = (target - obj.offsetLeft) / 10;
                step = step > 0 ? Math.ceil(step) : Math.floor(step);
                if (obj.offsetLeft == target) {
                    // 停止动画 本质是停止定时器
                    clearInterval(obj.timer);
                }
                // 把每次加1 这个步长值改为一个慢慢变小的值  步长公式:(目标值 - 现在的位置) / 10
                obj.style.left = obj.offsetLeft + step + 'px';

            }, 15);
        }
        var span = document.querySelector('span');
        var btn500 = document.querySelector('.btn500');
        var btn800 = document.querySelector('.btn800');

        btn500.addEventListener('click', function() {
            // 调用函数
            animate(span, 500);
        })
        btn800.addEventListener('click', function() {
                // 调用函数
                animate(span, 800);
            })
            // 匀速动画 就是 盒子是当前的位置 +  固定的值 10 
            // 缓动动画就是  盒子当前的位置 + 变化的值(目标值 - 现在的位置) / 10)
    </script>
04-WebApl_第71张图片

缓动动画添加回调函数

回调函数 :上一个事件执行完后再来执行这个事件

回调函数原理函数可以作为一个参数。将这个函数作为参数传到另一个函数里面,当那个函数执行完之后,再执行传进去的这个函数,这个过程就叫做回调

回调函数写的位置:定时器结束的位置。

动画完整版代码

function animate(obj, target, callback) {
    // console.log(callback);  callback = function() {}  调用的时候 callback()

    // 先清除以前的定时器,只保留当前的一个定时器执行
    clearInterval(obj.timer);
    obj.timer = setInterval(function() {
        // 步长值写到定时器的里面
        // 把我们步长值改为整数 不要出现小数的问题
        // var step = Math.ceil((target - obj.offsetLeft) / 10);
        var step = (target - obj.offsetLeft) / 10;
        step = step > 0 ? Math.ceil(step) : Math.floor(step);
        if (obj.offsetLeft == target) {
            // 停止动画 本质是停止定时器
            clearInterval(obj.timer);
            // 回调函数写到定时器结束里面
            // if (callback) {
            //     // 调用函数
            //     callback();
            // }
            callback && callback();
        }
        // 把每次加1 这个步长值改为一个慢慢变小的值  步长公式:(目标值 - 现在的位置) / 10
        obj.style.left = obj.offsetLeft + step + 'px';

    }, 15);
}
04-WebApl_第72张图片

案例 - 引用animate动画函数

  1. 单独新建一个JS文件。
  2. HTML文件引入 JS 文件。
    <script>
        // 1. 获取元素
        var sliderbar = document.querySelector('.sliderbar');
        var con = document.querySelector('.con');
        // 当我们鼠标经过 sliderbar 就会让 con这个盒子滑动到左侧
        // 当我们鼠标离开 sliderbar 就会让 con这个盒子滑动到右侧
        sliderbar.addEventListener('mouseenter', function() {
            // animate(obj, target, callback);
            animate(con, -160, function() {
                // 当我们动画执行完毕,就把 ← 改为 →
                sliderbar.children[0].innerHTML = '→';
            });

        })
        sliderbar.addEventListener('mouseleave', function() {
            // animate(obj, target, callback);
            animate(con, 0, function() {
                sliderbar.children[0].innerHTML = '←';
            });

        })
    </script>
引用animate动画函数

常见网页特效案例

案例 - 网页轮播图 ★

轮播图四部分组成

  1. 大盒子(包含整个轮播图)
  2. 左右箭头 (a) 不显示注意 ul 定位 z-index
  3. ul 里面放图片
  4. 下面小圆圈

轮播图也称为焦点图,是网页中比较常见的网页特效。

功能需求:

​ 1.鼠标经过轮播图模块,左右按钮显示,离开隐藏左右按钮。

​ 2.点击右侧按钮一次,图片往左播放一张,以此类推,左侧按钮同理。

​ 3.图片播放的同时,下面小圆圈模块跟随一起变化。

​ 4.点击小圆圈,可以播放相应图片。

​ 5.鼠标不经过轮播图,轮播图也会自动播放图片。

​ 6.鼠标经过,轮播图模块, 自动播放停止。

鼠标经过显示隐藏左右按钮
    // 2. 鼠标经过focus 就显示隐藏左右按钮
    focus.addEventListener('mouseenter', function() {
        arrow_l.style.display = 'block';
        arrow_r.style.display = 'block';
    });
    focus.addEventListener('mouseleave', function() {
        arrow_l.style.display = 'none';
        arrow_r.style.display = 'none';
    });
动态生成小圆圈 绑定点击事件排他思想

案例分析

① 动态生成小圆圈
② 核心思路:小圆圈的个数要跟图片张数一致
③ 所以首先先得到ul里面图片的张数(图片放入li里面,所以就是li的个数)
④ 利用循环动态生成小圆圈(这个小圆圈要放入ol里面)
创建节点 createElement(‘li’)
插入节点 ol. appendChild(li)
⑦ 第一个小圆圈需要添加 current 类

① 小圆圈的排他思想
② 点击当前小圆圈,就添加current类
③ 其余的小圆圈就移除这个current类
④ 注意: 我们在刚才生成小圆圈的同时,就可以直接绑定这个点击事件了。

    // 3. 动态生成小圆圈  有几张图片,我就生成几个小圆圈
    var ul = focus.querySelector('ul');
    var ol = focus.querySelector('.circle');
    // console.log(ul.children.length);
    for (var i = 0; i < ul.children.length; i++) {
        // 创建一个小li 
        var li = document.createElement('li');
        // 记录当前小圆圈的索引号 通过自定义属性来做 
        li.setAttribute('index', i);
        // 把小li插入到ol 里面
        ol.appendChild(li);
        // 4. 小圆圈的排他思想 我们可以直接在生成小圆圈的同时直接绑定点击事件
        li.addEventListener('click', function() {
            // 干掉所有人 把所有的小li 清除 current 类名
            for (var i = 0; i < ol.children.length; i++) {
                ol.children[i].className = '';
            }
            // 留下我自己  当前的小li 设置current 类名
            this.className = 'current';
            })
    }
点击小圆圈滚动图片

案例分析

① 点击小圆圈滚动图片
② 此时用到animate动画函数,将js文件引入(注意,因为index.js 依赖 animate.js 所以,animate.js 要写到 index.js 上面)
③ 使用动画函数的前提,该元素必须有定位
④ 注意是ul 移动 而不是小li
⑤ 滚动图片的核心算法: 点击某个小圆圈 , 就让图片滚动 小圆圈的索引号乘以图片的宽度做为ul移动距离
⑥ 此时需要知道小圆圈的索引号, 我们可以在生成小圆圈的时候,给它设置一个自定义属性,点击的时候获取这个自定义属性即可。

            // 5. 点击小圆圈,移动图片 当然移动的是 ul 
            // ul 的移动距离 小圆圈的索引号 乘以 图片的宽度 注意是负值
            // 当我们点击了某个小li 就要把这个li 的索引号给 num  
            var index = this.getAttribute("index");
            // 图片的宽度
            var focusWidth = focus.offsetWidth;
            animate(ul, -index * focusWidth);
右侧按钮无缝滚动

案例分析

① 点击右侧按钮一次,就让图片滚动一张。
② 声明一个变量num, 点击一次,自增1, 让这个变量乘以图片宽度,就是 ul 的滚动距离。
图片无缝滚动原理
④ 把ul 第一个li 复制一份,放到ul 的最后面
⑤ 当图片滚动到克隆的最后一张图片时, 让ul 快速的、不做动画的跳到最左侧: left 为0
⑥ 同时num 赋值为0,可以从新开始滚动图片了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gOMqwnAu-1646565991682)(https://gitee.com/shen-longfei001/image-bed/raw/master/ImageWebApls/202203061831607.gif)]

  var num = 0;
  arrow_r.addEventListener("click", function () {
    if (num == ul.children.length-1) {
      ul.style.left = 0;
      num = 0;
    }
    num++;
    animate(ul, -num * focusWidth);
克隆第一张图片

① 克隆第一张图片
② 克隆ul 第一个li cloneNode() 加true 深克隆 复制里面的子节点 false 浅克隆
③ 添加到 ul 最后面 appendChild

    // 6. 克隆第一张图片(li)放到ul 最后面
    // 写在for 循环 生成li外 , 因为不能让ol循环到
    var first = ul.children[0].cloneNode(true);
    ul.appendChild(first);
小圆圈跟随右侧按钮一起变化

案例分析

① 点击右侧按钮, 小圆圈跟随变化
② 最简单的做法是再声明一个变量circle,每次点击自增1,注意,左侧按钮也需要这个变量,因此要声明全局变量。
③ 但是图片有5张,我们小圆圈只有4个少一个,必须加一个判断条件
④ 如果circle == 4 就 从新复原为 0

    // circle 控制小圆圈的播放
    var circle = 0;
    circle++;
  // 等于 他的长度说明到了最后一张了 . 需要把索引改为 0
    if (circle == ol.children.length) {
      circle = 0;
    }

    for (var i = 0; i < ol.children.length; i++) {     // 其他小圆圈 清除样式
      ol.children[i].className = "";
    }  // 当前小圆圈添加 样式
    ol.children[circle].className = "current";
右侧按钮和小圆圈 BUG
            // 当我们点击了某个小li 就要把这个li 的索引号给 num  
            num = index;
            // 当我们点击了某个小li 就要把这个li 的索引号给 circle  
            circle = index;
            // num = circle = index;
左侧按钮
  // 左侧按钮
  arrow_l.addEventListener("click", function () {
    if (num == 0) {
      // 到了第一张 直接跳到最后一张
      ul.style.left = -(ul.children.length - 1) * focusWidth + "px";
      num = ul.children.length - 1;
    }
    num--;
    animate(ul, -num * focusWidth);
    // 点击左侧按钮,小圆圈跟随一起变化 可以再声明一个变量控制小圆圈的播放
    circle--;
   // 如果circle < 0  说明第一张图片,则小圆圈要改为第4个小圆圈(3)   
    if (circle < 0) {
      circle = ol.children.length - 1;
    }
  // circle = circle < 0 ? ol.children.length - 1 : circle;    
    for (var i = 0; i < ol.children.length; i++) {
      ol.children[i].className = "";
    }
    ol.children[circle].className = "current";
      
      // 可优化 外面封装一个函数调用它
      
    function circleChildren() {
    for (var i = 0; i < ol.children.length; i++) {
      ol.children[i].className = "";
    }
    ol.children[circle].className = "current";
  }
  });
自动播放

案例分析

① 自动播放功能
添加一个定时器
③ 自动播放轮播图,实际就类似于点击了右侧按钮
④ 此时我们使用手动调用右侧按钮点击事件 arrow_r.click()
⑤ 鼠标经过focus 就停止定时器
⑥ 鼠标离开focus 就开启定时器

  var timer = setInterval(function () {
    arrow_r.click();
  }, 2000);
节流阀

04-WebApl_第73张图片

04-WebApl_第74张图片

防止轮播图按钮连续点击造成播放过快
节流阀目的:当上一个函数动画内容执行完毕,再去执行下一个函数动画,让事件无法连续触发
核心实现思路:利用回调函数,添加一个变量来控制,锁住函数和解锁函数。
开始设置一个变量 var flag = true;
If(flag) {flag = false; do something} 关闭水龙头
利用回调函数 动画执行完毕, flag = true 打开水龙头

逻辑中断 高级技巧
      // if (callback) {
      //     callback(); // 调用函数
      // }
      callback && callback();

利用短路运算 , 要求左边和右边都为 true 才去执行

如果 && 左边为 true (有参数传进来) , 就去看右边的 , (调用函数)

如果 && 左边为 false (没有参数传进来) , 就结束 (不去看右边的)

案例 - 返回顶部

滚动窗口至文档中的特定位置。
window.scroll(x, y)
注意,里面的x和y 不跟单位,直接写数字

案例分析

① 带有动画的返回顶部
② 此时可以继续使用我们封装的动画函数
③ 只需要把所有的left 相关的值 改为 跟页面垂直滚动距离相关就可以了
④ 页面滚动了多少,可以通过 window.pageYOffset 得到
⑤ 最后是页面滚动,使用 window.scroll(x,y)

            // 3. 当我们点击了返回顶部模块,就让窗口滚动的页面的最上方
        goBack.addEventListener('click', function() {
            // 里面的x和y 不跟单位的 直接写数字即可
            // window.scroll(0, 0);
            // 因为是窗口滚动 所以对象是window
            animate(window, 0);
        });
        // 动画函数
        function animate(obj, target, callback) {
            // console.log(callback);  callback = function() {}  调用的时候 callback()
            
            // 先清除以前的定时器,只保留当前的一个定时器执行
            clearInterval(obj.timer);
            obj.timer = setInterval(function() {
                // 步长值写到定时器的里面
                // 把我们步长值改为整数 不要出现小数的问题
                // var step = Math.ceil((target - obj.offsetLeft) / 10);
                var step = (target - window.pageYOffset) / 10;
                step = step > 0 ? Math.ceil(step) : Math.floor(step);
                if (window.pageYOffset == target) {
                    // 停止动画 本质是停止定时器
                    clearInterval(obj.timer);
                    // 回调函数写到定时器结束里面
                    // if (callback) {
                    //     // 调用函数
                    //     callback();
                    // }
                    callback && callback();
                }
                // 把每次加1 这个步长值改为一个慢慢变小的值  步长公式:(目标值 - 现在的位置) / 10
                // obj.style.left = window.pageYOffset + step + 'px';
                window.scroll(0, window.pageYOffset + step);  // window 滚动 window.scroll(x, y) xy 轴
            }, 15);
        }

案例 - 筋斗云

要求

鼠标经过某个小li, 筋斗云跟这到当前小li位置
鼠标离开这个小li, 筋斗云复原为原来的位置
鼠标点击了某个小li, 筋斗云就会留在点击这个小li 的位置

案例分析

① 利用动画函数做动画效果
② 原先筋斗云的起始位置是0
③ 鼠标经过某个小li, 把当前小li 的 offsetLeft 位置 做为目标值即可
④ 鼠标离开某个小li, 就把目标值设为 0
⑤ 如果点击了某个小li, 就把li当前的位置存储起来,做为筋斗云的起始位置 current = this.offsetLeft;

    <script>
        window.addEventListener('load', function() {
            // 1. 获取元素
            var cloud = document.querySelector('.cloud');
            var c_nav = document.querySelector('.c-nav');
            var lis = c_nav.querySelectorAll('li');
            // 2. 给所有的小li绑定事件 
            // 这个current 做为筋斗云的起始位置
            var current = 0;
            for (var i = 0; i < lis.length; i++) {
                // (1) 鼠标经过把当前小li 的位置做为目标值
                lis[i].addEventListener('mouseenter', function() {
                    animate(cloud, this.offsetLeft);
                });
                // (2) 鼠标离开就回到起始的位置 
                lis[i].addEventListener('mouseleave', function() {
                    animate(cloud, current);
                });
                // (3) 当我们鼠标点击,就把当前位置做为目标值
                lis[i].addEventListener('click', function() {
                    current = this.offsetLeft;
                });
            }
        })
    </script>
04-WebApl_第75张图片

移动端网页特效

04-WebApl_第76张图片04-WebApl_第77张图片

触屏事件

触屏事件概述

移动端浏览器兼容性较好,我们不需要考虑以前 JS 的兼容性问题,可以放心的使用原生 JS 书写效果,但是移动
端也有自己独特的地方。比如触屏事件 touch(也称触摸事件),Android 和 IOS 都有。

触屏 touch 事件 说明
touchstart 手指触摸到一个 DOM 元素时触发
touchmove 手指在一个 DOM 元素上滑动时触发
touchend 手指从一个 DOM 元素上移开时触发
    <script>
      // 1. 获取元素
      // 2. 手指触摸DOM元素事件
      var div = document.querySelector("div");
      div.addEventListener("touchstart", function () {
        console.log("我模了你");
      });
      // 3. 手指在DOM元素上移动事件
      div.addEventListener("touchmove", function () {
        console.log("我继续模你");
      });
      // 3. 手指离开DOM元素事件
      div.addEventListener("touchend", function () {
        console.log("我走了");
      });
    </script>

触摸事件对象(TouchEvent)

触摸列表 说明
touches 正在触摸屏幕的所有列表
targetTouches 正在触摸当前 DOM 元素上的手指的一个列表
changedTouches 手指状态发生了改变的列表 , 从无到有 从有到无的变化

事件小结

04-WebApl_第78张图片

因为平时我们都是给元素注册触摸事件,所以重点记住 targetTocuhes

      p.addEventListener("touchstart", function (e) {
        // e.targetTouches[0] 就可以得到正在触摸 dom 元素的第一个手指的相关信息 如手指坐标 哪个元素等等
        console.log(e.targetTouches[0]);
      });
      div.addEventListener("touchend", function (e) {
        // 当我们手指离开屏幕的时候 , 就没有touches 和 targetTouches 列表
        // 但是会有 changedTouches
        console.log(e);
      });

案例-移动端拖动元素

  1. touchstart、touchmove、touchend 可以实现拖动元素
  2. 但是拖动元素需要当前手指的坐标值 我们可以使用 targetTouches[0] 里面的pageXpageY
  3. 移动端拖动的原理: 手指移动中,计算出手指移动的距离。然后用盒子原来的位置 + 手指移动的距离
  4. 手指移动的距离: 手指滑动中的位置 减去 手指刚开始触摸的位置

拖动元素三步曲:

盒子需要加定位

(1) 触摸元素 touchstart: 获取手指初始坐标,同时获得盒子原来的位置
(2) 移动手指 touchmove: 计算手指的滑动距离,并且移动盒子
(3) 离开手指 touchend:

注意: 手指移动也会触发滚动屏幕所以这里要阻止默认的屏幕滚动 e.preventDefault();

04-WebApl_第79张图片

    <script>
        // (1) 触摸元素 touchstart:  获取手指初始坐标,同时获得盒子原来的位置
        // (2) 移动手指 touchmove:  计算手指的滑动距离,并且移动盒子
        // (3) 离开手指 touchend:
        var div = document.querySelector('div');
        var startX = 0; //获取手指初始坐标
        var startY = 0;
        var x = 0; //获得盒子原来的位置
        var y = 0;
        div.addEventListener('touchstart', function(e) {
            //  获取手指初始坐标
            startX = e.targetTouches[0].pageX;
            startY = e.targetTouches[0].pageY;
            x = this.offsetLeft;
            y = this.offsetTop;
        });

        div.addEventListener('touchmove', function(e) {
            //  计算手指的移动距离: 手指移动之后的坐标减去手指初始的坐标
            var moveX = e.targetTouches[0].pageX - startX;
            var moveY = e.targetTouches[0].pageY - startY;
            // 移动我们的盒子 盒子原来的位置 + 手指移动的距离
            this.style.left = x + moveX + 'px';
            this.style.top = y + moveY + 'px';
            e.preventDefault(); // 阻止屏幕滚动的默认行为
        });
    </script>

移动端常见特效

案例 - 移动端轮播图

移动端轮播图功能和基本PC端一致。

  1. 可以自动播放图片
  2. 手指可以拖动播放轮播图
自动播放

案例分析

  1. 自动播放功能
  2. 开启定时器
  3. 移动端移动,可以使用translate 移动
  4. 想要图片优雅的移动,请添加过渡效果
    // 1. 获取元素 
    var focus = document.querySelector('.focus');
    var ul = focus.children[0];
    // 获得focus 的宽度
    var w = focus.offsetWidth;
    var ol = focus.children[1];
    // 2. 利用定时器自动轮播图图片
    var index = 0;
    var timer = setInterval(function() {
        index++;
        var translatex = -index * w;
        ul.style.transition = 'all .3s';
        ul.style.transform = 'translateX(' + translatex + 'px)';
        // ul.style.transform = `translateX(${translatex}px)`;
    }, 2000);
无缝滚动
  1. 注意,我们判断条件是要等到图片滚动完毕再去判断,就是过渡完成后判断

  2. 此时需要添加检测过渡完成事件  transitionend

  3. 判断条件:如果索引号等于 3 说明走到最后一张图片,此时 索引号要复原为 0

  4. 此时图片,去掉过渡效果,然后移动

  5. 如果索引号小于0, 说明是倒着走, 索引号等于2

  6. 此时图片,去掉过渡效果,然后移动

    // 等着我们过渡完成之后,再去判断 监听过渡完成的事件 transitionend 
    ul.addEventListener('transitionend', function() {
        // 无缝滚动
        if (index >= 3) {
            index = 0;
            // console.log(index);
            // 去掉过渡效果 这样让我们的ul 快速的跳到目标位置
            ul.style.transition = 'none';
            // 利用最新的索引号乘以宽度 去滚动图片
            var translatex = -index * w;
            ul.style.transform = 'translateX(' + translatex + 'px)';
        } else if (index < 0) {
            index = 2;
            ul.style.transition = 'none';
            // 利用最新的索引号乘以宽度 去滚动图片
            var translatex = -index * w;
            ul.style.transform = 'translateX(' + translatex + 'px)';
        }
小圆点跟随变化效果

案例分析

  1. 把ol里面li带有current类名的选出来去掉类名 remove
  2. 让当前索引号的小li 加上 current   add
  3. 但是,是等着过渡结束之后变化,所以这个写到 transitionend 事件里面
        // 3. 小圆点跟随变化
        // 把ol里面li带有current类名的选出来去掉类名 remove
        ol.querySelector('.current').classList.remove('current');
        // 让当前索引号 的小li 加上 current   add
        ol.children[index].classList.add('current');
手指滑动轮播图

案例分析

  1. 本质就是ul跟随手指移动,简单说就是移动端拖动元素
  2. 触摸元素touchstart:  获取手指初始坐标
  3. 移动手指touchmove:  计算手指的滑动距离,并且移动盒子
  // 4. 手指滑动轮播图
  // 触摸元素 touchstart: 获取手指初始坐标
  var startX = 0;
  var moveX = 0; // 后面我们会使用这个移动距离所以要定义一个全局变量
  var flag = false;
  ul.addEventListener("touchstart", function (e) {
    startX = e.targetTouches[0].pageX;
    // 手指触摸的时候就停止定时器
    clearInterval(timer);
  });
  // 移动手指 touchmove: 计算手指的滑动距离, 并且移动盒子
  ul.addEventListener("touchmove", function (e) {
    // 计算移动距离
    moveX = e.targetTouches[0].pageX - startX;
    // 移动盒子:  盒子原来的位置 + 手指移动的距离
    var translateX = -index * w + moveX;
    // 手指拖动的时候,不需要动画效果所以要取消过渡效果
    ul.style.transition = "none";
    ul.style.transform = "translateX(" + translateX + "px)";
  });
   flag = true; // 如果用户手指移动过我们再去判断否则不做判断效果
    e.preventDefault(); // 阻止滚动屏幕的行为
手指滑动播放上/下一张
  1. 离开手指touchend:   根据滑动的距离分不同的情况
  2. 如果移动距离小于某个像素  就回弹原来位置
  3. 如果移动距离大于某个像素就上一张下一张滑动。
  4. 滑动也分为左滑动和右滑动判断的标准是 移动距离正负 如果是负值就是左滑 反之右滑
  5. 如果是左滑就播放下一张 (index++)
  6. 如果是右滑就播放上一张  (index–)
// 手指离开 根据移动距离去判断是回弹还是播放上一张下一张
  ul.addEventListener("touchend", function (e) {
    if (flag) {
      // (1) 如果移动距离大于50像素我们就播放上一张或者下一张
      if (Math.abs(moveX) > 50) {
        // 如果是右滑就是 播放上一张 moveX 是正值
        if (moveX > 0) {
          index--;
        } else {
          // 如果是左滑就是 播放下一张 moveX 是负值
          index++;
        }
        var translatex = -index * w;
        ul.style.transition = "all .3s";
        ul.style.transform = "translateX(" + translatex + "px)";
      } else {
        // (2) 如果移动距离小于50像素我们就回弹
        var translatex = -index * w;
        ul.style.transition = "all .1s";
        ul.style.transform = "translateX(" + translatex + "px)";
      }
    }
    // 手指离开的时候就重新开启定时器
    clearInterval(timer);
    timer = setInterval(function () {
      index++;
      var translatex = -index * w;
      ul.style.transition = "all .3s";
      ul.style.transform = "translateX(" + translatex + "px)";
    }, 2000);
  });
返回顶部模块

案例分析

  1. 滚动某个地方显示
  2. 事件:scroll页面滚动事件
  3. 如果被卷去的头部(window.pageYOffset )大于某个数值
  4. 点击,window.scroll(0,0) 返回顶部
  // 返回顶部模块制作
  var goBack = document.querySelector(".goBack");
  var nav = document.querySelector("nav");
  window.addEventListener("scroll", function () {
    if (window.pageYOffset >= nav.offsetTop) {
      goBack.style.display = "block";
    } else {
      goBack.style.display = "none";
    }
  });
  goBack.addEventListener("click", function () {
    window.scroll(0, 0);
  });
完整代码
window.addEventListener('load', function() {
  // alert(1);
  // 1. 获取元素
  var focus = document.querySelector(".focus");
  var ul = focus.children[0];
  // 获得focus 的宽度
  var w = focus.offsetWidth;
  var ol = focus.children[1];
  // 2. 利用定时器自动轮播图图片
  var index = 0;
  var timer = setInterval(function () {
    index++;
    var translatex = -index * w;
    ul.style.transition = "all .3s";
    ul.style.transform = "translateX(" + translatex + "px)";
  }, 2000);
  // 等着我们过渡完成之后,再去判断 监听过渡完成的事件 transitionend
  ul.addEventListener("transitionend", function () {
    // 无缝滚动
    if (index >= 3) {
      index = 0;
      // console.log(index);
      // 去掉过渡效果 这样让我们的ul 快速的跳到目标位置
      ul.style.transition = "none";
      // 利用最新的索引号乘以宽度 去滚动图片
      var translatex = -index * w;
      ul.style.transform = "translateX(" + translatex + "px)";
    } else if (index < 0) {
      index = 2;
      ul.style.transition = "none";
      // 利用最新的索引号乘以宽度 去滚动图片
      var translatex = -index * w;
      ul.style.transform = "translateX(" + translatex + "px)";
    }
    // 3. 小圆点跟随变化
    // 把ol里面li带有current类名的选出来去掉类名 remove
    ol.querySelector(".current").classList.remove("current");
    // 让当前索引号 的小li 加上 current   add
    ol.children[index].classList.add("current");
  });

  // 4. 手指滑动轮播图
  // 触摸元素 touchstart: 获取手指初始坐标
  var startX = 0;
  var moveX = 0; // 后面我们会使用这个移动距离所以要定义一个全局变量
  var flag = false;
  ul.addEventListener("touchstart", function (e) {
    startX = e.targetTouches[0].pageX;
    // 手指触摸的时候就停止定时器
    clearInterval(timer);
  });
  // 移动手指 touchmove: 计算手指的滑动距离, 并且移动盒子
  ul.addEventListener("touchmove", function (e) {
    // 计算移动距离
    moveX = e.targetTouches[0].pageX - startX;
    // 移动盒子:  盒子原来的位置 + 手指移动的距离
    var translatex = -index * w + moveX;
    // 手指拖动的时候,不需要动画效果所以要取消过渡效果
    ul.style.transition = "none";
    ul.style.transform = "translateX(" + translatex + "px)";
    flag = true; // 如果用户手指移动过我们再去判断否则不做判断效果
    e.preventDefault(); // 阻止滚动屏幕的行为
  });
  // 手指离开 根据移动距离去判断是回弹还是播放上一张下一张
  ul.addEventListener("touchend", function (e) {
    if (flag) {
      // (1) 如果移动距离大于50像素我们就播放上一张或者下一张
      if (Math.abs(moveX) > 50) {
        // 如果是右滑就是 播放上一张 moveX 是正值
        if (moveX > 0) {
          index--;
        } else {
          // 如果是左滑就是 播放下一张 moveX 是负值
          index++;
        }
        var translatex = -index * w;
        ul.style.transition = "all .3s";
        ul.style.transform = "translateX(" + translatex + "px)";
      } else {
        // (2) 如果移动距离小于50像素我们就回弹
        var translatex = -index * w;
        ul.style.transition = "all .1s";
        ul.style.transform = "translateX(" + translatex + "px)";
      }
    }
    // 手指离开的时候就重新开启定时器
    clearInterval(timer);
    timer = setInterval(function () {
      index++;
      var translatex = -index * w;
      ul.style.transition = "all .3s";
      ul.style.transform = "translateX(" + translatex + "px)";
    }, 2000);
  });

  // 返回顶部模块制作
  var goBack = document.querySelector(".goBack");
  var nav = document.querySelector("nav");
  window.addEventListener("scroll", function () {
    if (window.pageYOffset >= nav.offsetTop) {
      goBack.style.display = "block";
    } else {
      goBack.style.display = "none";
    }
  });
  goBack.addEventListener("click", function () {
    window.scroll(0, 0);
  });
})

classList 属性

classList属性是HTML5新增的一个属性,返回元素的类名。但是ie10以上版本支持。

该属性用于在元素中添加,移除及切换 CSS 类。有以下方法

添加类:

element.classList.add(’类名’); 是在后面追加类名不会覆盖以前的类名 注意前面不需要加.

focus.classList.add('current');

移除类:

element.classList.remove(’类名’);

focus.classList.remove('current');

切换类:

element.classList.toggle(’类名’);

focus.classList.toggle('current');

注意:以上方法里面,所有类名都不带点

        // classList 返回元素的类名
        var div = document.querySelector('div');
        // console.log(div.classList[1]);
        // 1. 添加类名  是在后面追加类名不会覆盖以前的类名 注意前面不需要加.
        div.classList.add('three');
        // 2. 删除类名
        div.classList.remove('one');
        // 3. 切换类
        var btn = document.querySelector('button');
        btn.addEventListener('click', function() {
            document.body.classList.toggle('bg');
        })

click 延时解决方案

移动端 click 事件会有 300ms 的延时,原因是移动端屏幕双击会缩放(double tap to zoom) 页面。

解决方案:

​ 1. 禁用缩放。 浏览器禁用默认的双击缩放行为并且去掉300ms 的点击延迟。

  <meta name="viewport" content="user-scalable=no">

​ 2.利用touch事件自己封装这个事件解决300ms 延迟。

​ 原理就是:

  1. 当我们手指触摸屏幕,记录当前触摸时间
  2. 当我们手指离开屏幕, 用离开的时间减去触摸的时间
  3. 如果时间小于150ms,并且没有滑动过屏幕, 那么我们就定义为点击

代码如下:

//封装tap,解决click 300ms 延时
function tap (obj, callback) {
        var isMove = false;
        var startTime = 0; // 记录触摸时候的时间变量
        obj.addEventListener('touchstart', function (e) {
            startTime = Date.now(); // 记录触摸时间
        });
        obj.addEventListener('touchmove', function (e) {
            isMove = true;  // 看看是否有滑动,有滑动算拖拽,不算点击
        });
        obj.addEventListener('touchend', function (e) {
            if (!isMove && (Date.now() - startTime) < 150) {  // 如果手指触摸和离开时间小于150ms 算点击
                callback && callback(); // 执行回调函数
            }
            isMove = false;  //  取反 重置
            startTime = 0;
        });
}
//调用  
  tap(div, function(){   // 执行代码  });

  1. 使用插件。fastclick 插件解决300ms 延迟。

移动端常用开发插件

什么是插件

移动端要求的是快速开发,所以我们经常会借助于一些插件来帮我完成操作,那么什么是插件呢?

JS 插件是 js 文件,它遵循一定规范编写,方便程序展示效果,拥有特定功能且方便调用。如轮播图和瀑布流插件。

特点:它一般是为了解决某个问题而专门存在,其功能单一,并且比较小。

我们以前写的animate.js 也算一个最简单的插件

fastclick 插件的使用

fastclick 插件解决 300ms 延迟。 使用延时

GitHub官网地址: https://github.com/ftlabs/fastclick

if ('addEventListener' in document) {
            document.addEventListener('DOMContentLoaded', function() {
                       FastClick.attach(document.body);
            }, false);
}

// 使用插件
      var div = document.querySelector("div");
      div.addEventListener("click", function () {
        alert("1");
      });

Swiper 插件的使用

中文官网地址: https://www.swiper.com.cn/

  1. 引入插件相关文件。
    
    <link rel="stylesheet" href="./css/swiper.min.css">
    
    <script src="./js/swiper.min.js">script>
    
    <script src="js/index.js">script>
  1. 按照规定语法使用
    1. 复制 HTML 结构
 
 <div class="swiper-container">
    <div class="swiper-wrapper">
      <div class="swiper-slide">Slide 1div>
      <div class="swiper-slide">Slide 2div>
      <div class="swiper-slide">Slide 3div>
      <div class="swiper-slide">Slide 4div>
      <div class="swiper-slide">Slide 5div>
      <div class="swiper-slide">Slide 6div>
      <div class="swiper-slide">Slide 7div>
      <div class="swiper-slide">Slide 8div>
      <div class="swiper-slide">Slide 9div>
      <div class="swiper-slide">Slide 10div>
    div>
    
    <div class="swiper-pagination">div>
  div>
  1. 复制 CSS 样式
/* 复制swiper css样式到index.css */
    .swiper-container {
      width: 100%;
      height: 100%;
    }
    .swiper-slide {
      text-align: center;
      font-size: 18px;
      background: #fff;

      /* Center slide text vertically */
      display: -webkit-box;
      display: -ms-flexbox;
      display: -webkit-flex;
      display: flex;
      -webkit-box-pack: center;
      -ms-flex-pack: center;
      -webkit-justify-content: center;
      justify-content: center;
      -webkit-box-align: center;
      -ms-flex-align: center;
      -webkit-align-items: center;
      align-items: center;
    }
    .swiper-pagination-bullet {
      width: 20px;
      height: 20px;
      text-align: center;
      line-height: 20px;
      font-size: 12px;
      color:#000;
      opacity: 1;
      background: rgba(0,0,0,0.2);
    }
    .swiper-pagination-bullet-active {
      color:#fff;
      background: #007aff;
    }
  1. 复制 js 并引入
// 引入 index.js
window.addEventListener("load", function () {
  var swiper = new Swiper(".swiper-container", {
    pagination: {
      el: ".swiper-pagination", // 调用分页器
      clickable: true, // 是否可以点击
      renderBullet: function (index, className) { // 显示分页序号
        return '+ className + '">' + (index + 1) + "";
      },
    },
  });
});

其他移动端常见插件

lsuperslide: http://www.superslide2.com/

l iscroll: https://github.com/cubiq/iscroll

插件的使用总结

1.确认插件实现的功能 ( 轮播图 , Tab 栏切换 )

2.去官网查看使用说明

3.下载插件

4.打开demo实例文件,查看需要引入的相关文件,并且引入

5.复制demo实例文件中的结构html,样式css以及js代码

移动端视频插件 zy.media.js

H5 给我们提供了 video 标签,但是浏览器的支持情况不同。

不同的视频格式文件,我们可以通过source解决。

但是外观样式,还有暂停,播放,全屏等功能我们只能自己写代码解决。

这个时候我们可以使用插件方式来制作。

我们可以通过 JS 修改元素的大小、颜色、位置等样式。

image-20220112142152476

	x11.复制 html 结构 ,复制 css 样式22.引入 css 样式33.引入 js44.调用 js

移动端常用开发框架

框架,顾名思义就是一套架构,它会基于自身的特点向用户提供一套较为完整的解决方案。框架的控制权在框架本身,使用者要按照框架所规定的某种规范进行开发。

插件一般是为了解决某个问题而专门存在,其功能单一,并且比较小。

前端常用的框架有 Bootstrap、Vue、Angular、React 等。既能开发PC端,也能开发移动端

前端常用的移动端插件有 swipersuperslideiscroll等。

框架: 大而全,一整套解决方案

插件: 小而专一,某个功能的解决方案

Bootstrap

Bootstrap 官网

Bootstrap 是一个简洁、直观、强悍的前端开发框架,它让 web 开发更迅速、简单。

它能开发PC端,也能开发移动端

Bootstrap JS插件使用步骤:

    <link rel="stylesheet" href="./bootstrap/css/bootstrap.min.css" />
    <script src="./bootstrap/js/jquery.min.js">script>
    <script src="./bootstrap/js/bootstrap.min.js">script>

1.引入相关js 文件

2.复制HTML 结构

3.修改对应样式

      .carousel,
      .carousel img {
        height: 300px !important;
        width: 100%;
      }

4.修改相应JS 参数

<script>
        $(".carousel").carousel({
             interval: 1000,
         });
</script>

本地存储

04-WebApl_第80张图片

本地存储特性

1、数据存储在用户浏览器中

2、设置、读取方便、甚至页面刷新不丢失数据

3、容量较大,sessionStorage约5M、localStorage约20M

4、只能存储字符串,可以将对象JSON.stringify() 编码后存储

sessionStorage 存储的值 , 只能是字符串 , 如果传入的不是字符串 将会自动转为字符串

image-20220112144458491sessionStorage 会话存储

  1. 生命周期为关闭浏览器窗口
  2. 在同一个窗口(页面)下数据可以共享
  3. 以键值对的形式存储使用

存储数据:

sessionStorage.setItem(key, value)

获取数据:

sessionStorage.getItem(key)

删除数据:

sessionStorage.removeItem(key)

清空数据:(所有都清除掉)

sessionStorage.clea)
    <script>
        // console.log(localStorage.getItem('username'));

        var ipt = document.querySelector('input');
        var set = document.querySelector('.set');
        var get = document.querySelector('.get');
        var remove = document.querySelector('.remove');
        var del = document.querySelector('.del');
        set.addEventListener('click', function() {
            // 当我们点击了之后,就可以把表单里面的值存储起来
            var val = ipt.value;
            sessionStorage.setItem('uname', val);
            sessionStorage.setItem('pwd', val);
        });
        get.addEventListener('click', function() {
            // 当我们点击了之后,就可以把表单里面的值获取过来
            console.log(sessionStorage.getItem('uname'));

        });
        remove.addEventListener('click', function() {
            // 
            sessionStorage.removeItem('uname');

        });
        del.addEventListener('click', function() {
            // 当我们点击了之后,清除所有的
            sessionStorage.clear();

        });
    </script>

localStorage 本地存储

  1. 生命周期永久生效,除非手动删除 否则关闭页面也会存在
  2. 可以多窗口(页面)共享(同一浏览器可以共享)
  3. 以键值对的形式存储使用

存储数据:

localStorage.setItem(key, value)

获取数据:

localStorage.getItem(key)

删除数据:

localStorage.removeItem(key)

清空数据:(所有都清除掉)

localStorage.clear()

sessionStorage 和 localStorage 区别

相同点 : 使用方法是一样的 ; 都就可以把数据存储到浏览器中

不同点 :

  • 生命周期不一样
    • sessionStorage 生命周期为关闭浏览器窗口
    • localStorage 生明周期永久生效,除非手动删除
  • 容量存储容量
    • sessionStorage 约 5M
    • localStorage 约 20M
  • 数据共享
    • sessionStorage 在同一个窗口(页面)下数据可以共享
    • localStorage 可以多窗口(页面)共享

案例-记住用户名

如果勾选记住用户名, 下次用户打开浏览器,就在文本框里面自动显示上次登录的用户名

案例分析

  1. 把数据存起来,用到本地存储
  2. 关闭页面,也可以显示用户名,所以用到localStorage
  3. 打开页面,先判断是否有这个用户名,如果有,就在表单里面显示用户名,并且勾选复选框
  4. 当复选框发生改变的时候change事件
  5. 如果勾选,就存储,否则就移除
    <input type="text" id="username"> <input type="checkbox" name="" id="remember"> 记住用户名
    <script>
        var username = document.querySelector('#username');
        var remember = document.querySelector('#remember');
        if (localStorage.getItem('username')) {
            username.value = localStorage.getItem('username');
            remember.checked = true;
        }
        remember.addEventListener('change', function() {
            if (this.checked) {
                localStorage.setItem('username', username.value)
            } else {
                localStorage.removeItem('username');
            }
        })
    </script>
  <body>
    账号:<input type="text" placeholder="请输入账号" />
    <br />
    <br />
    密码:<input type="password" placeholder="请输入密码" />
    <hr />
    <input type="checkbox" />记住账号密码
    <script>
      // 给记住我 绑定单击事件,单机的时候获取表单数据,将表单数据存到localstorage中
      document.querySelector("input[type='checkbox']").addEventListener("click", function () {
        // 2.判断单前复选框是否选中,通过checked,如果该值是true,表示选中 false 表示未选中
        if (this.checked) {
          // 2.1 如果选中,就获取表单数据,将表单数据存入localstorage中
          var username = document.querySelectorAll("input")[0].value;
          var password = document.querySelectorAll("input")[1].value;
          localStorage.setItem("username", username);
          localStorage.setItem("password", password);
        } else {
          // 2.2 如果没有选中就删除localstorage中的数据
          localStorage.removeItem("username");
          localStorage.removeItem("password");
        }
      });
      //   页面加载完毕后将账户密码回显到输入框
      var username = localStorage.getItem("username");
      var password = localStorage.getItem("password");
      //如果本地有数据
      if (username) {
        document.querySelectorAll("input")[0].value = username;
        document.querySelectorAll("input")[1].value = password;
        document.querySelectorAll("input")[2].checked = true;
      }
    </script>
  </body>

案例

    <input type="text" id="username" />
    <input type="checkbox" id="remember" />是否记住用户名
    <br />
    <button>登录</button>

      var username = document.querySelector("#username"); // 输入框
      var remember = document.querySelector("#remember"); // 复选框
      var btn = document.querySelector("button"); // 登录

      // 加载页面时 判断 localstorage 中是否有数据 , 有数据就把值赋值给输入框 , 并勾选复选框
      if (localStorage.getItem("username")) {
        username.value = localStorage.getItem("username");
        remember.checked = true;
      }

      // 点击按钮判断 复选框有没被勾选,勾选上把输入框的值添加到 localstorage中 ,否知清除localstorage数据
      btn.onclick = function () {
        if (remember.checked) {
          localStorage.setItem("username", username.value);
        } else {
          localStorage.removeItem("username");
        }
      };

JSON 数据

JSON : 对象或数组的字符串展示形式

JSON : 存储数据 , 数据交换

前后端进行数据交互的时候 ,采用 JSON 格式的数据进行交换

  • JSON .stringify() // 将对象转为字符串
  • JSON .parse() // 将json字符串转为对象
      var obj = {
        name: "lll",
        age: 19,
        sex: "男",
      };

      // JSON.stringify() 将对象转为字符串
      localStorage.setItem("xx", obj);
      console.log(localStorage.getItem("xx"));

      console.log(localStorage.getItem("obj"));
      //  JSON.parse() 将json 字符串转为对象
      console.log(JSON.parse(localStorage.getItem("obj")));
      console.log(obj.name); // 111
      console.log(obj.age); // age

表单 change 事件

  • 获取复选框的选中转态
  • 获取表单的值
    <select name="" id="">
      <option value="1">山西</option>
      <option value="2">山东</option>
      <option value="3">陕西</option>
      <option value="4">上海</option>
    </select>
    <script>
      var select = document.querySelector("select");
      // 获取下拉单的值 通过change事件
      // selsect 的值 value 是option里面的 value 属性的值
      select.addEventListener("change", function () {
        console.log(this.value); // 1-2-3-4
      });
    </script>
  • 表单里数据的重置方法 reset()
$("#myForm").reset() 方法可把表单中的元素重置为它们的默认值

你可能感兴趣的:(javascript,前端)