目录

一、Web APIs简介

1 Web APIs和JS基础的关联性

2 MDN详细API 网址

二、DOM

1 什么是DOM

1.1 DOM树

2 获取页面元素

2.1 根据ID获取

2.2 根据标签名获取

2.3 通过HTML5新增的方法获取

2.4 获取特殊元素 body html

3 事件基础

3.1 事件三要素

3.2 执行事件的步骤

4 DOM操作元素

4.1 改变元素内容

4.2 innerText 和 innerHTML 的区别

4.3 常用元素的属性操作

4.4 案例 分时显示不同照片,显示不同问候语

4.5 表单元素的属性操作

4.6 案例 仿京东显示隐藏密码明文

4.7 样式属性操作

三、操作元素案例

1 案例 仿淘宝关闭二维码案例

2 案例 排他思想(算法)

3 案例 百度换肤

4 案例 表格隔行变色效果

5 案例 全选与取消全选

四、自定义属性

1 获取自定义属性值

2 设置属性值

3 移除属性

4 案例 tab栏切换  重点

5 H5自定义属性

五、节点操作

1 节点操作之父节点

2 子节点

2.1    1. 第一种 parentNode.childNodes  (标准)

2.2    2. 第二种 parentNode.children (非标准)

2.3 子节点之第一个子元素和最后一个子元素  不管是文本节点还是元素节点

2.4 案例 新浪下拉菜单

3 兄弟节点

4 创建和添加节点

5 案例-简单版发布留言案例

6 删除节点

7 案例 删除留言

8 复制节点(克隆节点)

9 案例 动态生成表格

六、事件高级

1 注册事件

1.1 传统注册方式

1.2 方法监听注册方式

2 删除事件

3 DOM事件流

3.1 DOM事件流代码验证

4 事件对象

4.1 常见事件对象属性和方法

4.2 e.target和this的区别

4.3 阻止默认行为

5 阻止事件冒泡

6 事件委托(代理、委派)

7 常用的鼠标事件

7.1 鼠标事件对象

7.2 案例-跟随鼠标的天使

8 常用的键盘事件

8.1 keyCode 判断用户按下了哪个键

9 案例-模拟京东按键输入内容

10 案例-模拟京东快递单号查询

七、BOM

1 BOM概述

1.1 BOM的构成

2 window对象的常见事件

2.1 窗口加载事件

2.2 调整窗口大小事件

3 定时器

3.1 两种定时器

3.2 setTimeout()定时器

3.3 案例 5秒钟后自动关闭广告

3.4 停止setTimeout()定时器

3.5 setInterval()定时器

3.6 案例 计时器效果

3.7 停止setInterval()定时器

3.8 案例 发送短信案例

3.9 this指向问题

4 JS执行机制

4.1 JS是单线程

4.2 同步和异步

4.3 JS执行机制

5 location对象

5.1 location对象属性

5.2 案例 5秒钟之后自动跳转页面

5.3 案例 location对象常见的方法

6 navigator对象

7 history对象

八、PC端网页特效

1 元素偏移量offset系列

1.1 offset概述

1.2 offset和style的区别

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

1.4 案例 模态框拖拽

2 元素可视区client系列

2.1 立即执行函数

2.2 淘宝 flexible.js源码分析

3 元素滚动scroll系列

3.1 元素滚动scroll系列

3.2 页面被卷去头部

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

3.4 页面被卷去头部兼容性解决方案

3.5 mouseenter和mouseover的区别

4 动画函数封装

4.1 动画实现原理

4.2 简单动画函数封装

4.3 给不同对象添加不同计时器

4.4 缓动动画原理

4.5 动画函数添加回调函数

4.6 动画函数封装到单独JS文件里面

5 常见网页特效案例

5.1 案例-网页轮播图

5.2 节流阀以及逻辑中断应用

5.3 案例-返回顶部

5.4 案例-筋斗云

九、移动端网页特效

1 触屏事件

1.1 触屏事件概述

1.2 触摸事件对象(TouchEvent)

1.4 移动端拖动元素

2 移动端常见特效

2.1 classList属性

2.2 案例-返回顶部

2.3 移动端click事件300ms延时问题解决方案

3 移动端常用开发插件

3.1 什么是插件

3.2 Swiper插件的使用

3.3 其他常见插件

3.4 插件使用总结

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

4 移动端常用开发框架

4.1 框架概述

4.2 bootstrap轮播图

十、本地存储

1 本地存储

2 window.sessionStorage

3 window.localStorage

4 案例-记住用户名


一、Web APIs简介

1 Web APIs和JS基础的关联性

Web APIs阶段:
        Web APIs 是w3c组织的标准
        Web APIs 我们主要学习 DOM 和 BOM
        Web APIs 是我们JS所独有的部分
        我们主要学习页面交互功能
        需要使用JS基础的课程内容做基础
    API :
    API(Application Programming Interface)之主要目的是提供应用程序与开发人员以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。提供API所定义的功能的软件称作此API的实现。API是一种接口,故而是一种抽象。
    是给程序员提供的一种工具,以便能更轻松的实现想要完成的功能。
    Web APIs:
    Web APIs 是浏览器提供的一套操作浏览器功能和页面元素的API(DOM 和 BOM)。现阶段我们主要针对于浏览器讲解常用的API,主要针对浏览器做交互效果。

2 MDN详细API 网址

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

二、DOM

1 什么是DOM

文档对象模型(Doucument Object Model,简称DOM),是w3c组织推荐的处理可扩展标记语言(HTML或者XML)的标准编程接口。
    W3C已经定义了一系列的DOM接口,通过这些DOM接口可以改变网页的内容、结构和样式。

1.1 DOM树


    文档:一个页面就是一个文档,DOM中使用document表示
    元素:页面中的所有标签都是元素,DOM中使用element表示
    节点:网页中的所有内容都是节点(标签、属性、文本、注释等),DOM中使用node表示。

2 获取页面元素

2.1 根据ID获取

使用getElementById()的方法可以获取带有ID的元素对象。

    <div id="time"> 2022 </div><script>// 1. 因为我们文档页面从上往下加载,所以先得有标签 所以script写到标签下面// 2. get 获取element 元素 by 通过   驼峰命名法// 3. 参数 id 是大小写敏感的字符串// 4. 返回的是一个元素对象var timer = document.getElementById('time');console.log(timer);console.log(typeof timer);// 5. console.dir 打印我们返回的元素对象  更好的查看里面的属性和方法console.dir(timer);</script>

2.2 根据标签名获取

getElementsByTagName()方法可以返回带有指定标签名的对象的集合。

    <body><ul><li> 许嵩11 </li><li> 许嵩22 </li><li> 许嵩33 </li><li> 许嵩44 </li><li> 许嵩55 </li></ul><ol id="ol"><li> 嵩11 </li><li> 嵩22 </li><li> 嵩33 </li><li> 嵩44 </li><li> 嵩55 </li></ol><script>// 1. 返回的是 获取过来的元素对象的集合 以伪数组的形式存储的var lis = document.getElementsByTagName('li');console.log(lis);console.log(lis[0]);// 2. 我们想要依次打印里面的元素对象我们可以采取遍历的方式  得到元素是动态的for (var i = 0; i < lis.length; i++){console.log(lis[i]);}// 3. 如果页面中只有一个li  返回的还是伪数组的形式// 4. 如果页面中没有这个元素 返回的是一个空的伪数组// 5. 还可以获取某个元素(父元素)内部所有指定标签名的子元素。 element.getElementsByTagName('标签名');// 6. 注意:父元素必须是单个对象(必须指明是哪一个元素对象)获取的时候不包括父元素自己//var ol = document.getElementsByTagName('ol');//console.log(ol[0].getElementsByTagName('li'));var ol = document.getElementById('ol');console.log(ol.getElementsByTagName('li'));</script></body>

2.3 通过HTML5新增的方法获取

 <body><div class = "box"> 盒子 </div><div class = "box"> 盒子 </div><div id = "nav"><ul><li> 首页 <li><li> 产品 <li></ul></div><script>// 1. document.getElementsByClassName('类名');根据类名返回元素对象集合var boxs = document.getElementsByClassName('box');console.log(boxs);// 2. document.querySelector('选择器'); 返回指定选择器的第一个元素 切记 里面的选择器需要加符号  类加.  .box id加#   #navvar firstBox = document.querySelector('.box');console.log(firstBox);var nav = document.querySelector('#nav');console.log(nav);var li = document.querySelector('li');console.log(li);// 3. document.querySelectorAll('选择器'); 根据指定选择器返回所有元素对象 var allBox = document.querySelectorAll('.box');console.log(allBox);var lis = document.querySelectorAll('li');console.log(lis);</script></body>

2.4 获取特殊元素 body html

 <body><script>// 1. 获取body元素var bodyEle = document.body;console.log(bodyEle);console.dir(bodyEle);// 2. 获取html元素//var htmlEle = document.html; // undefinedvar htmlEle = document.documentElement;console.log(htmlEle);  </script></body>

3 事件基础

3.1 事件三要素

JavaScript使我们有能力创建动态页面,而事件是可以被JavaScript侦测到的行为。
    简单理解: 触发——响应机制
    网页中的每个元素都可以产生某些可以触发JavaScript的事件,例如,我们可以在用户点击某一个按钮时产生一个事件,然后去执行某些操作。

 <body><button>许嵩</button><script>//  点击一个按钮,弹出对话框// 1. 事件是由三部分组成 事件源 事件类型 事件处理程序 我们也称为事件三要素// (1) 事件源 事件被触发的对象   谁  按钮var btn = document.getElementById('btn');// (2) 事件类型  如何触发  什么事件 比如鼠标点击(onclick) 还是鼠标经过 还是键盘按下// (3) 事件处理程序 通过一个函数赋值的方式 完成btn.onclick = function(){alert('许嵩');}</script></body>

3.2 执行事件的步骤

1.获取事件源
    2.注册事件(绑定事件)
    3.添加事件处理程序(采取函数赋值形式)

 <body><div>许嵩</div><script>//  执行事件步骤// 点击div控制台输出 许嵩// (1) 获取事件源 var div = document.querySelector('div');// (2) 事件类型  注册事件(绑定事件)// div.onclick// (3) 事件处理程序 通过一个函数赋值的方式 div.onclick = function(){alert('许嵩');}</script></body>

4 DOM操作元素

DOM操作可以改变网页的内容、结构和样式。我们可以利用DOM操作元素来改变元素里面的内容、属性等。

4.1 改变元素内容

element.innerText
    从起始位置到终止位置的内容,但它去除html标签,同时空格和换行也会去掉
    element.innerHTML
    起始位置到终止位置的全部内容,包括html标签,同时保留空格和换行

 <body><button> 许嵩 显示当前系统时间 </button><div> 某个时间 </div><p> 123 </p><script>//  当点击了按钮,div里面的文字会发生变化// (1) 获取元素 var btn = document.querySelector('button');var div = document.querySelector('div');// (2) 事件类型  注册事件(绑定事件)btn.onclick = function(){//div.innerText = '2022-7-24';div.innerText = getDate();}function getDate(){var date = new Date();// 我们写一个 2019年 5月 1日 星期三var year = date.getFullYear();var month = date.getMonth() + 1;var dates = date.getDate();var arr = ['星期日','星期一','星期二','星期三','星期四','星期五','星期六'];var day = date.getDay();return '今天是:' + year + '年' + month + '月' + date + '日' + arr[day];}// 我们元素可以不用添加事件var p = document.querySelector('p');p.innerText = getDate();</script></body>

4.2 innerText 和 innerHTML 的区别

 <body><button> 许嵩 显示当前系统时间 </button><div> 某个时间 </div><p> 我是文字<span> 123 </span> </p><script>// innerText 和 innerHTML 的区别// 1.innerText  不识别html标签  非标准var div = document.querySelector('div');div.innerText = '<strong> 今天是:</strong> 2022';// 1.innerHTML 识别html标签 W3c标准 会保留空格和换行div.innerHTML = '<strong> 今天是:</strong> 2022';// 这两个属性是可读写的  可以获取元素里面的内容var p = document.querySelector('p');console.log(p.innerText);console.log(p.innerHTML); // 会保留空格</script></body>

4.3 常用元素的属性操作

element.innerHTML   element.innerText    改变元素内容
    src href
    id alt title

 <body><button id = "xs"> 许嵩1 </button><button id = "ss" > 许嵩2 </button><img src = "images/xs.jpg" alt = "xs许嵩" title="许嵩1"><script>// 修改元素属性  src// (1) 获取元素 var xs = document.getElementById('xs');var ss = document.getElementById('ss');var img = document.querySelector('img');// (2) 事件类型  注册事件(绑定事件)ss.onclick = function(){img.src = 'images/ss.jpg';img.title = "许嵩2";}xs.onclick = function(){img.src = 'images/xs.jpg';img.title = "许嵩1";}</script></body>

4.4 案例 分时显示不同照片,显示不同问候语

如果上午时间打开页面,显示上午好,显示上午的图片
    如果下午时间打开页面,显示下午好,显示下午的图片
    如果晚上时间打开页面,显示晚上好,显示晚上的图片

案例分析:
    根据系统不同时间来判断,所以需要用到日期内置对象
    利用多分支语句来设置不同的图片
    需要一个图片,并且根据时间修改图片,就需要用到操作元素src属性
    需要一个div元素,显示不同问候语,修改元素内容即可

 <body><button> 许嵩 显示当前系统时间 </button><img src = "images/swh.gif" alt = "上午好" title="上午好">//<img src = "images/xwh.gif" alt = "下午好" title="下午好">//<img src = "images/wsh.gif" alt = "晚上好" title="晚上好"><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/swh.gif';div.innnerHTML = '亲,上午好';}else if(h < 18){img.src = 'images/xwh.gif';div.innnerHTML = '亲,下午好';}else {img.src = 'images/wsh.gif';div.innnerHTML = '亲,晚上好';}</script></body>

4.5 表单元素的属性操作

type value checked selected disabled

 <body><button> 许嵩 </button><input type="text" value="输入内容"><script>// (1) 获取元素 var btn = document.querySelector('button');var input = document.querySelector('input');// (2) 注册事件 处理程序btn.onclick = function(){// input.innerHTML = '被点击了'; // 这个是普通盒子 比如 div标签里面的内容// 表单里面的值  文字内容是通过 value 来修改的input.value='被点击了';// 如果想要某个表单被禁用 不能再点击 disabled 我们想要这个按钮 button 禁用//btn.disabled = true;this.disabled = true;// this指向的是事件函数的调用者 btn}</script></body>

4.6 案例 仿京东显示隐藏密码明文

核心思路:
    点击眼睛按钮,把密码框类型改为文本框就可以看见里面的密码
    一个按钮两个状态,点击一次,切换为文本框,继续点击切换为密码框
    算法:
    利用一个flag变量,来判断flag的值,如果是1就切换为文本框,flag设置为0,如果是0就切换为密码框,flag设置为1

 <!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>.box {position: relative;width: 400px;border-bottom: 1px solid #ccc;margin: 100px auto;}.box input {width: 370px;height: 30px;border: 0;outline: none;}.box img {position: absolute;top: 2px;right: 2px;width: 24px;}</style></head><body><div class="box"><label for=""><img src="data: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. 注册事件 处理程序var flag = 0;eye.onclick = function() {// 点击一次之后,flag 一定要变化if (flag == 0) {pwd.type = 'text';eye.src = 'images/open.ong';flag = 1; // 赋值操作} else {pwd.type = 'password';eye.src = 'images/close.ong';flag = 0;}}</script></body></html>

4.7 样式属性操作

我们可以通过JS修改元素的大小、颜色、位置等样式、
    element.style  行内样式操作

 <body><div style="width:100px; height:200px;background-color:pink;"></div> <script>// 1. 获取元素var div = document.querySelector('div');// 2. 注册事件 处理程序div.onclick = function() {// div.style 里面的属性 采取驼峰命名法this.style.backgroundColor = 'blue';this.style.width = '200px';// JS修改style样式操作,产生的是行内样式,css权重比较高}</script></body>element.className 类名样式操作<style> .change{background-color:purple;color:#fff;font-size:25px;margin-top:100px;}</style><body><div style="width:100px; height:100px;background-color:pink;"  class="first">许嵩</div> <script>// 1. 使用element.style 获取修改元素样式 如果样式比较少 或者 功能简单的情况下使用var test = document.querySelector('div');// 2. 注册事件 处理程序test.onclick = function() {this.className = 'change';// 如果样式修改较多,可以采取操作类名方式更改元素样式// class因为是个保留字,因此使用className来操作类名属性// className 会直接更改元素的类名,会覆盖原先的类名// 如果想要保留原先的类名,可以 多类名选择器this.className = 'first change';}</script></body>

三、操作元素案例

1 案例 仿淘宝关闭二维码案例

核心思路:
    利用样式的显示与隐藏完成,display:none隐藏元素  display:block显示元素
    点击按钮,就让这个二维码盒子隐藏起来

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>* {margin: 0;padding: 0;}.erweima {position: relative;width: 160px;height: 160px;margin: 100px auto;border: 1px solid #ccc;}.erweima i {position: absolute;left: -13px;top: 0;width: 10px;height: 10px;border: 1px solid #ccc;font-size: 12px;line-height: 10px;color: #ccc;font-style: normal;cursor: pointer;}</style>
</head><body><div class="erweima"><img src="./images/code.png" alt=""><i class="close_btn">x</i></div><script>// 1. 获取元素   事件源 i   关闭的 erweima let close_btn = document.querySelector('.close_btn')let erweima = document.querySelector('.erweima')// 2. 事件监听close_btn.addEventListener('click', function () {// erweima 关闭  他是隐藏的erweima.style.display = 'none'})</script>
</body></html>

2 案例 排他思想(算法)

 <body><button>许嵩1</button><button>许嵩2</button><button>许嵩3</button><button>许嵩4</button><button>许嵩5</button><script>// 1.获取所有按钮元素var btns = document.getElementsByTagName('button');// btns 得到的是伪数组 里面的每一个元素 btns[i]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) 然后才让当前元素背景颜色为pink 留下我自己this.style.backgroundColor = 'pink';}}// 2.首先 先排除其他人 然后才设置自己的样式 这种排除他人的思想我们称为 排他思想</script></body>

3 案例 百度换肤

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

 <style>*{margin:0;padding:0;}body{background:url(images/1.jpg) no-repeat center top;}li{list-style:none;}.baidu{overflow:hidden;margin:100px auto;background-color:#fff;width:410px;padding-top:3px;}.baidu li{float:left;margin:0 1px;cursor:pointer;}.baidu img{width:100px;}</style>
 <body><ul class="baidu"><li><img src="data:images/1.jpg"></li><li><img src="data:images/2.jpg"></li><li><img src="data:images/3.jpg"></li><li><img src="data:images/4.jpg"></li></ul><script>// 1.获取元素var imgs = document.querySelector('.baidu').querySelectorAll('img');// 2.循环注册事件for(var i = 0; i < imgs.length; i++){imgs[i].onclick = function(){// this.src 就是我们点击图片的路径// console.log(this.src);// 把这个路径 this.src 给body就可以了document.body.style.backgroundImage = 'url(' + this.src + ')'; }}</script></body>

4 案例 表格隔行变色效果

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

 <style>table{width:800px;margin:100px auto;text-align:center;border-collapse:collapse;font-size:14px;}thead tr{height:30px;background-color:skyblue;}tbody tr{height:30px;}tbody td{border-bottom:1px solid #d7d7d7;font-size:12px;color:blue;}.bg{background-color:pink;}</style>
 <body><table><thead><tr><th>代码</th><th>名称</th><th>最新公布净值</th><th>累计净值</th><th>前单位净值</th><th>净值增长率</th></tr></thead><tbody><tr><td>003526</td><td>农银金穗3个月定期开放债券</td><td>1.075</td><td>1.079</td><td>1.074</td><td>+0.047%</td></tr><tr><td>003526</td><td>农银金穗3个月定期开放债券</td><td>1.075</td><td>1.079</td><td>1.074</td><td>+0.047%</td></tr><tr><td>003526</td><td>农银金穗3个月定期开放债券</td><td>1.075</td><td>1.079</td><td>1.074</td><td>+0.047%</td></tr><tr><td>003526</td><td>农银金穗3个月定期开放债券</td><td>1.075</td><td>1.079</td><td>1.074</td><td>+0.047%</td></tr><tr><td>003526</td><td>农银金穗3个月定期开放债券</td><td>1.075</td><td>1.079</td><td>1.074</td><td>+0.047%</td></tr></tbody></table><script>// 1. 获取元素 获取的是 tbody 里面所有的行var trs = document.querySelector('tbody').querySelectorAll('tr');// 2.利用循环绑定注册事件for(var i = 0; i < trs.length; i++){// 3. 鼠标经过事件  onmouseovertrs[i].onmouseover = function(){//console.log(11);this.className = 'bg';}// 4.鼠标离开事件 onmouseouttrs[i].onmouseout = function(){this.className = '';}}</script></body>

5 案例 全选与取消全选

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

    <div class="wrap"><table><thead><tr><th><input type="checkbox" id="j_cbAll" /></th><th>商品</th><th>价钱</th></tr></thead><tbody id="j_tb"><tr><td><input type="checkbox" /></td><td>iphone13</td><td>7000</td></tr><tr><td><input type="checkbox" /></td><td>iphone13</td><td>7000</td></tr><tr><td><input type="checkbox" /></td><td>iphone13</td><td>7000</td></tr><tr><td><input type="checkbox" /></td><td>iphone13</td><td>7000</td></tr></tbody></table></div>
<script>// 1. 全选和取消全选做法: 让下面所有复选框的checked属性(选中状态)跟随全选按钮即可。// 获取元素var j_cbAll = document.getElementById('j_cbAll'); // 全选按钮var j_tbs = document.getElementById('j_tb').getElementsByTagName('input'); // 下面所有的复选框// 注册事件j_cbAll.onclick = function() {// this.checked 可以得到当前复选框的选中状态 如果是true就是选中  false就是未选中console.log(this.checked);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;break; // 退出for循环 这样可以提高执行效率 因为只要有一个没有选中 剩下的就无需循环判断了}}}j_cbAll.checked = flag;}}</script>

四、自定义属性

1 获取自定义属性值

获取属性值
    element.属性  获取属性值
    element.getAttribute('属性');

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

 <body><div id="demo" index="1" class="nav"></div><script>var div = document.querySelector('div');// 1. 获取元素的属性值// (1) element.属性console.log(div.id);// (2) element.getAttribute('属性'); get得到属性 attribute 属性的意思 程序员自己添加的属性 称为自定义属性 indexconsole.log(div.getAttribute('id'));console.log(div.getAttribute('index'));// 2. 设置元素属性值// (1) element.属性 = '值'div.id = 'test';div.className = 'navs';// (2) element.getAttribute('属性','值'); 主要针对于自定义属性值div.getAttribute('index',2);div.getAttribute('class','footer');// 3. 移除属性  element.removeAttribute('属性');div.removeAttribute('index');</script></body>

2 设置属性值

element.属性 = '值'
    element.getAttribute('属性','值'); 主要针对于自定义属性值

3 移除属性

element.removeAttribute('属性');

4 案例 tab栏切换  重点

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


<!DOCTYPE html>
<html><head lang="en"><meta charset="UTF-8" /><title></title><style type="text/css">* {margin: 0;padding: 0;}ul {list-style: none;}.wrapper {width: 1000px;height: 475px;margin: 0 auto;margin-top: 100px;}.tab {border: 1px solid #ddd;border-bottom: 0;height: 36px;width: 320px;}.tab li {position: relative;float: left;width: 80px;height: 34px;line-height: 34px;text-align: center;cursor: pointer;border-top: 4px solid #fff;}.tab span {position: absolute;right: 0;top: 10px;background: #ddd;width: 1px;height: 14px;overflow: hidden;}.products {width: 1002px;border: 1px solid #ddd;height: 476px;}.products .main {float: left;display: none;}.products .main.active {display: block;}.tab li.active {border-color: red;border-bottom: 0;}</style>
</head><body><div class="wrapper"><ul class="tab"><li class="tab-item active">国际大牌<span>◆</span></li><li class="tab-item">国妆名牌<span>◆</span></li><li class="tab-item">清洁用品<span>◆</span></li><li class="tab-item">男士精品</li></ul><div class="products"><div class="main active"><a href="###"><img src="imgs/guojidapai.jpg" alt="" /></a></div><div class="main"><a href="###"><img src="imgs/guozhuangmingpin.jpg" alt="" /></a></div><div class="main"><a href="###"><img src="imgs/qingjieyongpin.jpg" alt="" /></a></div><div class="main"><a href="###"><img src="imgs/nanshijingpin.jpg" alt="" /></a></div></div></div><script>// 0. 获取元素// 得到所有的小li  let lis = document.querySelectorAll('.tab .tab-item')let divs = document.querySelectorAll('.products .main')// 1. 头部tab栏切换模块// 1.1 先给4个小li添加点击事件for (let i = 0; i < lis.length; i++) {lis[i].addEventListener('click', function () {// console.log(11)// 找到以前的active 类,移除掉 document.querySelector('.tab .active').classList.remove('active')// 当前的元素添加this.classList.add('active')// 2. 底部显示隐藏模块  一定要写到点击事件的里面document.querySelector('.products .active').classList.remove('active')// div对应序号的那个加上active divs[i].classList.add('active')})}</script></body></html>

5 H5自定义属性

自定义属性目的:是为了保存并使用数据。有些数据可以保存到页面中而不用保存到数据库中。
    自定义属性获取是通过getAttribute('属性')获取。
    但是有些自定义属性很容易引起歧义,不容易判断是元素的内置属性还是自定义属性。

H5自定义属性:
    H5规定自定义属性data-开头作为属性名并赋值
    比如 <div data-index = "1"></div>
    或者使用JS设置
    element.setAttribute('data-index',2)

获取H5自定义属性:
    兼容性获取  element.getAttribute('data-index');
    H5新增获取方法 element.dataset.index  或者 element.dataset["index"]

五、节点操作

网页中所有内容都是节点(标签、属性、文本、注释等),在DOM中 节点用node表示。
    HTML DOM树中的所有节点均可通过JavaScript进行访问,所有HTML元素(节点)均可被修改,也可以创建或删除。

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

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

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

1 节点操作之父节点

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

 <body><!-- 节点的有点 --><div>div</div><span>span</span><ul><li>li</li><li>li</li><li>li</li><li>li</li></ul><div class="demo"><div class="box"><span class="erweima">x</span></div></div><script>//var box = document.querySelector('.box');//console.dir(box);// 1.父级节点 node.parentNodevar erweima = document.querySelector('.erweima');//var box = document.querySelector('.box');// 得到的是离元素最近的父级节点(亲爸爸) 如果找不到父节点就返回nullconsole.log(erweima.parentNode);</script></body>

2 子节点

2.1    1. 第一种 parentNode.childNodes  (标准)

 <body><!-- 节点的有点 --><div>div</div><span>span</span><ul><li>li</li><li>li</li><li>li</li><li>li</li></ul><ol><li>li</li><li>li</li><li>li</li><li>li</li></ol><div class="demo"><div class="box"><span class="erweima">x</span></div></div><script>// DOM 提供的方法(API) 获取var ul = document.querySelector('ul');var lis = ul.querySelectorAll('li');// 2. 子节点  childNodes 所有的子节点 包含 元素节点 文本节点等console.log(ul.childNodes);console.log(ul.childNodes[0].nodeType);console.log(ul.childNodes[1].nodeType);// 3. 子节点 第二种 parentNode.childrenconsole.log(ul.children);// 4. 子节点之第一个子元素和最后一个子元素// 不管是文本节点还是元素节点console.log(ol.firstChild);console.log(ol.lastChild);// 5. firstElementChild  返回第一个子元素节点console.log(ol.firstElementChild);// 6. 实际开发 写法 既没有兼容性问题又返回第一个元素 最后一个元素console.log(ol.children[0]);console.log(ol.children[ol.children.length - 1]);</script></body>注意:返回值里面包含了所有的子节点,包括元素节点,文本节点等如果只想要获得里面的元素节点,则需要专门处理。所以我们一般不提倡使用childNodes  

2.2    2. 第二种 parentNode.children (非标准)

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

2.3 子节点之第一个子元素和最后一个子元素  不管是文本节点还是元素节点

找不到返回null
    parentNode.firstChild
    parentNode.lastChild

还有一种 不过有兼容性问题 ie9以上支持
    parentNode.firstElementChild
    parentNode.lastElementChild

实际开发 写法 既没有兼容性问题又返回第一个元素

2.4 案例 新浪下拉菜单

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

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style></style>
</head><body><ul class="nav"><li><a href="#">微博</a><ul><li><a href="">评论</a></li><li><a href="">@我</a></li></ul></li><li><a href="#">微博</a><ul><li><a href="">评论</a></li><li><a href="">@我</a></li></ul></li><li><a href="#">微博</a><ul><li><a href="">评论</a></li><li><a href="">@我</a></li></ul></li></ul><script>// 1. 获取元素var nav = document.querySelector('.nav');var lis = nav.children; //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>
</body></html>

3 兄弟节点

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><div>div</div><span>span</span><script>var div = document.querySelector('div');// 1. nextSibling 下一个兄弟节点 包含所有节点console.log(div.nextSibling);// previousSibling 上一个兄弟节点console.log(div.previousSibling);// 2. nextElementSibling  返回当前元素下一个兄弟元素节点  有兼容问题 ie9以上console.log(div.nextElementSibling);// previousElementSibling 返回当前元素上一个兄弟元素节点console.log(div.previousElementSibling);// 问: 如何解决兼容性问题// 答: 自己封装一个兼容性函数 怎么分装呢function getNextElementSibling(element) {var el = element;while (el = el.nextSibling) {if (el.nodeType === 1) {return el;}}return null;}</script>
</body></html>

4 创建和添加节点

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

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

添加指定子元素
    node.insertBefore(child,指定元素);

<body><ul><li>123</li></ul><script>// 1. 创建节点  创建一个li在ul里var li = document.createElement('li');// 2. 添加节点  node.appendChild(child)  node 父级  child 子级var ul = document.querySelector('ul');ul.appendChild(li);// 3. 添加指定子元素 node.insertBefore(child,指定元素);var lili = document.createElement('li');ul.insertBefore(lili, ul.children[0]);</script>
</body>

5 案例-简单版发布留言案例

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

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><textarea name="" id="" cols="30" rows="10">123</textarea><button>发布</button><ul><li>123</li></ul><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 {//console.log(text.value);// (1) 创建元素var li = document.createElement('li');// 先有li 才能赋值li.innerHTML = text.value + "<a href='javascript:;'>删除</a>";// (2) 添加元素//ul.appendChild(li);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 当前a 所在的li this.parentNodeul.removeChild(this.parentNode);}}}}</script>
</body></html>

6 删除节点

node.removeChild(child)

<body><button>删除</button><ul><li>许嵩</li><li>许</li><li>嵩</li></ul><script>// 1. 获取元素var ul = document.querySelector('ul');var btn = document.querySelector('button');// 2. 删除元素  node.removeChild(child)//ul.removeChild(ul.children[0]);// 3. 点击按钮依次删除里面的孩子btn.onclick = function() {if (ul.children.length == 0) {this.disabled = true;} else {ul.removeChild(ul.children[0]);}}</script>
</body>

7 案例 删除留言

案例分析:
    当我们把文本域里面的值赋值给li的时候,多添加一个删除的链接
    需要把所有的链接获取过来,当我们点击当前的链接的时候,删除当前链接所在的li
    阻止链接跳转需要添加javascript:void(0) 或者 javascript:;

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><textarea name="" id="" cols="30" rows="10">123</textarea><button>发布</button><ul><li>123</li></ul><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 {//console.log(text.value);// (1) 创建元素var li = document.createElement('li');// 先有li 才能赋值li.innerHTML = text.value + "<a href='javascript:;'>删除</a>";// (2) 添加元素//ul.appendChild(li);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 当前a 所在的li this.parentNodeul.removeChild(this.parentNode);}}}}</script>
</body></html>

8 复制节点(克隆节点)

node.cloneNode()

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

<body><ul><li>1</li><li>2</li><li>3</li></ul><script>// 1.  复制节点(克隆节点)node.cloneNode() 浅拷贝var ul = document.querySelector('ul');var lili = ul.children[0].cloneNode();ul.appendChild(lili);// 2. node.cloneNode(true)   深拷贝</script>
</body>

9 案例 动态生成表格

案例分析:
    1.因为里面的学生数据都是动态的,我们需要js动态生成。
    2.这里我们模拟数据,自己定义好数据。
    3.数据采取对象形式存储。
    4.所有的数据都是放到tbody里面的行里面。
    5.因为行很多,我们需要循环创建多个行(对应多少人)
    5.每个行里面又有很多单元格(对应里面的数据),我们还继续使用循环创建多个单元格,并且把数据存入里面(双重for循环)
    6.最后一列单元格是删除,需要单独创建单元格

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>table {width: 500px;margin: 100px auto;border-collapse: collapse;text-align: center;}td,th {border: 1px solid #333;}thead tr {height: 40px;background-color: #ccc;}</style>
</head><body><table cellspacing="0"><thead><tr><th>姓名</th><th>科目</th><th>成绩</th><th>操作</th></tr></thead><tbody><!-- <tr><td>小吴</td><td>JavaScript</td><td>100</td><td><a href="#">删除</a></td></tr><tr><td>小曾</td><td>JavaScript</td><td>100</td><td><a href="#">删除</a></td></tr><tr><td>小海</td><td>JavaScript</td><td>100</td><td><a href="#">删除</a></td></tr> --></tbody></table><script>// 1. 先准备好学生的数据// var datas = {//     name: '小吴',//     subject: 'javascript',//     score: 100// }var datas = [{name: '小吴',subject: 'javascript',score: 100}, {name: '小z',subject: 'javascript',score: 99}, {name: '小l',subject: 'javascript',score: 98}, {name: '小s',subject: 'javascript',score: 100}];// 2. 往tbody里面创建行:有几个人(通过数组的长度)就创建几行var tbody = document.querySelector('tbody');// 外面的for循环管行 trfor (var i = 0; i < datas.length; i++) {// 1. 创建 tr 行var tr = document.createElement('tr');tbody.appendChild(tr);// 2. 行里面创建单元格(跟数据有关系的3个单元格) td 单元格的数量取决于每个对象里面的属性个数 for循环遍历对象// 里面的for循环管列 td// for (var k in obj)for (var k in datas[i]) {// 创建单元格var td = document.createElement('td');// 把对象里面的属性值 给 td// console.log(datas[i][k]); // 打印给控制台看一下td.innerHTML = datas[i][k];tr.appendChild(td);}// 3. 创建有删除两个字的单元格var td = document.createElement('td');td.innerHTML = '<a href="javascript:;">删除</a>';tr.appendChild(td);}// 4. 删除操作 开始var as = document.querySelectorAll('a');for (var i = 0; i < as.length; i++) {as[i].onclick = function() {// 点击a 删除当前a 所在的行(链接的爸爸的爸爸) node.removeChild(child)tbody.removeChild(this.parentNode.parentNode)}}// 回忆一下:// for (var k in obj) {//     k 得到的是属性名//     obj[k] 得到的是属性值// }</script>
</body></html>

六、事件高级

1 注册事件

给元素添加事件,称为注册事件或者绑定事件
    注册事件有两种方式:传统方式 和 方法监听注册方式

1.1 传统注册方式

传统注册方式:
    利用on开头的事件 onclick
    <button onclick = "alert('hi~')"> </button>
    btn.onclick = function(){}
    特点:注册事件的唯一性
    同一个元素同一个事件只能设置一个处理函数,最后注册的处理函数将会覆盖前面注册的处理函数。

1.2 方法监听注册方式

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

1.2.1 addEventListener() 事件监听方式

eventTarget.addEventListener(type,listener[,useCapture])
    eventTarget.addEventListener() 方法将指定监听器注册到eventTarget(目标对象)上,当该对象触发指定的事件时,就会执行事件处理函数。

该方法接收三个参数:
    type 事件类型字符串,比如click、mouseover,注意这里不要带on
    listener 事件处理函数,事件发生时,会调用该监听函数
    useCapture 可选参数,是一个布尔值,默认是false。

1.2.2 attachEvent()

eventTarget.attachEvent(eventNameWithOn,callback)
    eventTarget.attachEvent()方法将指定的监听器注册到eventTarget(目标对象)上,当该对象触发指定的事件时,指定的回调函数就会被执行。

该方法接收两个参数:
    eventNameWithOn 事件类型字符串,比如onclick、onmouseover,这里要带on
    callback 事件处理函数,当目标触发事件时回调函数被调用

代码:

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><button>传统注册事件</button><button>方法监听注册事件</button><button>ie9 attachEvent</button><script>var btns = document.querySelectorAll('button');// 1. 传统注册事件btns[0].onclick = function() {alert('hi~');}btns[0].onclick = function() {alert('ni  hao ~');}// 2. 事件侦听注册事件 addEventListener()// (1) 里面的事件类型是字符串 必定加引号 而且不带on// (2) 同一个元素 同一个事件可以添加多个侦听器 (事件处理程序)btns[1].addEventListener('click', function() {alert('22');})btns[1].addEventListener('click', function() {alert('32');})// 3. attachEvent ie9以前的的版本支持btns[2].attachEvent('oonclick', function() {alert(11);})// // 4. 注册事件兼容性解决方案// function addEventListener(element, evenName, fn) {//     // 判断当前浏览器是否支持 addEventListener方法//     if (element.addEventListener) {//         element.addEventListener(evenName, fn); // 第三个参数 默认false//     } else if (element.attachEvent) {//         element.attachEvent('on' + eventName, fn);//     } else {//         // 相当于 element.onclick = fn;//         element['on' + evenName] = fn;//     }// }</script>
</body></html>

2 删除事件

删除事件的方式
    1.传统注册方式
    eventTarget.onclick = null;

2.方法监听注册方式
    eventTarget.removeEventListener(type,listener[,useCapture])
    eventTarget.detachEvent(eventNameWithOn,callback);

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>div {width: 100px;height: 100px;background-color: pink;}</style>
</head><body><div>1</div><div>2</div><div>3</div><script>var divs = document.querySelectorAll('div');divs[0].onclick = function() {alert(11);// 1. 传统方式删除事件divs[0].onclick = null;}// 2. 方法监听注册方式divs[1].addEventListener('click', fn) // 里面的fn不需要调用加小括号function fn() {alert(22);// eventTarget.removeEventListener(type,listener[,useCapture])divs[1].removeEventListener('click', fn);}// 3. eventTarget.detachEvent(eventNameWithOn,callback);divs[2].attachEvent('onclick', fn1);function fn1() {alert(33);divs[2].detachEvent('onclick', fn1);}</script>
</body></html>

3 DOM事件流

事件流描述的是从页面接收事件的顺序。
    事件发生时会在元素节点之间按照特定的顺序传播,这个传播过程即DOM事件流。
    DOM事件流分为3个阶段:
    1.捕获阶段
    2.当前目标阶段
    3.冒泡阶段
    事件冒泡:IE最早提出,事件开始由最具体的元素接收,然后逐级向上传播到DOM最顶层节点的过程。
    事件捕获:网景最早提出,由DOM最顶层节点开始,然后逐级向下传播到最具体的元素接收的过程。

举例说明:向水里扔一块石头,首先它会有一个下降的过程,这个过程就可以理解为从最顶端向事件发生的最具体元素(目标点)的捕获过程;之后会产生泡泡,会在最低点(最具体元素)之后漂浮到水面上,这个过程相当于事件冒泡。

3.1 DOM事件流代码验证

事件发生时会在元素节点之间按照特定的顺序传播,这个传播过程即DOM事件流。
    注意:
    1.JS代码中只能执行捕获或者冒泡其中的一个阶段
    2.onclick和attachEvent只能得到冒泡阶段
    3.addEventListener(type,listener[,useCapture])第三个参数如果是true,表示在事件捕获阶段调用事件处理程序;如果时false(不写默认就是false),表示在事件冒泡阶段调用事件处理程序。
    4.实际开发中很少使用事件捕获,更关注事件冒泡
    5.有些事件是没有冒泡的,比如onblur、onfocus、onmouseenter、onmouseleave
    6.事件冒泡有时候会带来麻烦,时候又会帮助很巧妙的做某些事件,后面介绍

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>.father {position: relative;width: 300px;height: 300px;background-color: aqua;}.son {position: absolute;width: 200px;height: 200px;background-color: tomato;}</style>
</head><body><div class="father"><div class="son">son盒子</div></div><script>// DOM事件流  3个阶段// 1.JS代码中只能执行捕获或者冒泡其中的一个阶段// 2.onclick和attachEvent只能得到冒泡阶段// 3. 捕获阶段 如果addEventListener(type,listener[,useCapture])//    第三个参数如果是true,那么处于捕获阶段//    document --> html --> body --> father --> son -->var son = document.querySelector('.son');son.addEventListener('click', function() {alert('son');}, false);var father = document.querySelector('.father');father.addEventListener('click', function() {alert('father');}, false);document.addEventListener('click', function() {alert('document');})</script>
</body></html>

4 事件对象

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>div {width: 100px;height: 100px;background-color: purple;}</style>
</head><body><div>123</div><script>// 事件对象var div = document.querySelector('div');// 传统方式div.onclick = function(e) {console.log(e);console.log(window.event);// 兼容性写法//e = e || window.event;}//div.addEventListener('click', function(event) {//        console.log(event);//   })// 1.event 就是一个事件对象 写到侦听函数的 小括号里 当形参看// 2. 事件对象只有有了事件才会存在,它是系统给我们自动创建的,不需要我们传递参数// 3. 事件对象 是 我们事件的一系列相关数据的集合 跟事件相关的 比如鼠标点击里面就//    包含了鼠标的相关信息,鼠标坐标啊,如果是键盘事件里面就把包含键盘事件的信息//    比如 判断用户按下了哪个键// 4. 这个事件对象可以自己命名 event evt e 都可以// 5. 事件对象也有兼容性问题  ie678 通过 window.event</script>
</body></html>

4.1 常见事件对象属性和方法

事件对象属性方法            说明
    e.target                            返回触发事件的对象    标准
    e.srcElement                   返回触发事件的对象    非标准 ie6-8使用
    e.type                              返回事件的类型 比如 click mouseover 不带on
    e.canceBubble                该属性阻止冒泡   非标准ie6-8使用
    e.returnValue                  该属性阻止默认事件(默认行为) 非标准 ie6-8使用 比如不让链接跳转
    e.prevenDefault()            该方法阻止默认事件(默认行为) 标准 比如不让链接跳转
    e.stopPropagation()        阻止冒泡  标准

4.2 e.target和this的区别

e.target返回触发事件的对象(元素)
    this返回的是绑定事件的对象(元素)

e.target 点击了哪个元素,就返回哪个元素
    this就是哪个元素绑定了这个点击事件,就返回谁

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>div {width: 100px;height: 100px;background-color: palegreen;}</style>
</head><body><div>123</div><ul><li>abc</li><li>abc</li><li>abc</li></ul><script>// 常见事件对象的属性和方法// 1. e.target返回触发事件的对象(元素)// this返回的是绑定事件的对象(元素)// 2. 区别:e.target 点击了哪个元素,就返回哪个元素  //          this就是哪个元素绑定了这个点击事件,就返回谁var div = document.querySelector('div');div.addEventListener('click', function(e) {console.log(e.target);console.log(this);})var ul = document.querySelector('ul');ul.addEventListener('click', function(e) {// 我们给ul 绑定了事件 那么this 就指向ulconsole.log(this);// e.target 指向我们点击的那个对象 谁触发了这个事件 // 我们点击的是 li e.target指向的就是liconsole.log(e.target);})// 了解兼容性// div.onclick = function(e){//     e = e || window.event;//     var target = e.target || e.srcElement;//     console.log(target);// }// 了解 跟this有个非常相似的属性: currentTarget</script>
</body></html>

4.3 阻止默认行为

<body><div>123</div><a href="http://www.baidu.com">百度</a><form action="http://www.baidu.com"><input type="submit" value="提交" name="sub"></form><script>// 常见事件对象的属性和方法// 1.返回事件类型var div = document.querySelector('div');div.addEventListener('click', fn);div.addEventListener('mouseover', fn);div.addEventListener('mouseout', fn);function fn(e) {console.log(e.type);}// 2.阻止默认行为(事件) 让链接不跳转 按钮不提交var a = document.querySelector('a');a.addEventListener('click', function(e) {e.preventDefault(); // dom标准写法})// 3.传统的注册方式a.onclick = function(e) {// 普通浏览器  e.preventDefault();e.preventDefault();// 低版本浏览器 ie6768 returnValue;e.returnValue;// 我们可以利用return false 也能阻止默认行为 没有兼容性问题// 特点 : return后面的代码不执行了 而且只限于传统的注册方式return false;alert(11);}</script>
</body>

5 阻止事件冒泡

事件冒泡:事件开始由最具体的元素接收,然后逐级向上传播到DOM最顶层节点的过程。
    阻止事件冒泡的两种方式
    标准写法: e.stopPropagation();
    非标准ie6-8使用 e.canceBubble = true;  阻止冒泡

6 事件委托(代理、委派)

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

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

7 常用的鼠标事件

<body>我是一段不愿意分享的文字<script>// 1. contextmenu  禁用右键菜单document.addEventListener('contextmenu', function(e) {e.preventDefault();})// 2. selectstart 禁止选中文字 document.addEventListener('selectstart', function(e) {e.preventDefault();})</script>
</body>

7.1 鼠标事件对象

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

鼠标事件对象            说明
    e.clientX                    返回鼠标相对于浏览器窗口可视区的X坐标
    e.clientY                    返回鼠标相对于浏览器窗口可视区的Y坐标
    e.pageX                     返回鼠标相对于文档页面的X坐标 IE9+支持
    e.pageY                     返回鼠标相对于文档页面的Y坐标 IE9+支持
    e.srceenX                  返回鼠标相对于电脑屏幕的X坐标
    e.srceenY                  返回鼠标相对于电脑屏幕的Y坐标

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>body {height: 3000px;}</style>
</head><body><script>// 鼠标事件对象 MouseEventdocument.addEventListener('click', function(e) {// 1. client   返回鼠标相对于浏览器窗口 可视区 的坐标console.log(e.clientX);console.log(e.clientY);console.log('-----------------');})// 2. page 返回鼠标相对于文档页面的坐标 IE9+支持 常用console.log(e.pageX);console.log(e.pageY);console.log('------------------');// 3. srceenX  返回鼠标相对于电脑屏幕的坐标console.log(e.screenX);console.log(e.screenY);</script>
</body></html>

7.2 案例-跟随鼠标的天使

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

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>img {position: absolute;top: 2px;}</style>
</head><body><img src="data:images/angel.gif" alt=""><script>var pic = document.querySelector('img');document.addEventListener('mousemove', function(e) {var x = e.pageX;var y = e.pageY;console.log('x坐标是' + x, 'y坐标是' + y);// 不要忘记加单位pxpic.style.left = x - 50 + 'px';pic.style.top = y - 40 + 'px';})</script>
</body></html>

8 常用的键盘事件

事件除了使用鼠标触发,还可以使用键盘触发。

键盘事件            触发条件
    onkeyup        某个键盘按键被松开时触发
    onkeydown    某个键盘按键被按下时触发
    onkeypress    某个键盘按键被按下时 触发 但是它不识别功能键 比如ctrl shift 箭头等。
    注意:
    如果使用addEventListener 不需要加on

<script>//常用的键盘事件// 1.onkeyup      某个键盘按键被松开时触发// document.onkeyup = function() {//     console.log('我弹起了');// }document.addEventListener('keyup', function() {console.log('我弹起了');})// 2.onkeydown     某个键盘按键被按下时触发document.addEventListener('keydown', function() {console.log('我按下了down');})// 3.onkeypress      某个键盘按键被按下时触发  不识别功能键 ctrl等document.addEventListener('keypress', function() {console.log('我按下了press');})// 4. 执行顺序: keydown -- keypress -- keyup// 5. 如果使用addEventListener 不需要加on</script>

8.1 keyCode 判断用户按下了哪个键

说明:返回该键的ASCII值

<script>// keyCode 判断用户按下了哪个键 返回该键的ASCII值// 1.keyup和keydown 事件不区分大小写 a和A 得到都是65document.addEventListener('keyup', function(e) {//console.log(e);console.log('keyup:' + e.keyCode);// 可以利用keycode返回的ASCII值来判断用户按下了哪个键if (e.keyCode === 65) {alert('您按下了a键');} else {alert('没有按下a键')}})// 2.keypress 事件区分大小写  a -- 97     A -- 65document.addEventListener('keypress', function(e) {console.log('press:' + e.keyCode);})</script>

9 案例-模拟京东按键输入内容

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

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><input type="text"><script>// 核心思路:// 检测用户是否按下了 s 键,如果按下了 s 键,就把光标定位到搜索框里面// 使用键盘事件对象里面的keyCode 判断用户按下的是否是 s 键// 搜索框获得焦点: 使用js里面的focus() 方法var search = document.querySelector('input');document.addEventListener('keyup', function(e) {console.log(e.keyCode);if (e.keyCode === 83) {search.focus();}})</script>
</body></html>

10 案例-模拟京东快递单号查询

案例分析:
    快递单号输入内容时,上面的大号字体盒子(con)显示(这里面的字号更大)
    表单检测用户输入:给表单添加键盘事件
    同时把快递单号里面的值(value)获取过来赋值给con盒子(innerText)作为内容
    如果快递单号里面的内容为空,则隐藏大号字体盒子(con)盒子

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>* {margin: 0;padding: 0;box-sizing: border-box;}.search {position: relative;width: 178px;margin: 100px;}.con {display: none;position: absolute;top: -40px;width: 171px;border: 1px solid rgba(0, 0, 0, .2);box-shadow: 0 2px 4px rgba(0, 0, 0, .2);padding: 5px 0;font-size: 18px;line-height: 20px;color: #333;}.con::before {content: '';width: 0;height: 0;position: absolute;top: 28px;left: 18px;border: 8px solid #000;border-style: solid dashed dashed;border-color: #fff transparent transparent;}</style>
</head><body><div class="search"><div class="con">123</div><input type="text" placeholder="请输入您的快递单号" class="jd"></div><script>// 快递单号输入内容时,上面的大号字体盒子(con)显示(这里面的字号更大)// 表单检测用户输入:给表单添加键盘事件// 同时把快递单号里面的值(value)获取过来赋值给con盒子(innerText)作为内容// 如果快递单号里面的内容为空,则隐藏大号字体盒子(con)盒子var con = document.querySelector('.con');var jd_input = document.querySelector('.jd');jd_input.addEventListener('keyup', function() {//console.log('输入内容啦');// con.style.display = 'block';// con.innerText = this.value;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>
</body></html>

七、BOM

1 BOM概述

BOM(Browser Object Model)即浏览器对象模型,它提供了独立于内容而与浏览器窗口进行交互的对象,其核心对象是window。
    BOM由一系列相关的对象构成,并且每个对象都提供了很多方法和属性。
    BOM缺乏标准,JavaScript语法的标准化组织是ECMA,DOM的标准化组织W3C,BOM最初是Netscape浏览器标准的一部分。

DOM
    文档对象模型
    DOM就是把 文档 当作一个 对象 来看待
    DOM的顶级对象是document
    DOM主要学习的是操作页面元素
    DOM是W3C标准规范

BOM
    浏览器对象模型
    把 浏览器 当作一个 对象 来看待
    BOM的顶级对象是window
    BOM学习的是浏览器窗口交互的一些对象
    BOM是浏览器厂商在各自浏览器上定义的,兼容性较差

1.1 BOM的构成

BOM比DOM更大,它包含DOM。
    window:document location navigation screen history
    window对象是浏览器的顶级对象,它具有双重角色。
        1.它是JS访问浏览器窗口的一个接口。
        2.它是一个全局对象。定义在全局作用域中的变量、函数都会变成window对象的属性和方法。
    在调用的时候可以省略window,前面学习的对话框都属于window对象方法,如window.alert() 、prompt()等
    注意:window下的一个特殊属性window.name

2 window对象的常见事件

2.1 窗口加载事件

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

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

DOMContentLoaded事件触发时,仅当DOM加载完成,不包括样式表,图片,flash等等。
    Ie9以上才支持。
    如果页面的图片很多的话,从用户访问到onload触发可能需要较长的时间,交互效果就不能实现,必然影响用户的体验,此时DOMContentLoaded事件比较合适。

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><!-- 有了window.onload 这段代码写在任何位置都可以 --><!-- 注意:1.有了window.onload就可以把JS代码写到页面元素的上方,因为onload是等页面内容全部加载完毕,再去执行处理函数。2.window.onload传统注册事件方式只能写一次,如果有多个,会以最后一个window.onload为准。 --><script>// window.onload = function() {//     var btn = document.querySelector('button');//     btn.addEventListener('click', function() {//         alert('点击我');//     })// }// window.onload = function() {//     alert(22);// }// 3. 如果使用addEventListener 则没有限制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);})// load等页面内容全部加载完毕 包含dom元素 图片 flash css 等// DOMContentLoaded 是dom加载完毕 不包含图片 falsh css等</script></head><body><button>点击</button>
</body></html>

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

2.2 调整窗口大小事件

window.onsize = function(){}
    window.addEventListener("resize",function(){});

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

注意:
    1.只要窗口大小发生像素变化,就会触发这个事件。
    2.我们经常利用这个事件完成响应式布局。window.innerWidth当前屏幕的宽度。

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>div {width: 200px;height: 200px;background-color: pink;}</style>
</head><body><script>window.addEventListener('load', function() {var div = document.querySelector('div');window.addEventListener('resize', function() {console.log(window.innerWidth);console.log('变化了');if (window.innerWidth <= 800) {div.style.display = 'none';} else {div.style.display = 'block';}})})</script><div></div>
</body></html>

3 定时器

3.1 两种定时器

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

3.2 setTimeout()定时器

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

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

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

 <script>// 1.setTimeout()// 语法规范 window.setTimeout(调用函数, [延迟的毫秒数]);// 注意:// 1. window在调用时可以省略// 2. 这个延时时间单位是毫秒 但是可以省略, 如果省略默认的是0// setTimeout(function() {//     console.log('时间到了');// }, 2000);// 3. 这个调用函数可以直接写函数 还可以写 函数名function callback() {console.log('爆炸了');}setTimeout(callback, 3000);// 4. 还有一个写法  '函数名()'  不提倡 啰嗦setTimeout('callback()', 3000);// 5. 页面中可能有很多的定时器 ,我们经常给定时器加标识符var timer1 = setTimeout(callback, 3000);var timer2 = setTimeout(callback, 5000);</script>

3.3 案例 5秒钟后自动关闭广告

案例分析
    核心思路:5秒之后,就把这个广告隐藏起来
    用定时器setTimeout

<body><img src="data:images/ad.jpg" alt="guanggao" class="ad"><script>var ad = document.querySelector('.ad');setTimeout(function() {ad.style.display = 'none';}, 5000);</script>
</body>

3.4 停止setTimeout()定时器

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

注意:
    1.window可以省略。
    2.里面的参数就是定时器的标识符。

<body><button>点击停止定时器</button><script>var btn = document.querySelector('button');var timer = setTimeout(function() {console.log('爆炸了');}, 5000);btn.addEventListener('click', function() {clearTimeout(timer);})</script>
</body>

3.5 setInterval()定时器

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

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

注意:
    1.window可以省略。
    2.这个调用函数可以直接写函数,或者写函数名或者采取字符串'函数名()'三种形式。
    3.间隔的毫秒数省略默认是0,如果写,必须是毫秒,表示每隔多少毫秒就自动调用这个函数。
    4.因为定时器可能有很多,所以经常给定时器赋值一个标识符。

代码:

<SCript>// 1.setInterval()// 语法规范 window.setInterval(回调函数,[间隔的毫秒数]);setInterval(function() {console.log('继续输出');}, 1000);// 2. setTimeout 延时时间到了,就去调用这个回调函数,只调用依次,就结束了这个定时器// 3. setInterval 每隔这个延时时间,就去调用这个回调函数,会调用很多次,重复调用这个函数</SCript>

区别:
    1. setTimeout 延时时间到了,就去调用这个回调函数,只调用依次,就结束了这个定时器
        2. setInterval 每隔这个延时时间,就去调用这个回调函数,会调用很多次,重复调用这个函数

3.6 案例 计时器效果

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

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style></style>
</head><body><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('2022-8-13 16:00:00'); // 返回的是用户输入时间的总毫秒数countDown(); // 先调用一次这个函数,防止刚开始刷新页面有空白问题// 2.开启定时器setInterval(countDown, 1000);function countDown() {var newTime = +new Date(); // 返回的是当前时间总的毫秒数var times = (inputTime - nowTime) / 1000; // time是剩余时间总的毫秒数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>
</body></html>

3.7 停止setInterval()定时器

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

注意:
    1.window可以省略
    2.里面的参数就是定时器的标识符

<body><button class="begin">开启定时器</button><button class="stop">停止定时器</button><script>var begin = document.querySelector('.begin');var stop = document.querySelector('.stop');var timer = null; // 全局变量  null是一个空对象begin.addEventListener('click', function() {timer = setInterval(function() {console.log('ni hao ma');}, 1000);})stop.addEventListener('click', function() {clearInterval(timer);})</script>
</body>

3.8 案例 发送短信案例

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

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><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 = '发送';time = 3; // 这个3需要从新开始} else {btn.innerHTML = '还剩' + time + '秒';time--;}}, 1000);// btn.innerHTML = '还剩10秒';})</script>
</body></html>

3.9 this指向问题

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

<body><button>点击</button><script>// this  一般情况下this的最终指向的是那个调用它的对象// 1. 全局作用域或者普通函数中this指向全局对象window//    注意定时器里面的this指向windowconsole.log(this);function fn() {console.log(this);}window.fn();window.setTimeout(function() {console.log(this);}, 1000);// 2. 方法调用中谁调用this指向谁var o = {sayHi: function() {console.log(this); // this指向的是 o 这个对象}}o.sayHi();// var btn = document.querySelector('button');// btn.onclick = function() {//     console.log(this); // this 指向的是 btn 这个对象// }btn.addEventListener('click', function() {console.log(this); // this 指向的是 btn 这个对象})// 3. 构造函数中this指向构造函数的实例function Fun() {console.log(this); // this 指向的是 fun 这个对象}var fun = new Fun();</script>
</body>

4 JS执行机制

4.1 JS是单线程

JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。这是因为JavaScript这门脚本语言诞生的使命所致——JavaScript是为处理页面中用户的交互,以及操作DOM而诞生的。比如我们对某个DOOM元素进行添加和删除操作,不能同时进行。应该先进行添加,之后再删除。

单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。这样所导致的问题是:如果JS执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉。

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

<SCript>// 1. 第一个问题  结果 1 2 3console.log(1);setTimeout(function() {console.log(3);}, 1000);console.log(2);// 2.第二个问题  结果 1 2 3console.log(1);setTimeout(function() {console.log(3);}, 0);console.log(2);</SCript>

4.2 同步和异步

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

异步:
    在做一件事情时,因为这件事会花费很长时间,在做这件事情的同时,还可以去处理其他事情。

本质区别:这条流水线上各个流程的执行顺序不同。
    
    同步任务:
    同步任务都在主线程上执行,形成一个执行栈。

异步任务:
    JS的异步是通过回调函数实现的。
    一般而言,异步任务有以下三种类型:
        1.普通事件,如click、resize等
        2.资源加载,如load、error等
        3.定时器,包括setInterval、setTimeout等
    异步任务相关的回调函数添加到任务队列中(任务队列也称为消息队列)。

4.3 JS执行机制

1.先执行执行栈中的同步任务。
    2.异步任务(回调函数)放入任务队列中 暂不执行,
    3.一旦执行栈中的所有同步任务执行完毕,系统就会按次序读取任务队列中的异步任务,于是被读取的异步任务结束等待状态,进入执行栈,开始执行。

5 location对象

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

URL,统一资源定位符(Uniform Resource Locator,URL)是互联网上标准资源的地址。互联网上的每个文件都有一个唯一的URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它。
    
    URL的一般语法格式为:
    protocal://host[:port]/path/[?query]#fragment
    http://www.itcast.cn/index.html?name=andy&age=18#link
    
    组成           说明
    protocol     通信协议 常用的http ftp matio等
    host           主机(域名) www.itheima.com
    port            端口号 可选,省略时使用方案的默认端口 如http的  默认端口为80
    path           路径 由 零或多个'/'符号隔开的字符串,一般用来表示主机上的一个目录或文件地址
    query         参数 以键值对的形式,通过&符号分隔开来
    fragment    片段 #后面内容 常见于链接 锚点

5.1 location对象属性

location对象属性        返回值
    location.href               获取或者设置 整个URL
    location.host              返回主机(域名) www.itheima.com
    location.port               返回端口号 如果未写返回 空字符串
    location.pathname     返回路径
    location.search          返回参数
    location.hash             返回片段 #后面内容 常见于链接 锚点

5.2 案例 5秒钟之后自动跳转页面

案例分析:
    利用定时器做倒计时效果
    时间到了,就跳转页面。使用location.href

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><button>点击</button><div></div><script>var btn = document.querySelector('button');var div = document.querySelector('div');btn.addEventListener('click', function() {//console.log(location.href);location.href = 'http://www.baidu.com';})var timer = 5;setInterval(function() {if (timer == 0) {location.href = 'http://www.baidu.com';} else {div.innerHTML = '您将在' + timer + '秒钟之后跳转到首页';timer--;}}, 1000);</script>
</body></html>

5.3 案例 location对象常见的方法

location.assign() 跟href一样,可以跳转页面(也称为重定向页面)
    location.assign() 替换当前页面,因为不记录历史,所以不能后退页面
    location.assign() 重新加载页面,相当于刷新按钮或者 f5 如果参数为true强制刷新 ctrl+f5

<body><button>点击</button><script>var btn = document.querySelector('button');btn.addEventListener('click', function() {// 记录浏览历史,所以可以实现后退功能location.assign('http://www.baidu.com');// 不记录浏览历史,所以不可以实现后退功能location.replace('http://www.baidu.com');location.reload();})</script>
</body>

6 navigator对象

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

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

7 history对象

history对象方法        作用
    back()            可以后退功能
    forward()        前进功能
    go(参数)        前进后退功能 参数如果是1 前进1个页面
                如果是-1 则后退1个页面
    
    代码:

<body><a href="list.html">点我去往列表页</a><button>前进</button><script>var btn = document.querySelector('button');btn.addEventListener('click', function() {//history.forward();history.go(1);})</script>
</body>
<body><a href="index.html">点我去往首页</a><button>后退</button><script>var btn = document.querySelector('button');btn.addEventListener('click', function() {//history.back();history.go(-1);})</script>
</body>

history对象一般在实际开发中比较少用,但是会在一些OA办公系统中见到。

八、PC端网页特效

1 元素偏移量offset系列

1.1 offset概述

offset翻译过来就是偏移量,我们使用offset系列相关属性可以动态的得到该元素的位置(偏移)、大小等。
        获得元素距离带有定位父元素的位置
        获得元素自身的大小(宽度高度)
        注意:返回的数值都不带单位

offset系列常用属性:
    offset系列属性                作用
    element.offsetParent    返回作为该元素带有定位的父级元素 如果父级都没有定位则返回body
    element.offsetTop         返回元素相对带有定位父元素上方的偏移
    element.offsetLeft         返回元素相对带有定位父元素左边框的偏移
    element.offsetWidth      返回自身包括padding、边框、内容区的宽度,返回数值不带单位
    element.offsetHeight     返回自身包括padding、边框、内容区的高度,返回值不带单位

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>* {margin: 0;padding: 0;}.father {position: relative;width: 200px;height: 200px;background-color: aqua;margin: 100px;}.son {width: 100px;height: 100px;background-color: purple;margin-left: 45px;}.w {width: 200px;height: 200px;background-color: brown;margin: 0 auto 200px;padding: 10px;border: 15px solid red;}</style>
</head><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 + widthconsole.log(w.offsetWidth);console.log(w.offsetHeight);// element.offsetParent // 3. 返回作为该元素带有定位的父级元素 如果父级都没有定位则返回bodyconsole.log(son.offsetParent);// 返回父亲  是最近一级的父级 不管有没有定位console.log(son.parentNode);</script>
</body></html>

1.2 offset和style的区别

offset
    offset可以得到任意样式表中的样式值
    offset系列获得的数值是没有单位的
    offsetWidth包含padding + border + width
    offsetWidth等属性是只读属性,只能获取不能赋值
    所以,我们想要获取元素大小位置,用offset更合适

style
    style只能得到行内样式表中的样式值
    style.width获得的是带有单位的字符串
    style.width获得不包含padding和border的值
    style.width是可读写属性,可以获取也可以赋值
    所以,我们想要给元素更改值,则需要用style改变

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

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

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>.box {width: 200px;height: 200px;background-color: brown;margin: 0 auto 200px;padding: 10px;border: 15px solid red;}</style>
</head><body><div class="box"></div><script>var box = document.querySelector('.box');// box.addEventListener('click', function(e) {box.addEventListener('mousemove', function(e) {// console.log(e.pageX);// console.log(e.pageY);// console.log(box.offsetLeft);var x = e.pageX - this.offsetLeft;var y = e.pageY - this.offsetTop;this.innerHTML = 'x坐标是' + x + 'y坐标是' + y;})</script>
</body></html>

1.4 案例 模态框拖拽

案例分析:
    弹出框,我们也称为模态框
    1.点击弹出层,会弹出模态框,并且显示灰色半透明的遮挡层
    2.点击关闭按钮,可以关闭模态框,并且同时关闭灰色半透明遮挡层
    3.鼠标放到模态框最上面一行,可以按住鼠标拖拽模态框在页面中移动
    4.鼠标松开,可以停止拖动模态框移动

点击弹出框,模态框和遮挡层就会显示出来display:block;
    点击关闭按钮,模态框和遮挡层就会隐藏起来 display:none;
    在页面中拖拽的原理:鼠标按下并且移动,之后松开鼠标
    触发事件是鼠标按下 mousedown,鼠标移动mousemove 鼠标松开mouseup
    拖拽过程:鼠标移动过程中,获得最新的值赋值给模态框的left和top值,这样模态框可以跟着鼠标走了
    鼠标按下触发的事件源是 最上面一行,就是id为title
    鼠标的坐标 减去 鼠标在盒子内的坐标,才是模态框真正的位置
    鼠标按下,我们要得到鼠标在盒子的坐标
    鼠标移动,就让模态框的坐标设置为:鼠标坐标减去盒子坐标即可,注意移动事件写到按下事件里面。
    鼠标松开,就停止拖拽,就是可以让鼠标移动事件解除

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>h6,a {padding: 0px;margin: 0px;}.login {display: none;width: 512px;height: 280px;position: fixed;border: #ebebeb solid 1px;left: 50%;top: 50%;background: #ffffff;box-shadow: 0px 0px 20px #ddd;z-index: 9999;transform: translate(-50%, -50%);}.login-title {width: 100%;margin: 10px 0px 0px 0px;text-align: center;line-height: 40px;height: 40px;font-size: 18px;position: relative;cursor: move;}.login-input-content {margin-top: 20px;}.login-button {width: 50%;margin: 30px auto 0px auto;line-height: 40px;font-size: 14px;border: #ebebeb 1px solid;text-align: center;}.login-bg {display: none;width: 100%;height: 100%;position: fixed;top: 0px;left: 0px;background: rgba(0, 0, 0, .3);}a {text-decoration: none;color: #000000;}.login-button a {display: block;}.login-input input.list-input {float: left;line-height: 35px;height: 35px;width: 350px;border: #ebebeb 1px solid;text-indent: 5px;}.login-input {overflow: hidden;margin: 0px 0px 20px 0px;}.login-input label {float: left;width: 90px;padding-right: 10px;text-align: right;line-height: 35px;height: 35px;font-size: 14px;}.login-title span {position: absolute;font-size: 12px;right: -20px;top: -30px;background: #ffffff;border: #ebebeb solid 1px;width: 40px;height: 40px;border-radius: 20px;}</style>
</head><body><div class="login-header"><a id="link" href="javascript:;">点击,弹出登录框</a></div><div id="login" class="login"><div id="title" class="login-title">登录会员<span><a id="closeBtn" href="javascript:void(0);" class="close-login">关闭</a></span></div><div class="login-input-content"><div class="login-input"><label>用户名:</label><input type="text" placeholder="请输入用户名" name="info[username]" id="username" class="username"></div><div class="login-input"><label>登录密码:</label><input type="password" placeholder="请输入登录密码" name="info[password]" id="password" class="username"></div></div><div id="loginBtn" class="login-buton"><a href="javascript:void(0);" id="login-button-submit"></a></div></div><!-- 遮盖层 --><div id="bg" class="login-bg"></div><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 和logincloseBtn.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)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>
</body></html>

2 元素可视区client系列

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

client系列属性                作用
    element.clientTop        返回元素上边框的大小
    element.clientLeft        返回元素左边框的大小
    element.clientWidth     返回自身包括padding、内容区的宽度,不含边框,返回数值不带单位
    element.clientHeight    返回自身包括padding、内容区的高度,不含边框,返回数值不带单位

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>div {width: 200px;height: 200px;background-color: pink;border: 10px solid red;}</style>
</head><body><div></div><script>// client 宽度   和offsetWidth 最大的区别就是 不包含边框var div = document.querySelector('div');console.log(div.clientWidth);</script>
</body></html>

2.1 立即执行函数

立即执行函数
    主要作用:创建一个独立的作用域
    (function(){})();

<script>// 1. 立即执行函数:不需要调用,立马就能够自己执行的函数function fn() {console.log(1);}fn();// 2. 写法 也可以传递参数进来// (function(){})();// (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));// 3. 立即执行函数最大的作用就是 独立创建了一个作用域,// 里面的左右的变量都是局部变量 ,没有命名冲突现象</script>

2.2 淘宝 flexible.js源码分析

// 立即执行函数
// ( function flexible ( window, document ) { } ( window, document ) )(function flexible(window, document) {// 获取的html 的根元素var docEl = document.documentElement// dpr物理像素比var dpr = window.devicePixelRatio || 1// adjust body font size  设置body的字体大小function setBodyFontSize() {// 如果页面中有body这个元素 就设置body的字体大小if (document.body) {document.body.style.fontsize = (12 * dpr) + 'px'} else {// 如果页面中没有body这个元素 则等着页面主要的DOM元素加载完毕再设置bodydocument.addEventListener('DOMContentLoaded', setBodyFontSize)}}setBodyFontSize();// set 1rem = viewWidth / 10  设置html元素的文字大小function setRemUnit() {var rem = docEl.clientWidth / 10docEl.style.fontsize = rem + 'px'}setRemUnit()// reset rem unit on page resize   当页面尺寸大小发生变化 重新设置remwindow.addEventListener('resize', setRemUnit)// pageshow  是重新加载页面触发的事件 // 与load相似 但不同 有三种情况 刷新页面会触发load事件 1.a标签的超链接 2.F5或者刷新按钮(强制刷新) 3.前进后退按钮// 但是 火狐中,有个特点 有个“往返缓存”,这个缓存中不仅保存着页面数据,还保存了DOM和Java的状态;实际上是将整个页面都保存在了内存里// 此时可以使用pageshow事件来触发,这个事件在页面显示时触发,无论页面是否来自缓存。在重新加载页面中,pageshow会在load事件触发后触发// 根据事件对象中的persisted来判断是否是缓存中的页面触发的pageshow事件,注意这个事件给window添加window.addEventListener('pageshow', function(e) {// e.persisted 返回的是true 就是说如果这个页面是从缓存取过来的页面,也需要重新计算一下rem的大小if (e.persisted) {setRemUnit()}})// detect 0.5px supports  有些移动端的浏览器不支持0.5像素的写法if (dpr >= 2) {var fakeBody = document.createElement('body')var testElement = document.createElement('div')testElement.style.border = '.5px solid transparent'fakeBody.appendChild(testElement)docEl.appendChild(fakeBody)if (testElement.offsetHeight === 1) {docE1.classList.add('hairlines')}docEl.removeChild(fakeBody)}
}(window, document))

3 元素滚动scroll系列

3.1 元素滚动scroll系列

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

scroll系列属性               作用
    element.scrollTop        返回被卷去的上侧距离,返回数值不带单位
    element.scrollLeft        返回被卷去的左侧距离,返回数值不带单位
    element.scrollWidth     返回自身实际的宽度,不含边框,返回数值不带单位
    element.scrollHeight    返回自身实际的高度,不含边框,返回数值不带单位

3.2 页面被卷去头部

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

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>div {width: 100px;height: 100px;background-color: pink;border: 10px solid red;padding: 10px;overflow: auto;}</style>
</head><body><div>divdivdivdivdivdiv我是内容 divdivdivdivdivdiv我是内容 divdivdivdivdivdiv我是内容divdivdivdivdivdiv我是内容divdivdivdivdivdiv我是内容 divdivdivdivdivdiv我是内容divdivdivdivdivdiv我是内容</div><script>// scroll系列var div = document.querySelector('div');// 得出来的高度不同console.log(div.scrollHeight);console.log(div.clientHeight);// scroll滚动事件 当我们滚动条发生变化时会触发的事件div.addEventListener('scroll', function() {console.log(div.scrollTop);})</script>
</body></html>

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

案例分析:
    原先侧边栏是绝对单位
    当页面滚动到一定位置,侧边栏改为固定定位
    页面继续滚动,会让 返回顶部 显示出来

需要用到页面滚动事件 scroll 因为是页面滚动 所以事件源是document
    滚动到某个位置,就是判断页面被卷去的上部值
    页面被卷曲的头部:可以通过window.pageYOffset获得 如果被卷去的左侧 window.pageXOffset
    注意:元素被卷去的头部是element.scrollTop,如果页面被卷去的头部则是window.pageYOffset

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>.slider-bar {position: absolute;left: 50%;top: 300px;margin-left: 600px;width: 45px;height: 130px;background: purple;}.w {width: 1200px;margin: 10px auto;}.header {height: 150px;background-color: darkolivegreen;}.banner {height: 250px;background-color: tomato;}.main {height: 1000px;background-color: yellowgreen;}span {display: none;position: absolute;bottom: 0;}</style>
</head><body><div class="slider-bar"><span class="goBack">返回顶部</span></div><div class="header w">头部区域</div><div class="banner w">banner区域</div><div class="main w">主体部分</div><script>// 1. 获取元素var sliderbar = document.querySelector('.slider-bar');var banner = document.querySelector('.banner');// banner.offsetTop 就是被卷去头部的大小  写到滚动外部// console.log(banner.offsetTop);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. 页面滚动事件 scrolldocument.addEventListener('scroll', function() {// console.log(11);//console.log(window.pageXOffset); //页面被卷去的头部// console.log(window.pageXOffset);// 3. 侧边栏改为固定定位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';}})// 3. 当我们点击了返回顶部  就让窗口滚动到页面的最上方goBack.addEventListener('click', function() {// window.scroll(x,y);  // x 和 y不跟单位// window.scroll(0, 0);// 因为是窗口滚动 所以对象是windowanimate(window, 0);});// 动画函数function animate(obj, target, callback) {// console.log(callback); // callback 存的是一个函数  callback = function() {}// 先清除以前的定时器 只保留当前的一个定时器执行clearInterval(obj.timer);obj.timer = setInterval(function() {// 步长值写到定时器里面// 把步长值改为整数 不要出现小数问题// var step = (target - obj.offsetLeft) / 10;// 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();}}// 把每次 加5 步长值 改为慢慢变小的值  步长公式(目标值-现在的位置)/ 10//obj.style.left = window.pageYOffset + step + 'px';// 修改window.scroll(0, window.pageYOffset + step);}, 15);}</script>
</body></html>

3.4 页面被卷去头部兼容性解决方案

需要注意的是,页面被卷去的头部,有兼容性问题,因此被卷去的头部通常有以下几种写法:
    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

3.5 mouseenter和mouseover的区别

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

4 动画函数封装

4.1 动画实现原理

核心原理:通过定时器sestInterval()不断移动盒子位置
    实现步骤:
    1.获取盒子当前位置
    2.让盒子在当前位置加上1个移动距离
    3.利用定时器不断重复这个操作
    4.加一个结束定时器的条件
    5.注意此元素需要添加定位,才能使用element.style.left

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>div {position: absolute;left: 0;width: 100px;height: 100px;background-color: aqua;}</style>
</head><body><div></div><script>// 实现步骤:// 1.获取盒子当前位置// 2.让盒子在当前位置加上1个移动距离// 3.利用定时器不断重复这个操作// 4.加一个结束定时器的条件// 5.注意此元素需要添加定位,才能使用element.style.leftvar div = document.querySelector('div');var timer = setInterval(function() {if (div.offsetLeft >= 400) {// 停止动画 本质是停止定时器clearInterval(timer);}div.style.left = div.offsetLeft + 5 + 'px';}, 30);</script>
</body></html>

4.2 简单动画函数封装

注意函数需要传递两个参数,动画对象和移动到的距离

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>div {position: absolute;left: 0;width: 100px;height: 100px;background-color: aqua;}span {position: absolute;left: 0;top: 200px;display: block;width: 150px;height: 150px;background-color: brown;}</style>
</head><body><div></div><span>许嵩</span><script>// 简单函数封装  obj 目标对象    target 目标位置function animate(obj, target) {var timer = setInterval(function() {if (obj.offsetLeft >= target) {// 停止动画 本质是停止定时器clearInterval(timer);}obj.style.left = obj.offsetLeft + 5 + 'px';}, 30);}var div = document.querySelector('div');var span = document.querySelector('span');// 调用函数animate(div, 300);animate(span, 200);</script>
</body></html>

4.3 给不同对象添加不同计时器

如果多个元素都使用这个动画函数,每次都要var 声明定时器。我们可以给不同的元素使用不同的定时器(自己专门用自己的定时器)。
    核心原理:利用JS是一门动态语言,可以很方便的给当前对象添加属性。

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>div {position: absolute;left: 0;width: 100px;height: 100px;background-color: aqua;}span {position: absolute;left: 0;top: 200px;display: block;width: 150px;height: 150px;background-color: brown;}</style>
</head><body><button>点击许嵩才走</button><div></div><span>许嵩</span><script>// var obj ={};// obj.name='andy';// 简单函数封装  obj 目标对象    target 目标位置// 给不同的元素指定了不同的定时器function animate(obj, target) {// 当不断点击按钮 这个元素速度会越来越快,因为开启了太多定时器// 解决方案就是 让元素只有一个定时器执行// 先清除以前的定时器 只保留当前的一个定时器执行clearInterval(obj.timer);obj.timer = setInterval(function() {// var timer = setInterval(function() {if (obj.offsetLeft >= target) {// 停止动画 本质是停止定时器clearInterval(obj.timer);}obj.style.left = obj.offsetLeft + 5 + 'px';}, 30);}var div = document.querySelector('div');var span = document.querySelector('span');var btn = document.querySelector('button');// 调用函数animate(div, 300);btn.addEventListener('click', function() {animate(span, 600);})</script>
</body></html>

4.4 缓动动画原理

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

思路:
    1.让盒子每次移动距离慢慢变小,速度就会慢慢落下来
    2.核心算法:(目标值-现在的位置)/ 10 做为每次移动的距离步长
    3.停止的条件是:让当前盒子位置等于目标位置就停止定时器
    4.注意步长需要取整

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>div {position: absolute;left: 0;width: 100px;height: 100px;background-color: aqua;}span {position: absolute;left: 0;top: 200px;display: block;width: 150px;height: 150px;background-color: brown;}</style>
</head><body><button class="btn500">点击许嵩才走500</button><button class="btn800">点击许嵩才走800</button><span>许嵩</span><script>// 思路:// 1.让盒子每次移动距离慢慢变小,速度就会慢慢落下来// 2.核心算法:(目标值-现在的位置)/ 10 做为每次移动的距离步长// 3.停止的条件是:让当前盒子位置等于目标位置就停止定时器function animate(obj, target) {// 先清除以前的定时器 只保留当前的一个定时器执行clearInterval(obj.timer);obj.timer = setInterval(function() {// 步长值写到定时器里面// 把步长值改为整数 不要出现小数问题// var step = (target - obj.offsetLeft) / 10;// 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);}// 把每次 加5 步长值 改为慢慢变小的值  步长公式(目标值-现在的位置)/ 10obj.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);})</script>
</body></html>

4.5 动画函数添加回调函数

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

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>div {position: absolute;left: 0;width: 100px;height: 100px;background-color: aqua;}span {position: absolute;left: 0;top: 200px;display: block;width: 150px;height: 150px;background-color: brown;}</style>
</head><body><button class="btn500">点击许嵩才走500</button><button class="btn800">点击许嵩才走800</button><span>许嵩</span><script>// 思路:// 1.让盒子每次移动距离慢慢变小,速度就会慢慢落下来// 2.核心算法:(目标值-现在的位置)/ 10 做为每次移动的距离步长// 3.停止的条件是:让当前盒子位置等于目标位置就停止定时器function animate(obj, target, callback) {// console.log(callback); // callback 存的是一个函数  callback = function() {}// 先清除以前的定时器 只保留当前的一个定时器执行clearInterval(obj.timer);obj.timer = setInterval(function() {// 步长值写到定时器里面// 把步长值改为整数 不要出现小数问题// var step = (target - obj.offsetLeft) / 10;// 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();}}// 把每次 加5 步长值 改为慢慢变小的值  步长公式(目标值-现在的位置)/ 10obj.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() {// 调用函数 // 把 这个函数当作 参数 function() {}  传给callbackanimate(span, 800, function() {// alert('你好吗?')span.style.backgroundColor = 'yellow';});})// 匀速动画 就是 盒子是当前的位置 + 固定的值 10// 缓动动画就是 盒子当前的位置 + 变化的值  (目标值-现在的位置)/ 10</script>
</body></html>

4.6 动画函数封装到单独JS文件里面

因为以后经常使用这个动画函数,可以单独封装到一个JS文件里面,使用的时候引用这个JS文件即可。
    1.单独建一个js文件

function animate(obj, target, callback) {// console.log(callback); // callback 存的是一个函数  callback = function() {}// 先清除以前的定时器 只保留当前的一个定时器执行clearInterval(obj.timer);obj.timer = setInterval(function() {// 步长值写到定时器里面// 把步长值改为整数 不要出现小数问题// var step = (target - obj.offsetLeft) / 10;// 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();}}// 把每次 加5 步长值 改为慢慢变小的值  步长公式(目标值-现在的位置)/ 10obj.style.left = obj.offsetLeft + step + 'px';}, 15);
}

5 常见网页特效案例

5.1 案例-网页轮播图

轮播图也称为焦点图,是网页中比较常见的网页特效。
    功能需求:
    1.鼠标经过轮播图模块,左右按钮显示,离开隐藏左右按钮
    2.点击右侧按钮一次,图片往左播放一张,以此类推,左侧按钮同理
    3.图片播放的同时,下面小圆圈模块跟随一起变化
    4.点击小圆圈,可以播放相应图片
    5.鼠标不经过轮播图,轮播图也会自动播放图片
    6.鼠标经过,轮播图模块,自动播放停止

因为js较多,我们单独新建js文件,再新建js文件,引入页面中
    此时需要添加load事件
    鼠标经过轮播图模块,左右按钮显示,离开隐藏左右按钮
    显示隐藏display按钮

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

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

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

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

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

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

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

代码 index.js部分:

window.addEventListener('load', function() {// alert(1);// 1. 获取元素var arrow_l = document.querySelector('.arrow-l');var arrow_r = document.querySelector('.arrow-r');var focus = document.querySelector('.focus');var focusWidth = focus.offsetWidth;// 2. 鼠标经过focus 就显示隐藏的左右按钮focus.addEventListener('mouseenter', function() {arrow_l.style.display = 'block';arrow_r.style.display = 'block';clearInterval(timer);timer = null; //清楚定时器变量})focus.addEventListener('mouseleave', function() {arrow_l.style.display = 'none';arrow_r.style.display = 'none';timer = setInterval(function() {// 手动调用右侧按钮点击事件arrow_r.click();}, 2000);})// 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++) {// 创建一个小livar li = document.createElement('li');// 记录当前小圆圈的索引号 通过自定义属性来做li.setAttribute('index', i);// 把小li插入到ol里面ol.appendChild(li);// 4. 小li的排他思想  可以直接生成小圆圈的同时,就可以直接绑定这个点击事件li.addEventListener('click', function() {// 干掉所有人 把所有的小li 清除  current 类名for (var i = 0; i < ol.children.length; i++) {ol.children[i].className = '';}// 留下我自己  当前的小li 设置current 类名this.className = 'current';// 5. 点击小圆圈,移动图片 当然移动的是ul// ul 的移动距离 小圆圈的索引号 乘以 图片的宽度// 当我们点击了某个小li 就拿到当前小li 的索引号// animate(obj,target,callback);var index = this.getAttribute('index');// 当我点击了某个小li 就要把这个小li 的索引号给numbernum = index;// 当我点击了某个小li 就要把这个小li 的索引号给circlecircle = index;// num = circle = index;console.log(focusWidth);console.log(index);// animate(ul, target, callback);animate(ul, -index * focusWidth);})}// 把ol里面的第一个小li 设置类名为currentol.children[0].className = 'current';// 6. 克隆第一张图片  克隆ul第一个li cloneNode() 加 true深克隆 复制里面的子节点var first = ul.children[0].cloneNode(true);ul.appendChild(first);// 7. 点击右侧按钮 图片滚动一张var num = 0;// 控制小圆圈的播放var circle = 0;// flag 节流阀var flag = true;arrow_r.addEventListener('click', function() {if (flag) {flag = false; // 关闭节流阀// 如果走到了最后复制的一张图片,让ul快速的、不做动画的跳到最左侧:left为0// 同时num赋值为0,可以从新开始滚动图片了if (num == ul.children.length - 1) {ul.style.left = 0;num = 0;}// alert(1);num++;animate(ul, -num * focusWidth, function() {flag = true; // 打开节流阀});// 8. 点击右侧按钮,小圆圈跟随变化 声明一个变量circle,每次点击自增1circle++;// 如果circle == 4 就重新复原为 0 说明走到最后我们克隆的这张图片了if (circle == ol.children.length) {circle = 0;}// 调用函数circleChange();}});// 9. 左侧按钮做法arrow_l.addEventListener('click', function() {if (flag) {flag = false;// 如果走到了最后复制的一张图片,让ul快速的、不做动画的跳到最左侧:left为0// 同时num赋值为0,可以从新开始滚动图片了if (num == 0) {num = ul.children.length - 1;ul.style.left = -num * focusWidth + 'px';// ul.style.left = (ul.children.length - 1) * focusWidth + 'px';}// alert(1);num--;animate(ul, -num * focusWidth, function() {flag = true;});// 8. 点击右侧按钮,小圆圈跟随变化 声明一个变量circle,每次点击自增1circle--;// 如果circle < 0  说明第一张图片 则小圆圈要改为第4个小圆圈(3)// if (circle < 0) {//     circle = ol.children.length - 1;// }// 三元表达式circle = circle < 0 ? ol.children.length - 1 : circle;// 调用函数circleChange();}});function circleChange() {// 先清除其余小圆圈的current类名for (var i = 0; i < ol.children.length; i++) {ol.children[i].className = '';}// 留下当前的小圆圈的current类名ol.children[circle].className = 'current';}// 10.  定时器  自动播放轮播图,实际就类似于点击了右侧按钮var timer = this.setInterval(function() {// 手动调用右侧按钮点击事件arrow_r.click();}, 2000);})

5.2 节流阀以及逻辑中断应用

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

逻辑中断运用
    // 回调函数写到定时器结束里面
            // if (callback) {
            //     // 调用函数
            //     callback();
            // }
            // 短路运算 两侧都为true才会执行 左边为假右边不会执行
            callback && callback();

5.3 案例-返回顶部

滚动窗口至文档中的特定位置
    window.srcll(x,y)
    注意 x 和 y不跟单位

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

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>.slider-bar {position: absolute;left: 50%;top: 300px;margin-left: 600px;width: 45px;height: 130px;background: purple;}.w {width: 1200px;margin: 10px auto;}.header {height: 150px;background-color: darkolivegreen;}.banner {height: 250px;background-color: tomato;}.main {height: 1000px;background-color: yellowgreen;}span {display: none;position: absolute;bottom: 0;}</style>
</head><body><div class="slider-bar"><span class="goBack">返回顶部</span></div><div class="header w">头部区域</div><div class="banner w">banner区域</div><div class="main w">主体部分</div><script>// 1. 获取元素var sliderbar = document.querySelector('.slider-bar');var banner = document.querySelector('.banner');// banner.offsetTop 就是被卷去头部的大小  写到滚动外部// console.log(banner.offsetTop);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. 页面滚动事件 scrolldocument.addEventListener('scroll', function() {// console.log(11);//console.log(window.pageXOffset); //页面被卷去的头部// console.log(window.pageXOffset);// 3. 侧边栏改为固定定位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';}})// 3. 当我们点击了返回顶部  就让窗口滚动到页面的最上方goBack.addEventListener('click', function() {// window.scroll(x,y);  // x 和 y不跟单位// window.scroll(0, 0);// 因为是窗口滚动 所以对象是windowanimate(window, 0);});// 动画函数function animate(obj, target, callback) {// console.log(callback); // callback 存的是一个函数  callback = function() {}// 先清除以前的定时器 只保留当前的一个定时器执行clearInterval(obj.timer);obj.timer = setInterval(function() {// 步长值写到定时器里面// 把步长值改为整数 不要出现小数问题// var step = (target - obj.offsetLeft) / 10;// 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();}}// 把每次 加5 步长值 改为慢慢变小的值  步长公式(目标值-现在的位置)/ 10//obj.style.left = window.pageYOffset + step + 'px';// 修改window.scroll(0, window.pageYOffset + step);}, 15);}</script>
</body></html>

5.4 案例-筋斗云

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

利用动画函数做动画效果
    原先筋斗云的起始位置是0
    鼠标经过某个小li,把当前小li的offsetLeft位置 作为目标值即可
    鼠标离开某个小li,就把目标值设为0
    如果点击了某个小li,就把li当前的位置存储起来,当鼠标离开,筋斗云就回到这个位置

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style></style><script src="js/animate.js"></script><script>window.addEventListener('load', function() {// 1.获取元素var cloud = document.querySelector('.cloud');var c_nav = document.querySelector('.c_nav');var lis = document.querySelector('li');// 2.给所有的小li绑定事件// 这个current作为筋斗云起始位置var current = 0;for (var i = 0; i < lis.length; i++) {// (1) 鼠标经过某个小li,把当前小li的offsetLeft位置 作为目标值即可lis[i].addEventListener('mouseenter', function() {animate(cloud, this.offsetLeft);})// (2) 鼠标离开就回到起始位置lis[i].addEventListener('mouseleave', function() {animate(cloud, 0);})// (3) 当鼠标点击,就把当前位置作为目标位置lis[i].addEventListener('mouseleave', function() {current = this.offsetLeft;})}})</script>
</head><body><div id="c_nav" class="c-nav"><span class="cloud"></span><ul><li class="current"><a href="#">首页新闻</a></li><li><a href="#">师资力量</a></li><li><a href="#">活动企划</a></li><li><a href="#">企业文化</a></li><li><a href="#">招聘信息</a></li><li><a href="#">公司简介</a></li><li><a href="#">雅俗共赏</a></li><li><a href="#">配齐配齐</a></li></ul></div>
</body></html>

九、移动端网页特效

1 触屏事件

1.1 触屏事件概述

移动端浏览器兼容性较好,我们不需要考虑以前JS的兼容性问题,可以放心地使用原生JS书写效果,但是移动端也有自己独特的地方。比如触屏事件touch(也称触摸事件),Android和IOS都有。
    touch对象代表一个触摸点。触摸点可能是一根手指,也可能是一根触摸笔。触屏事件可响应用户手指(或触控笔)对屏幕或者触控板操作。

触屏touch事件    说明
    touchstart          手指触摸到一个DOM元素时触发
    touchmove        手指在一个DOM元素上滑动时触发
    touchend           手指从一个DOM元素上移开时触发

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>div {width: 100px;height: 100px;background-color: pink;}</style>
</head><body><div></div><script>// 1. 获取元素var div = document.querySelector('div');div.addEventListener('touchstart', function() {console.log('手指触摸到一个DOM元素时触发');});div.addEventListener('touchmove', function() {console.log('手指在一个DOM元素上滑动时触发');});div.addEventListener('touchend', function() {console.log('手指从一个DOM元素上移开时触发');});</script>
</body></html>

1.2 触摸事件对象(TouchEvent)

TouchEvent是一类描述手指在触摸平面(触摸屏、触摸板等)的状态变化的事件。这类事件用于描述一个或多个触点,使开发者可以检测触点的移动,触点的增加和减少,等等

touchstart、touchmove、touchend三个事件都会各自有事件对象

触摸事件对象重点看三个常见对象列表:
    触摸列表                    说明
    touches                   正在触摸屏幕的所有手指的一个列表
    targetTouches         正在触摸当前DOM元素上的手指的一个列表
    changedTouches    手指状态发生了改变的列表,从无到有,从有到无变化

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>div {width: 100px;height: 100px;background-color: pink;}</style>
</head><body><div></div><script>// 1. 获取元素var div = document.querySelector('div');div.addEventListener('touchstart', function(e) {// console.log('手指触摸到一个DOM元素时触发');console.log(e);// touches 正在触摸屏幕的所有手指的列表// targetTouches 正在触摸当前DOM元素的手指列表// 如果侦听的是一个DOM元素,他们两个是一样的// changedTouches 手指状态发生了改变的列表,从无到有,从有到无变化// 因为我们一般都是触摸元素,所以最经常使用的是 targetTouchesconsole.log(e.targetTouches[0]);// targetTouches[0] 就可以得到正在触摸DOM元素的第一个手指信息,手指坐标等});div.addEventListener('touchmove', function() {// console.log('手指在一个DOM元素上滑动时触发');});div.addEventListener('touchend', function(e) {// console.log('手指从一个DOM元素上移开时触发');console.log(e);// 当我们手指离开屏幕的时候就没有了 touches 和 targetTouches 列表// 但是会有 changedTouches});</script>
</body></html>

1.4 移动端拖动元素

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

拖动元素三部曲:
    (1)触摸元素touchstart:获取手指初始坐标,同时获得盒子原来的位置
    (2)移动手指touchmove:计算手指的滑动距离,并且移动盒子
    (3)离开手指touchend:
    注意:手指移动也会触发滚动屏幕 所以这里要阻止默认的屏幕滚动e.preventDefault();

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>div {width: 100px;height: 100px;background-color: pink;}</style>
</head><body><div></div><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>
</body></html>

2 移动端常见特效

2.1 classList属性

classList属性是HTML5新增的一个属性,返回元素的类名。但是 ie 10以上版本支持
    该属性用于在元素中添加,移除及切换CSS类。有以下方法:
    添加类:element.classList.add('类名');
    移除类:element.classList.remove('类名');
    切换类:element.classList.toggle('类名');

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>.bg {background-color: black;}</style>
</head><body><div class="one two"></div><button>开关灯</button><script>// classList属性是HTML5新增的一个属性,返回元素的类名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');})</script>
</body></html>

2.2 案例-返回顶部

当页面滚动某个地方,就显示,否则隐藏
    点击可以返回顶部
    事件:scroll页面滚动事件
    如果被卷去的头部pageYOffset大于某个数值
    点击,window.scroll(0,0)返回顶部

<body><div class="goBack"></div><script>var goBack = document.querySelector('.goBack');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);})</script>
</body>

2.3 移动端click事件300ms延时问题解决方案

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

解决方案:
    1.禁用缩放,浏览器禁用默认的双击缩放行为并且去掉300ms的点击延迟。
    <meta name="viewport" content="user-scalable=no">
    2.利用touch事件自己封装这个事件解决300ms延迟
    原理就是:
    当我们手指触摸屏幕,记录当前触摸时间
    当我们手指离开屏幕,用离开的时间减去触摸的时间
    如果时间小于150ms,并且没有滑动过屏幕,那么我们就定义为点击

代码:


    代码多很麻烦

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

3 移动端常用开发插件

3.1 什么是插件

移动端要求的是快速开发,所以我们经常会借助于一些插件来帮我们完成操作,JS插件是js文件,它遵循一定规范编写,方便程序展示效果,拥有特定功能且方便调用。如轮播图和瀑布流插件。
    特点:它一般是为了解决某个问题而专门存在,其功能单一,并且比较小,之前写的animate.js也算是一个最简单的插件。

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

GitHub官网地址:

GitHub - ftlabs/fastclick: Polyfill to remove click delays on browsers with touch UIs

在lib中有一个fastclick.js文件  复制 然后引入
    Usage 使用规范

3.2 Swiper插件的使用

中文官网地址:

Swiper中文网-轮播图幻灯片js插件,H5页面前端开发
    1.引入插件相关文件
    2.按照规定语法使用
    
    中文教程--》使用方法
    API文档 一些配置选项方法函数

获取swiper--》下载swiper
    下载下来的里面有一个demos文件夹 里面有很多 选择自己需要的,浏览器打开 右键  源代码
    下载下来的中有一个dist文件夹中有文件js和css 引入mincss就可以 拖过去复制也可
    注意不要更改里面的结构和类名 不然相应的js和css都要改

复制html结构
    复制css样式
    复制js  第一行window.addEventListener('load',function(){
    
    }

3.3 其他常见插件

superslide:http://www.superslide2.com/

SuperSlide | TouchSlide 官方网站 大话主席

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

https://github.com/cubiq/scroll

3.4 插件使用总结

确认插件实现的功能
    去官网查看使用说明
    下载插件
    打开demo实例文件,查看需要引入的相关文件,并且引入
    复制demo实例文件中的结构html,样式css以及js代码

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

zy.media.js
    H5给我们提供了video标签,但是浏览器的支持情况不同
    不同的视频格式文件,我们可以通过source解决
    但是外观样式,还有暂停,播放,全屏等功能我们只能自己写代码解决
    这个时候我们可以使用插件方式来制作

4 移动端常用开发框架

4.1 框架概述

框架,顾名思义就是一套架构,它会基于自身的特点向用户提供一套较为完整的解决方案。框架的控制权在框架本身,使用者要按照框架所规定的某种规范进行开发。
    插件一般是为了解决某个问题而专门存在,其功能单一,并且比较小。
    前端常用的框架有Bootstrap、Vue、Angular、React等。既能开发PC端,也能开发移动端
    前端常用的移动端插件有swiper、superslide、iscroll等

框架:大而全,一整套解决方案
    插件:小而专一,某个功能的解决方案

4.2 bootstrap轮播图

下载下来bootstrap

Bootstrap v3 中文文档 · Bootstrap 是最受欢迎的 HTML、CSS 和 JavaScript 框架,用于开发响应式布局、移动设备优先的 WEB 项目。 | Bootstrap 中文网

JavaScript插件

Bootstrap 是一个简洁、直观、强悍的前端开发框架,它让web开发更迅速、简单。
    Bootstrap JavaScript插件使用步骤:
    引入相关js文件
    复制HTML结构
    修改对应样式
    修改相应JS参数

十、本地存储

1 本地存储

随着互联网的快速发展,基于网页的应用越来越普遍,同时也变得越来越复杂,为了满足各种各样的需求,会经常性的在本地存储大量的数据,HTML5规范提出了相关解决方案。

本地存储特性
    1.数据存储在用户浏览器中
    2.设置、读取方便、甚至页面刷新不丢失数据
    3.容量较大,sessionStorage约5M、localStorage约20M
    4.只能存储字符串,可以将对象JSON.stringify()编码后存储

2 window.sessionStorage

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

存储数据:
    sessionStorage.setItem(key,value)

获取数据:
    sessionStorage.getItem(key)

删除数据:
    sessionStorage.removeItem(key)

删除所有数据:
    sessionStorage.clear()

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><input type="text"><button class="set">存储数据</button><button class="get">获取数据</button><button class="remove">删除数据</button><button class="del">清空所有数据</button><script>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);});get.addEventListener('click', function() {// 当我们点击了之后,就可以把表单里面的值获取过来var val = ipt.value;console.log(sessionStorage.getItem('uname'));});remove.addEventListener('click', function() {// 当我们点击了之后,就可以把表单里面的值移除var val = ipt.value;console.log(sessionStorage.removeItem('uname'));});del.addEventListener('click', function() {// 当我们点击了之后,就可以把表单里面的所有的值都清除  慎用啊var val = ipt.value;console.log(sessionStorage.clear());});</script>
</body></html>

3 window.localStorage

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

存储数据:
    localStorage.setItem(key,value)

获取数据:
    localStorage.getItem(key)

删除数据:
    localStorage.removeItem(key)

删除所有数据:
    localStorage.clear()

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><input type="text"><button class="set">存储数据</button><button class="get">获取数据</button><button class="remove">删除数据</button><button class="del">清空所有数据</button><script>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;localStorage.setItem('username', val);});get.addEventListener('click', function() {// 当我们点击了之后,就可以把表单里面的值获取过来console.log(localStorage.getItem('username'));});remove.addEventListener('click', function() {// 当我们点击了之后,就可以把表单里面的值移除console.log(localStorage.removeItem('username'));});del.addEventListener('click', function() {// 当我们点击了之后,就可以把表单里面的所有的值都清除  慎用啊console.log(localStorage.clear());});</script>
</body></html>

4 案例-记住用户名

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

案例分析:
    把数据存储起来,用到本地存储
    关闭页面,也可以显示用户名,所以用到localStorage
    打开页面,先判断是否有这个用户名,如果有,就在表单里面显示用户名,并且勾选复选框
    当复选框发生改变的时候 change事件
    如果勾选 就存储 否则就移除

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><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></html>

JavaScript核心Web APIs相关推荐

  1. 【JavaScript】Web APIs基础

    文档对象模型 (DOM) 将 web 页面与到脚本或编程语言连接起来.通常是指 JavaScript,但将 HTML.SVG 或 XML 文档建模为对象并不是 JavaScript 语言的一部分.DO ...

  2. Web APIs,BOM树,声明dom对象,年会抽奖案例,随机图片,表单,用户注册倒计时,innerHTNL,classList的使用

    介绍 严格意义上讲,我们在 JavaScript 阶段学习的知识绝大部分属于 ECMAScript 的知识体系,ECMAScript 简称 ES 它提供了一套语言标准规范,如变量.数据类型.表达式.语 ...

  3. JavaScript核心笔记未完

    JavaScript核心 Web API阶段 DOM BOM 操作 js基础是语法阶段 web API阶段是应用 主要是BOM DOM 页面交互功能 1.API 和Web API 1.1API (应用 ...

  4. JavaScript(五)—— Web APIs 简介/JavaScript 必须掌握的 DOM 操作 (丰富案例 + 思维导图)

    本篇为 JavaScript 系列笔记第五篇,将陆续更新后续内容.参考:黑马程序员JavaScript核心教程,前端基础教程 系列笔记: JavaScript(一)-- 初识JavaScript / ...

  5. 小白JavaScript学习笔记----web APIs

    目录 web API 操作DOM BOM,比如控制网页元素交互等各种网页交互效果 一.web APIs第一天(DOM-获取元素) 1.1变量声明 1.2Web API 基本认知 1.2.1作用和分类 ...

  6. JavaScript——Web APIs

    JS的组成 JavaScript由ECMAScript(JavaScript基础).DOM和BOM(Web APIs)组成. 其中,JavaScript基础是ECMAScript标准规定的基本语法:而 ...

  7. JavaScript Web APIs

    Web APIS Web APIs和JS关联性 JS基础:ECMAscript基本语法 Web APIs阶段:DOM+BOM//页面交互功能 API和Web API API(应用程序编程接口):是一些 ...

  8. js Web APIs

    js Web APIs DOM 获取元素 获取页面元素的方法:1.根据ID获取 ​ 2.根据标签名获取 ​ 3.通过HTML5新增的方法获取 ​ 4.特殊元素获取 根据ID获取 使用getElemen ...

  9. 黑马JavaScript核心操作BOM与DOM课程笔记1-DOM

    一.Web APIs 简介 此部分的目标:能够说出 Web APIs 阶段与 JavaScript 语法阶段的关联性: 能够说出什么是 API: 能够说出什么是 Web API. 1. Web API ...

最新文章

  1. javascript操作对象的方法
  2. LeetCode-剑指 Offer 52. 两个链表的第一个公共节点
  3. k8s之kubebuilder简单理解
  4. android 常见分辨率(mdpi、hdpi 、xhdpi、xxhdpi )及屏幕适配注意事
  5. ZOJ3865:Superbot(BFS) The 15th Zhejiang University Programming Contest
  6. java过滤器api_springboot集成过滤器
  7. UVA 1645 - Count(简单DP)
  8. 2017 Multi-University Training Contest - Team 5:1001. Rikka with Candies(手写bitset)
  9. 怎么在大数据里面删除不了_数据库删除大数据怎么操作
  10. 插入数据并返回插入数据ID
  11. log4j.xml按照日期生成_荐读 | 进项发票快速生成凭证!这个功能太方便了!
  12. Java——异常处理,数据库连接
  13. 什么是k近邻算法,K近邻算法:Fackbook最近入住预测
  14. 2021-11-09 jQuery常用方法及事件
  15. 用Jekyll生成网页部署的若干问题
  16. Dicom标签之(0020,0037) Image Orientation (Patient)
  17. android 智能电视 电视盒子 安卓嵌入式硬件LAN压力测试
  18. ORACLE 字符集修改,ZHS16GBK转US7ASCII
  19. 数据链路层的基本功能简单总结
  20. android时间格式化

热门文章

  1. 基于webScoket的在线客服聊天
  2. GB28181 PS流传输格式详解
  3. SVA16.14并发断言翻译笔记$inferred_clock和$inferred_disable(六)
  4. rf 433/868MHZ sub-1g 无线通信知识系列(1):RF信道冲突
  5. 生成地球人申请表、逍遥游pdf
  6. Kubernetes学习之路(一)之概念和架构解析和证书创建和分发
  7. 日拱一卒无有尽,功不唐捐终入海
  8. 3元特卖好课AngularJs基础视频教程 大漠穷秋AngularJs基础教程 AngularJs入门教程
  9. 咦?智能颈部按摩仪还能语音播报,快搞起来!
  10. 带三维团队半年的一点总结和想法