JavaScript教程(二)

BOM浏览器对象模型

什么是BOM

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

BOM的构成

在这里插入图片描述

  • window对象是浏览器的顶级对象,它具有双重角色;
    (1)、它是JS访问浏览器窗口的一个接口;
    (2)、它是一个全局对象。定义在全局作用域中的变量、函数都会变成window对象的属性和方法;在调用的时候可以省略window,对话框alert() prompt() 等就属于window对象方法;
    注意:window下的一个特殊属性windw.name
var num=10;
console.log(num) // 10
console.log(window.num)// 10

function fn(){
	console.log(111)
}
fn();
window.fn();

window对象的常见事件

1、窗口加载事件

window.onload=function(){}
或者
window.addEventListener('load',function(){})
  • window.onload 是窗口(页面)加载事件,当文档内容完全加载完成会触发该事件(包括图像、脚本文件、CSS文件等),就调用的处理函数;
<script>
	window.onload=function(){
		var btn=document.querySelector('button');
		btn.addEventListener('click',function(){
			alert('111')
		})
	}
</script>
<button>点击</button>
  • 注意:
    (1)、有了window.onload 就可以把JS代码写到页面元素的上方,因为onload是等页面内容全部加载完毕,再去执行处理函数;
    (2)、window.onload 【传统注册事件方式】只能写一次,如果有多个,会以最后一个window.onload为准;
    (3)、如果使用addEventListener则没有限制;
//提倡写法(没有限制)
<script>
window.addEventListener('load',function(){
	var btn=document.querySelector('button');
	btn.addEventListener('click',function(){
		alert('111')
	})
})	
</script>
<button>点击</button>
  • window.addEventListener('DOMContentLoaded',function(){}) DOMContentLoaded事件触发时,仅当DOM元素加载完成,不包括样式表、图片、flash等等(所以他的速度更快);IE9以上支持;如果页面的图片很多的话,从用户访问到onload触发可能需要较长的时间,交互效果就不能实现,必然影响用户体验,此时用DOMContentLoaded事件比较合适;
  • 注意:
    (1)、load等页面内容全部加载完毕,包含页面dom元素、图片、flash、css等等;
    (2)、DOMContentLoaded是dom加载完毕,不包含图片、flash、css等就可以执行,加载速度比load更快一些;

2、调整窗口大小事件

window.onresize=function(){};
window.addEventListener('resize',function(){});
  • window.onresize是调整窗口大小加载事件,当触发时就调用的处理函数;
  • 注意:
    (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>调整窗口大小事件</title>
    <style>
        div {
            width: 200px;
            height: 100px;
            background-color: pink;
        }
    </style>
</head>

<body>
    <script>
        window.addEventListener('load', function () {        
            var div = document.querySelector('div')
            window.addEventListener('resize', function () {
                console.log(window.innerWidth);
                if (window.innerWidth <= 800) {
                    div.style.display = 'none'
                } else {
                    div.style.display = 'block'
                }
            })
        })
    </script>
    <div></div>
</body>

</html>

定时器

  • window对象提供了两种定时器:setTimeout()setInterval()
setTimeout()定时器
  • window.setTimeout(调用函数,[延迟的毫秒数])这个window在调用的时候可以省略;
  • setTimeout()方法用于设置一个定时器,该定时器在定时器到期后执行调用函数;这个延时时间单位是毫秒,但是可以省略,如果省略默认的是0;
  • 这个调用函数可以直接写函数,还可以写函数名;还有一个写法'函数名()'
function callback(){
	console.log(11)
}
setTimeout(callback,300)
setTimeout('callback()',300)//不提倡使用
  • 页面中可以有很多定时器,我们经常给定时器加标识符(名字);
  • setTimeout()这个调用函数我们也称为【回调函数 callback】;普通函数是按照代码顺序直接调用的,而这个函数,需要等待时间,时间到了才去调用这个函数,因此称为回调函数;
停止setTimeout()定时器的window.clearTimeout(timeout ID)
  • window.clearTimeout(timeout ID)取消了先前通过调用setTimeout()建立的定时器;
  • window可以省略;
  • 括号里面的参数就是定时器的标识符;
setInterval('回调函数',间隔的毫秒数)定时器
  • setInterval()方法重复调用一个函数,每隔这个时间,就去调用一次回调函数;
注意:

(1)、setTimeout()延时时间到了,就去调用这个回调函数,只调用一次,就结束了这个定时器;
(2)、setInterval()每隔这个延时时间,就去调用这个回调函数,会调用很多次,重复调用这个函数;

案例:倒计时案例
<!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>倒计时案例</title>
    <style>
        div {
            width: 150px;
            height: 50px;
            color: aliceblue;
            display: flex;
            justify-content: space-around;
        }

        .hour,
        .minute,
        .second {
            width: 40px;
            height: 40px;
            line-height: 40px;
            text-align: center;
            background-color: black;
        }
    </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');


        /*
            注意:第一次执行也是间隔毫秒数,因此刚刷新页面会有空白;
                 最好采取封装函数的方式,这样可以先调动一次这个函数,防止刚开始刷新页面有空白问题;
        */
        countDown()

        // 2、开启定时器
        setInterval(countDown, 1000);

        var inputTime = +new Date('2023-7-6 11:00:00');//返回的是用户输入时间总的毫秒数
        function countDown() {
            var nowTime = +new Date(); //返回的是当前时间总的毫秒数                
            var times = (inputTime - nowTime) / 1000;//times是剩余时间总的秒数 

            var h = parseInt(times / 60 / 60 % 24);//小时
            //h = h < 10 ? '0' + h : h;
            console.log(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>
clearInterval 清除定时器
  • window.clearInterval(intervalID)
  • clearInterval方法取消了先前通过调用setInterval() 建立的定时器;
  • 注意:window可以省略;里面的参数就是定时器的标识符;
<body>
    <button class="begin">开启</button>
    <button class="stop">停止</button>

    <script>
        var begin = document.querySelector('.begin');
        var stop = document.querySelector('.stop');

        var timer = null //全局变量,null是一个空对象,如果不赋初始值容易报undefined错;(注意:将timer定义为全局变量,使下面的函数内部都能访问)
        begin.addEventListener('click', function () {
            timer = setInterval(function () {
                console.log('你好哇');
            }, 1000)
        })
        stop.addEventListener('click', function () {
            clearInterval(timer)
        })
    </script>
</body>
案例:发送短信案例(点击按钮之后,该按钮60秒之内不能再次点击,防止重复发送短信)
<body>
    <!-- 点击按钮之后,该按钮60秒之内不能再次点击,防止重复发送短信 -->
    手机号:
    <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)
        })
    </script>
</body>

this 指向问题

  • this 的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,一般情况下this 的最终指向的是那个调用它的对象;
  • 1、全局作用域 或者 普通函数中this指向 【全局对象 window(浏览器顶级对象 window)】(注意定时器里面的this指向 window);
console.log(this);
function fn() {
    console.log(this);
}
fn() //其实是 window.fn() 调用,省略了window
  • 2、方法调用中谁调用this指向谁;
var o = {
    sayHi: function () {
            console.log(this); //this指向的是 o 这个对象      {sayHi: ƒ}         
         }
}
o.sayHi()
  • 3、构造函数中this指向构造函数实例;
function Fun(){
     console.log(this);//this 指向的是fun 实例对象  Fun {}
}
var fun=new Fun();

JS执行队列

  • JS是单线程。JavaScript 语言的一大特点就是【单线程】,也就是说,同一个时间只能做一件事。这是因为JavaScript 这门脚本语言诞生的使命所致——JavaScript 是为处理页面中用户的交互,以及操作DOM而诞生的。比如我们对某个DOM元素进行添加和删除操作,不能同时进行,应该先进行添加,之后再删除;单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。这样所导致的问题是:如果JS执行的时间过长,这样就会造成页面渲染不连贯,导致页面渲染加载阻塞。
  • 为了解决这个问题,利用多核CPU的计算能力,HTML5 提出 Web Worker 标准,允许JavaScript 脚本创建多个线程。于是,JS中出现了同步异步

同步和异步

  • 同步:前一个任务结束后再执行后一个任务,程序的执行顺序和任务的排列顺序是一致的、同步的。
  • 异步:在做一件事情时,因为这件事情会花费很长时间,在做这件事的同时,还可以去处理其他事情;
  • 它们的本质区别:这条流水线上各个流程的执行顺序不同;
  • 同步任务:同步任务都在主线程执行,形成一个【执行栈】。
  • 异步任务:JS的异步是通过【回调函数】实现的。一般而言,异步任务有以下三种类型:
    (1)、普通事件,如:click resize 等;
    (2)、资源加载,如:load error 等;
    (3)、定时器,包括:setInterval setTimeout 等;
    异步任务相关【回调函数】添加到【任务队列】中(任务队列也称为【消息队列】)
  • JS执行机制:
    (1)、先执行执行栈中的同步任务;
    (2)、遇到异步任务时,将异步任务(回调函数)放入任务队列中;
    (3)、一旦执行栈中所有同步任务执行完毕,系统就会按次序读取 任务队列 中的异步任务,于是被读取的异步任务结束等待状态,进入执行栈,开始执行;
    在这里插入图片描述
    在这里插入图片描述
  • 由于主线程不断的重复获得任务、执行任务、再获取任务、再执行,所以这种机制被称为事件循环(event loop)。

location 对象

  • window 对象给我们提供了一个location属性用于获取或设置窗体的URL ,并且可以用于解析URL,因为这个属性返回的是一个对象,所以我们将这个属性也称为location 对象;
  • URL :统一资源定位符(Uniform Resource Locator)是互联网上标准的地址。互联网上的每个文件都有一个唯一的URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它。
    在这里插入图片描述

location 对象的属性

在这里插入图片描述

  • 重点记住:hrefsearch
<body>
    <button>点击</button>

    <script>
        var btn=document.querySelector('button');
        btn.addEventListener('click',function(){
            console.log(window.location.href);//此处的window一般省略不写
            location.href='https://www.baidu.com/' //点击跳转页面
        })
    </script>
</body>
案例:5秒钟之后自动跳转页面
<body>
    <button>点击</button>
    <div></div>

    <script>
        var btn = document.querySelector('button');
        var div = document.querySelector('div');
        // btn.addEventListener('click',function(){
        //console.log(window.location.href);//此处的window可以省略
        //     location.href='https://www.baidu.com/'
        // })
                    
        var time = 5;
        function fun() {
            if (time == 0) {
                location.href = 'https://www.baidu.com/'
            } else {
                div.innerHTML = '你将在' + time + '秒钟之后跳转到首页'
                time--
            }
        }
        fun()  
        setInterval(fun, 1000)
    </script>
</body>
案例:获取URL参数数据(主要练习数据在不同页面中的传递)
<!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>登录页</title>
</head>

<body>
    <form action="index.html">
        用户名:
        <input type="text" name="uname">
        <input type="submit" value="登录">
    </form>
</body>

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

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

<body>
    <h3>首页</h3>
    <div></div>
    <script>
        console.log(location.search);// ?uname=输入的用户名

        // 1、先去掉? substr('起始的位置',截取几个字符)
        var params = location.search.substr(1);//从下标为1的字符开始截取,截取到最后一位
        console.log(params); //uname=输入的用户名
        // 2、利用 = 把字符串分割为数组 split('=')
        var arr = params.split('=')
        console.log(arr);//['uname', '输入的用户名']
        var div=document.querySelector('div');
        // 3、把数据写入div中
        div.innerHTML=arr[1]+'欢迎你'
    </script>
</body>

</html>
location 常见方法

在这里插入图片描述

navigator 对象

  • navigator 对象包含有关浏览器的信息,它有很多属性,我们最常用的是userAgent,该属性可以返回由客户机发送服务器的user-agent头部的值。
    在这里插入图片描述

history对象

  • window对象给我们提供了一个history对象,与浏览器历史记录进行交互。该对象包含用户(在浏览器窗口中)访问过的URL。
    -
  • history对象一般在实际开发中比较少用,但是会在一些OA办公系统中见到;

PC端网页特效

元素偏移量offset 系列

offsetLeftoffsetTop获取元素偏移
  • offset翻译过来就是偏移量,我们使用offset系列相关属性可以动态的得到该元素的位置(偏移)、大小等;
  • 获得元素距离带有定位父元素的位置;
  • 获得元素自身的大小(宽度高度);
  • 注意:返回的数值都不带单位;
    在这里插入图片描述
<!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>offset系列属性</title>
    <style>
        body,
        html {
            margin: 0;
            padding: 0;
        }

        .father {
            width: 200px;
            height: 200px;
            background-color: pink;
            margin: 150px;
        }

        .son {
            width: 100px;
            height: 100px;
            background-color: purple;
            margin-left: 45px;
        }
    </style>
</head>

<body>
    <div class="father">
        <div class="son"></div>
    </div>
    <script>
        // offset系列
        var father = document.querySelector('.father');
        var son = document.querySelector('.son');
        // 可以得到元素的 偏移  位置  返回的不带单位的数值 (记得设置body的margin为0)
        console.log(father.offsetTop);//150 (margin: 100px;)
        console.log(father.offsetLeft);//150 (margin: 100px;)

        // 它以带有定位的父元素为准,如果没有父亲或者父亲没有定位,则以 body 为准;
        console.log(son.offsetLeft);// 195 (父元素的 margin 值 + 子元素的 margin 值)  
        // 可以得到元素的 大小 宽度 和 高度
    </script>
</body>

</html>
offsetWidthoffsetHeight获取元素大小
<!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>offset系列属性</title>
    <style>
        body,
        html {
            margin: 0;
            padding: 0;
        }      
        .w{
            width: 200px;
            height: 200px;
            background-color: skyblue;
            margin: 0 auto 200px;
        }
    </style>
</head>

<body>
    <div class="w"></div>
    <script>        
        // 可以得到元素的大小 宽度 和 高度(是包含 border + padding + width)
        console.log(w.offsetWidth);//200 (因为盒子的宽高是200  200)
        console.log(w.offsetHeight);//200
    </script>
</body>

</html>
offsetParent 返回带有定位的父亲,否则返回的是body
// 返回带有定位的父亲,否则返回的是body
console.log(son.offsetParent);//如果son的父元素开了定位,就返回其父元素;没有开定位,就返回body
console.log(son.parentNode);//返回父亲,是最近一级的父亲,不管父亲有没有定位
offsetstyle的区别

-

案例:获取鼠标在盒子内的坐标
<!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>获取鼠标在盒子内的坐标</title>
    <style>
        .box {
            width: 200px;
            height: 200px;
            background-color: pink;
        }
    </style>
</head>

<body>
    <!-- 
        案例分析:
        1、我们在盒子内点击,想要得到鼠标距离盒子左右的距离;
        2、首先得到鼠标在页面中的坐标(e.pageX,e.pageY);
        3、其次得到盒子在页面中的距离(box.offsetLeft,box.offsetTop);
        4、用鼠标距离页面的坐标减去盒子在页面中的距离,得到 鼠标在盒子内的坐标;
        5、补充:如果想要移动一下鼠标,就要获取最新的坐标,使用鼠标移动事件 mousemove ;
     -->
    <div class="box"></div>
    <script>
        var box = document.querySelector('.box');
        box.addEventListener('mousemove', function (e) {
            console.log(e.pageX);
            console.log(e.pageY);
            var x = e.pageX - this.offsetLeft;
            var y = e.pageY - this.offsetTop;
            this.innerHTML = 'x坐标是' + x + 'y坐标是' + y
        })
    </script>
</body>

</html>
案例:模态框拖拽
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>模态框拖拽</title>
    <style>
        h6,
        a {
            padding: 0;
            margin: 0;
        }

        .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>
    <!-- 
        案例分析:弹出框,我们也称为模态框
        1、点击弹出层,会弹出模态框,并且显示灰色半透明的遮挡层;
        2、点击关闭按钮,可以关闭模态框,并且同时关闭灰色半透明遮挡层;
        3、鼠标放到模态框最上面一行,可以按住鼠标拖拽模态框在页面中移动;
        4、鼠标松开,可以停止拖动模态框移动;
        5、在页面中拖拽的原理:鼠标按下并且移动,之后松开鼠标;
        6、触发事件是 鼠标按下:mousedown,鼠标移动:mousemove,鼠标松开:mouseup;
        7、拖拽过程:鼠标移动过程中,获得最新的值赋给模态框的 left 和 top 值,这样模态框可以跟着鼠标走了;
        8、鼠标按下触发的事件源是 最上面一行,就是 id 为 title;
        9、鼠标的坐标 减去 鼠标在盒子内的坐标,才是模态框真正的位置;
        10、鼠标按下,我们要得到鼠标在盒子内的坐标;
        11、鼠标移动,就让模态框的坐标 设置为:鼠标坐标减去盒子坐标即可,注意:移动事件写到按下事件里面;
     -->
    <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 for="">用户名:</label>
                <input type="text" placeholder="请输入用户名" name="info[username]" id="username" class="username">
            </div>
            <div class="login-input">
                <label for="">登录密码:</label>
                <input type="password" placeholder="请输入登录密码" name="info[password]" id="password" class="password">
            </div>
        </div>
        <div id="loginBtn" class="login-button"><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 和 login;
        closeBtn.addEventListener('click', function () {
            mask.style.display = 'none';
            login.style.display = 'none';
        })

        // 4、开始拖拽
        // (1)、当我们鼠标按下,就获得鼠标在盒子内的坐标;
        title.addEventListener('mousedown', function (e) {
            console.log('111');
            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>
案例:仿京东放大镜效果
/*
    案例分析:
     1、鼠标经过小图片盒子,黄色的遮挡层 和 大图片盒子显示,离开隐藏2个盒子功能;
     2、把鼠标坐标给遮挡层不合适,因为遮挡层坐标以父盒子为准;
     3、首先是获得鼠标在盒子的坐标;
     4、之后把数值给遮挡层作为 left 和 top 值;
     5、遮挡层不能超出小图片盒子的范围;
     6、如果小于0,就把坐标设置为0;
     7、如果大于遮挡层最大的移动距离,就把坐标设置为最大的移动距离;
     8、遮挡层的最大移动距离:小图片盒子宽度 减去 遮挡层盒子宽度;
*/
// 入口函数,让当页面上的布局加载完再加载动画效果
window.addEventListener('load', function () {
    var preview_img = document.querySelector('.preview_img');
    var mask = this.document.querySelector('.mask');
    var big = this.document.querySelector('.big');
    var bigImg = this.document.querySelector('.bigImg');

    // 1、当我们鼠标经过 preview_img 就显示和隐藏 mask 遮挡层 和 big 大盒子;
    preview_img.addEventListener('mouseover', function () {
        mask.style.display = 'block';
        big.style.display = 'block';
    })
    preview_img.addEventListener('mouseout', function () {
        mask.style.display = 'none';
        big.style.display = 'none';
    })
    // 2、鼠标移动的时候,让黄色的盒子跟着鼠标来走
    preview_img.addEventListener('mousemove', function (e) {
        // (1)、先计算出鼠标在盒子内的坐标
        var x = e.pageX - this.offsetLeft;
        var y = e.pageY - this.offsetTop;
        // (2)、减去盒子高度300的一半 是 150,就是我们 mask 的最终 left 和 top 了;此处 mask 的 left 和 top 不写死,防止盒子宽度发生变化;
        // (3)、我们 mask 移动的距离
        var maskX = x - mask.offsetWidth / 2;
        var maskY = y - mask.offsetHeight / 2;
        // (4)、如果x坐标小于了0,就让他停在0的位置;
        // 遮挡层的最大移动距离(正方形,宽高相等);
        var maskMax = preview_img.offsetWidth - mask.offsetWidth;
        if (maskX <= 0) {
            maskX = 0
        } else if (maskX >= maskMax) {
            maskX = maskMax
        }
        if (maskY <= 0) {
            maskY = 0
        } else if (maskY >= maskMax) {
            maskY = maskMax
        }
        mask.style.left = maskX + 'px';
        mask.style.top = maskY + 'px';
        // 大图片的移动距离,遮挡层移动距离 * 大图片最大移动距离 / 遮挡层的最大移动距离
        // 大图片最大移动距离
        var bigMax = bigImg.offsetWidth - big.offsetWidth;
        // 大图片的移动距离 X Y(注意:需要给图片加上定位,否则无法实现移动)
        var bigX = maskX * bigMax / maskMax;
        var bigY = maskY * bigMax / maskMax;
        bigImg.style.left = -bigX + 'px';
        bigImg.style.top = -bigY + 'px';
    })
})

元素可视区client 系列

  • client 翻译过来就是客户端,我们使用client 系列的相关属性来获取元素可视区的相关信息。通过client 系列的相关属性可以动态的得到该元素的边框大小、元素大小等;
    在这里插入图片描述
<!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>元素可视区client系列</title>
    <style>
        div {
            width: 200px;
            height: 200px;
            background-color: pink;
            border: 10px solid red; /* 不包含 border */
            padding: 10px; /* 包含 padding */
        }
    </style>
</head>

<body>
    <div></div>

    <script>
        //client 宽度 和 我们 offsetWidth 最大的区别就是 不包含边框;
        var div = document.querySelector('div');
        console.log(div.clientWidth);//220 包含padding       
    </script>
</body>

</html>
案例:淘宝flexible.js源码分析
立即执行函数
  • 立即执行函数:不需要调用,立马能够自己执行的函数;
  • 也可以传递多个参数进来;
  • 如果有多个立即执行函数,结尾要用分号分隔开;
  • 立即执行函数最大的作用就是,独立创建了一个作用域;里面所有的变量都是局部变量,不会有命名冲突的情况;
<script>
         //2、立即执行函数写法
        // 写法一:
         (function () { })();
        // 写法二:
         (function () { }());

         (function(){
             console.log(2);
         })(); //第二个小括号可以看作是调用函数

         (function(a){
             console.log(a);//1
         })(1) ;

        (function (a, b) {
            console.log(a + b);//1
            var num = 10;//num是一个局部变量
        })(1, 2); //3
    </script>
pageshowload 事件分析
  • 下面3种情况都会刷新页面都会触发load 事件:
    (1)、a 标签的超链接;
    (2)、F5或者刷新按钮(强制刷新);
    (3)、前进、后退按钮;
  • 但是,火狐中有个特点,有个“往返缓存”,这个缓存中不仅保存着页面数据,还保存了DOMJavaScript的状态;实际上是将整个页面都保存在了内存里;所以,此时后退按钮不能刷新页面;
  • 此时可以使用pageshow事件来触发,这个事件在页面显示时触发,无论页面是否来自缓存。在重新加载页面中,pageshow会在load事件触发后触发;根据事件对象中的persisted来判断是否是缓存中的页面触发的pageshow事件,注意这个事件给window添加;
(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元素加载完毕再去设置body
            // 的字体大小
            document.addEventListener('DOMContentLoaded', setBodyFontSize)
        }
    }
    setBodyFontSize();

    // set 1rem = viewWidth / 10    设置我们html 元素的文字大小
    function setRemUnit() {
        var rem = docEl.clientWidth / 10 //将屏幕大小划分成10等份
        docEl.style.fontSize = rem + 'px'
    }

    setRemUnit()

    // reset rem unit on page resize  当我们页面尺寸大小发生变化的时候,要重新设置下rem 的大小
    window.addEventListener('resize', setRemUnit)
        // pageshow 是我们重新加载页面触发的事件
    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) {
            docEl.classList.add('hairlines')
        }
        docEl.removeChild(fakeBody)
    }
}(window, document))

元素滚动scroll 系列

  • scroll 翻译过来就是滚动,我们使用scroll 系列的相关属性可以动态的得到该元素的大小、滚动距离等等;
  • scroll是自身实际内容的大小;如一个div内内容比较多,超出盒子高度,返回的是实际内容的大小;
    在这里插入图片描述
    在这里插入图片描述
页面被卷去的头部
  • 如果浏览器的高(或宽)度不足以显示整个页面时,会自动出现滚动条。当滚动条向下滚动时,页面上面被隐藏掉的高度,我们就称为页面被卷去的头部。滚动条在滚动时会触发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>仿淘宝固定右侧侧边栏</title>
    <style>
        .slider-bar {
            position: absolute;
            left: 50%;
            top: 300px;
            margin-left: 600px;
            width: 45px;
            height: 130px;
            background-color: pink;
        }

        .w {
            width: 1200px;
            margin: 10px auto;
        }

        .header {
            height: 150px;
            background-color: purple;
        }

        .banner {
            height: 250px;
            background-color: skyblue;
        }

        .main {
            height: 1000px;
            background-color: yellowgreen;
        }

        span {
            display: none;
            position: absolute;
            bottom: 0;
        }
    </style>
</head>

<body>
    <!-- 
        案例分析:
           1、原先侧边栏是绝对定位;
           2、当页面滚动到一定位置,侧边栏改为固定定位;
           3、页面继续滚动,会让返回顶部显示出来;
           4、需要用到页面滚动事件 scroll,因为是页面滚动,所以事件源是 document;
           5、滚动到某个位置,就是判断页面被卷去的上部值;
           6、页面被卷去的头部,可以通过 window.pageYOffset 获得;如果是被卷去的左侧 window.pageXOffset;
           7、元素被卷去的头部是:element.scrollTop;如果是页面被卷去的头部则是:window.pageYOffset;
     -->
    <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');
        console.log(banner.offsetTop);
        // banner.offsetTop 就是被卷去头部的大小,一定要写到滚动的外面;
        var bannerTop = banner.offsetTop;
        //当我们侧边栏固定定位之后应该变化的数值; 
        var sliderbarTop = sliderbar.offsetTop - bannerTop;
        var main = document.querySelector('.main');
        var goBack = document.querySelector('.goBack');
        var mainTop = main.offsetTop;
        // 2、页面滚动事件 scroll
        document.addEventListener('scroll', function () {
            // 3、当页面被卷去的头部大于等于了 172 时,侧边栏就要改为固定定位;
            if (window.pageYOffset >= bannerTop) {
                sliderbar.style.position = 'fixed';
                sliderbar.style.top = sliderbarTop + 'px';
            } else {
                sliderbar.style.position = 'absolute';
                sliderbar.style.top = '300px';
            }
            // 4、当页面滚动到main盒子,就显示 goBack 模块;
            if (window.pageYOffset >= bannerTop) {
                goBack.style.display = 'block';
            } else {
                goBack.style.display = 'none';
            }
        })
    </script>
</body>

</html>

在这里插入图片描述

三大系列总结

  • offset系列经常用于获得元素位置 offsetLeft offsetTop
  • client经常用于获取元素大小clientWidth clientHeight
  • scroll经常用于获取滚动距离scrollTop scrollLeft
  • 注意:页面滚动的距离通过window.pageXOffset获得;

mouseentermouseover的区别

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

动画函数封装

动画实现原理

  • 核心原理:通过定时器setInterval()不断移动盒子位置;
<!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>动画原理</title>
    <style>
        div {
            position: absolute;
            left: 0;
            width: 100px;
            height: 100px;
            background-color: pink;
        }
    </style>
</head>

<body>
      <!-- 
        实现步骤:
          (1)、获得盒子当前位置;
          (2)、让盒子在当前位置加上1个移动距离;
          (3)、利用定时器不断重复这个操作;
          (4)、加一个结束定时器的条件;
          (5)、注意此元素需要添加定位,才能使用`element.style.left`-->
    <div></div>

    <script>
        var div = document.querySelector('div')
        var timer = setInterval(function () {
            if (div.offsetLeft >= 400) {
                // 停止定时器
                clearInterval(timer)
            }
            div.style.left = div.offsetLeft + 1 + 'px';
        }, 30)
    </script>
</body>

</html>

动画函数简单封装

  • 注意:函数需要传递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>动画原理</title>
    <style>
        div {
            position: absolute; /* 一定要加定位,否则动画不生效 */      
            left: 0;
            width: 100px;
            height: 100px;
            background-color: pink;
        }
    </style>
</head>

<body>
    <!-- 
        实现步骤:
          (1)、获得盒子当前位置;
          (2)、让盒子在当前位置加上1个移动距离;
          (3)、利用定时器不断重复这个操作;
          (4)、加一个结束定时器的条件;
          (5)、注意此元素需要添加定位,才能使用`element.style.left`-->
    <div></div>

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

        var div = document.querySelector('div');
        animate(div, 300);
    </script>
</body>

</html>

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

  • 如果多个元素都使用这个动画函数,每次都要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>动画原理</title>
    <style>
        div {
            position: absolute;/* 一定要加定位,否则动画不生效 */   
            top: 50px;        
            left: 0;
            width: 100px;
            height: 100px;
            background-color: pink;
        }
    </style>
</head>

<body>
    <!-- 
        实现步骤:
          (1)、获得盒子当前位置;
          (2)、让盒子在当前位置加上1个移动距离;
          (3)、利用定时器不断重复这个操作;
          (4)、加一个结束定时器的条件;
          (5)、注意此元素需要添加定位,才能使用`element.style.left`-->
    <div></div>
    <button>点击开始行走</button>

    <script>
        // var obj = {};
        // obj.name = 'andy';

        // 简单动画函数封装,obj 目标对象;target 目标位置;
        function animate(obj, target) {
            // 当我们不断的点击按钮,这个元素的速度会越来越快,因为开启了太多的定时器;
            // 解决方案就是:让元素只有一个定时器执行;
            // 先清除以前的定时器,只保留当前的一个定时器执行;
            clearInterval(obj.timer);

            //var timer = setInterval(function () {  //优化之前的写法
            obj.timer = setInterval(function () {   //优化后:不用var声明变量也就不用去内存中重新开辟空间了;给不同的元素指定了不同的定时器
                if (obj.offsetLeft >= target) {
                    // 停止定时器
                    clearInterval(timer);
                } else {
                    obj.style.left = obj.offsetLeft + 1 + 'px';
                }
            }, 30)
        }

        var div = document.querySelector('div');
        var btn = document.querySelector('button');
        // 当我们不断的点击按钮,这个元素的速度会越来越快,因为开启了太多的定时器;
        // 解决方案就是:让元素只有一个定时器执行; 先清除以前的定时器,只保留当前的一个定时器执行;
        btn.addEventListener('click', function () {
            animate(div, 300);
        })
    </script>
</body>

</html>

缓动效果原理

  • 缓动动画就是让元素运动速度有所变化,最常见的就是让速度慢慢停下来;
  • 思路:
    (1)、让盒子每次移动的距离慢慢变小,速度就会慢慢落下来;
    (2)、核心算法:(目标值 - 现在的位置) / 10 作为每次移动的距离(即步长);
    (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>缓动动画</title>
    <style>
        div {
            /* 一定要加定位,否则动画不生效 */
            position: absolute;
            top: 50px;
            left: 0;
            width: 100px;
            height: 100px;
            background-color: pink;
        }
    </style>
</head>

<body>
    <div></div>
    <button>点击开始行走</button>
    <!-- 
     思路:
      (1)、让盒子每次移动的距离慢慢变小,速度就会慢慢落下来;
      (2)、核心算法:(目标值 - 现在的位置) / 10   作为每次移动的距离(即步长);
      (3)、停止的条件是:让当前盒子位置等于目标位置就停止定时器;
    -->
    <script>
        function animate(obj, target) {
            clearInterval(obj.timer);

            obj.timer = setInterval(function () {
                // 步长值写到定时器的里面
                var step = (target - obj.offsetLeft) / 10;
                if (obj.offsetLeft == target) {
                    clearInterval(timer);
                } else {
                    // 把每次加1 这个步长值改为一个慢慢变小的值,步长公式:(目标值 - 现在的位置) / 10
                    obj.style.left = obj.offsetLeft + step + 'px';
                }
            }, 15)
        }

        var div = document.querySelector('div');
        var btn = document.querySelector('button');
        btn.addEventListener('click', function () {
            animate(div, 500);
        })
        //匀速动画:盒子当前的位置 + 固定的值
        // 缓动动画:盒子当前的位置 + 变化的值 【(目标值 - 现在的位置) / 10】
    </script>
</body>

</html>

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

  • 可以让动画函数从800移动到500;
  • 当我们点击按钮时,判断步长是正直还是负值;
    1、如果是正值,则步长往大了取整;
    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>缓动动画</title>
    <style>
        div {
            /* 一定要加定位,否则动画不生效 */
            position: absolute;
            top: 80px;
            left: 0;
            width: 100px;
            height: 100px;
            background-color: pink;
        }
    </style>
</head>

<body>
    <div></div>
    <button class="btn500">点击开始行走500</button>
    <button class="btn800">点击开始行走800</button>
    <!-- 
     思路:
      (1)、让盒子每次移动的距离慢慢变小,速度就会慢慢落下来;
      (2)、核心算法:(目标值 - 现在的位置) / 10   作为每次移动的距离(即步长);
      (3)、停止的条件是:让当前盒子位置等于目标位置就停止定时器;
      (4)、注意步长值需要取整;
    -->
    <script>
        function animate(obj, target) {
            clearInterval(obj.timer);

            obj.timer = setInterval(function () {
                // 步长值写到定时器的里面;把步长值改为整数,不要出现小数问题;
                //var step = Math.ceil((target - obj.offsetLeft) / 10);
                var step = (target - obj.offsetLeft) / 10;
                step = step > 0 ? Math.ceil(step) : Math.floor(step);
                if (obj.offsetLeft == target) {
                    clearInterval( obj.timer);
                } else {
                    // 把每次加1 这个步长值改为一个慢慢变小的值,步长公式:(目标值 - 现在的位置) / 10
                    obj.style.left = obj.offsetLeft + step + 'px';
                }
            }, 15)
        }

        var div = document.querySelector('div');
        var btn500 = document.querySelector('.btn500');
        var btn800 = document.querySelector('.btn800');
        btn500.addEventListener('click', function () {
            animate(div, 500);
        })
        btn800.addEventListener('click', function () {
            animate(div, 800);
        })
        //匀速动画:盒子当前的位置 + 固定的值;
        //缓动动画:盒子当前的位置 + 变化的值 【(目标值 - 现在的位置) / 10】;
    </script>
</body>

</html>

缓动动画添加回调函数

  • 回调函数原理:函数可以作为一个参数。将这个函数作为参数传到另一个函数里面,当那个函数执行完之后,再执行传进去的这个函数,这个过程就叫做回调
  • 回调函数写到定时器结束里面;
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>缓动动画</title>
    <style>
        div {
            /* 一定要加定位,否则动画不生效 */
            position: absolute;
            top: 80px;
            left: 0;
            width: 100px;
            height: 100px;
            background-color: pink;
        }
    </style>
</head>

<body>
    <div></div>
    <button class="btn500">点击开始行走500</button>
    <button class="btn800">点击开始行走800</button>
    <!-- 
     思路:
      (1)、让盒子每次移动的距离慢慢变小,速度就会慢慢落下来;
      (2)、核心算法:(目标值 - 现在的位置) / 10   作为每次移动的距离(即步长);
      (3)、停止的条件是:让当前盒子位置等于目标位置就停止定时器;
      (4)、注意步长值需要取整;
    -->
    <script>
        function animate(obj, target, callback) {
            console.log(callback); // callback = function(){} 调用的时候 callback()

            clearInterval(obj.timer);
            obj.timer = setInterval(function () {
                // 步长值写到定时器的里面;把步长值改为整数,不要出现小数问题;
                //var step = Math.ceil((target - obj.offsetLeft) / 10);
                var step = (target - obj.offsetLeft) / 10;
                step = step > 0 ? Math.ceil(step) : Math.floor(step);
                if (obj.offsetLeft == target) {
                    clearInterval(obj.timer);
                    // 回调函数写到定时器结束里面
                    if (callback) {
                        // 调用函数
                        callback();
                    }
                } else {
                    // 把每次加1 这个步长值改为一个慢慢变小的值,步长公式:(目标值 - 现在的位置) / 10
                    obj.style.left = obj.offsetLeft + step + 'px';
                }
            }, 15)
        }

        var div = document.querySelector('div');
        var btn500 = document.querySelector('.btn500');
        var btn800 = document.querySelector('.btn800');
        btn500.addEventListener('click', function () {
            animate(div, 500);
        })
        btn800.addEventListener('click', function () {
            animate(div, 800, function () {
                div.style.backgroundColor = 'red';
            });
        })
        //匀速动画:盒子当前的位置 + 固定的值;
        //缓动动画:盒子当前的位置 + 变化的值 【(目标值 - 现在的位置) / 10】;
    </script>
</body>

</html>

动画函数的使用

  • 动画函数封装到单独js文件里面:因为以后经常使用这个动画函数,可以单独封装到一个js文件里面,使用的时候引用这个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>引用animate动画函数</title>
    <style>
        .sliderbar {
            position: fixed;
            right: 0;
            bottom: 100px;
            width: 40px;
            height: 40px;
            text-align: center;
            line-height: 40px;
            cursor: pointer;
            color: #fff;
        }

        .con {
            position: absolute;
            left: 0;
            top: 0;
            width: 200px;
            height: 40px;
            background-color: purple;
            z-index: -1;
        }
    </style>
    <script src="54.animate.js"></script>
</head>

<body>
    <div class="sliderbar">
        <span></span>
        <div class="con">问题反馈</div>
    </div>

    <script>
        /*
            1、当我们鼠标经过 sliderbar ,就会让 con 这个盒子滑动到左侧;
            2、当我们鼠标离开 sliderbar,就会让 con 这个盒子滑动到右侧;
        */
        var sliderbar = document.querySelector('.sliderbar');
        var con = document.querySelector('.con');
        sliderbar.addEventListener('mouseenter', function () {
            animate(con, -160, function () {
                // 当动画执行完毕,就把 ← 改为 →
                sliderbar.children[0].innerHTML = '→';
            });
        })
        sliderbar.addEventListener('mouseleave', function () {
            animate(con, 0, function () {
                // 当动画执行完毕,就把 ← 改为 →
                sliderbar.children[0].innerHTML = '←';
            });
        })
    </script>
</body>

</html>

网页轮播图

  • 轮播图也称为焦点图;
  • 功能需求:
    (1)、鼠标经过轮播图模块,左右按钮显示,离开隐藏左右按钮;
    (2)、点击右侧按钮一次,图片往左播放一张,以此类推,左侧按钮同理;
    (3)、图片播放的同时,下面小圆圈模块跟随一起变化;
    (4)、点击小圆圈,可以播放相应图片;
    (5)、鼠标不经过轮播图,轮播图也会自动播放图片;
    (6)、鼠标经过轮播图模块,自动播放停止;
  • 动态生成小圆圈:
    (1)、核心思路:小圆圈的个数要跟图片张数一致;
    (2)、所以首先先得到ul里面图片的张数(图片放入li里面,所以就是li的个数);
    (3)、利用循环动态生成小圆圈(这个小圆圈要放入ol里面);
    (4)、创建节点createElement('li')
    (5)、插入节点ol.appendChild(li)
  • 小圆圈的排他思想:
    (1)、点击当前小圆圈,就添加current类;
    (2)、其余的小圆圈,就移除current类;
    (3)、注意:我们在刚才生成小圆圈的同时,就可以直接绑定这个点击事件了;
  • 点击小圆圈滚动图片:
    (1)、此时用到animate.js动画函数,将js文件引入。(注意:因为index.js依赖animate.js,所以,引入时animate.js要写到index.js上面);
    (2)、使用动画函数的前提,该元素必须有定位;
    (3)、注意是ul移动,而不是li
    (4)、滚动图片的核心算法:点击某个小圆圈,就让图片滚动 【小圆圈的索引号 x 图片的宽度】作为ul移动距离;
    (5)、此时需要知道小圆圈的索引号:我们可以在生成小圆圈的时候,给他设置一个自定义属性,点击的时候获取这个自定义属性即可;
  • 点击右侧按钮一次,就让图片滚动一次:
    (1)、声明一个变量num,点击一次,自增1,让这个变量乘以图片宽度,就是ul的滚动距离;
  • 图片无缝滚动原理:
    (1)、把ul第一个li复制一份,放到ul的最后面;
    (2)、当图片滚动到克隆的最后一张图片时,让ul快速的、不做动画的跳到最左侧,即left=0
    (3)、同时num赋值为0,可以从新开始滚动图片了;
  • 克隆第一张图片:
    (1)、克隆ul第一个licloneNode()true就是深克隆,复制里面的子节点;false浅克隆;
    (2)、添加到ul最后面appendChild
  • 点击右侧按钮,小圆圈跟随变化:
    (1)、最简单的做法是再声明一个变量circle,每次点击自增1;注意:左侧按钮也需要这个变量,因此要声明全局变量;
    (2)、但是,图片有5张,我们小圆圈只有4个少一个,必须加一个判断条件;
    (3)、如果circle == 4,就重新复原为0;
  • 自动播放功能:
    (1)、添加一个定时器;
    (2)、自动播放轮播图,实际就类似于点击了右侧按钮;
    (3)、此时我们使用【手动调用】右侧按钮点击事件arrow_r.click()
    (4)、鼠标经过focus就停止定时器;
    (5)、鼠标离开focus就开启定时器;
window.addEventListener('load', function () {
    // 1、获取元素
    var arrow_l = this.document.querySelector('.arrow-l');
    var arrow_r = this.document.querySelector('.arrow-r');
    var focus = this.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;//清除定时器bianl
    })
    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'); // 获取 focus 里面的 ul;
    var ol = focus.querySelector('.circle');
    // console.log(ul.children.length);
    for (var i = 0; i < ul.children.length; i++) {
        // 创建一个小 li
        var li = document.createElement('li');
        // 记录当前小圆圈的索引号,通过自定义属性来做;
        li.setAttribute('index', i);
        // 把小li插入到ol里面
        ol.appendChild(li);
        // 4、小圆圈的排他思想 我们可以直接在生成小圆圈的同时直接绑定点击事件
        li.addEventListener('click', function () {
            // 干掉所有人 把所有的小li 清除 current 类名
            for (var i = 0; i < ol.children.length; i++) {
                ol.children[i].className = '';
            }
            // 留下我自己 当前的小li 设置 current 类名
            this.className = 'current';
            // 5、点击小圆圈移动图片,当然移动的是ul
            // ul的移动距离 = 小圆圈的索引号 x 图片的宽度  注意是负值
            // 当我们点击了某个li,就拿到当前小li的索引号
            var index = this.getAttribute('index');
            //当我们点击了某个小li,就拿到当前小li的索引号给 num 
            num = index;
            //当我们点击了某个小li,就拿到当前小li的索引号给 clrcle 
            clrcle = index;
            console.log('index:' + index);
            console.log('num:' + num);
            console.log('circle:' + circle);
            animate(ul, -index * focusWidth);
        })
    }
    // 把ol里面的第一个小li设置类名为current;
    ol.children[0].className = 'current';
    // 6、克隆第一张图片(li)放到ul最后面;(在生成小圆圈的后面克隆的,所以页面上还是只有4个小圆圈)
    var first = ul.children[0].cloneNode(true);
    ul.appendChild(first);
    // 7、点击右侧按钮,图片滚动一张
    var num = 0;
    var circle = 0; //circle 用来控制小圆圈的播放
    // 右侧按钮
    arrow_r.addEventListener('click', function () {
        // 如果走到了最后复制的一张图片,此时,我们的ul要快速复原  left改为0;
        if (num == ul.children.length - 1) {//图片的长度减一
            ul.style.left = 0;
            num = 0;
        }
        num++;
        console.log('num:' + num);
        animate(ul, -num * focusWidth);
        // 8、点击右侧按钮,小圆圈跟随一起变化,可以再声明一个变量控制小圆圈的播放;
        circle++;
        console.log('circle:' + circle);
        // 如果 circle==4,说明走到最后我们克隆的这张图片了,我们就复原;
        if (circle == ol.children.length) {
            circle = 0;
        }
        // 调用函数
        circleChange();
    })

    // 9、左侧按钮
    arrow_l.addEventListener('click', function () {
        // 如果走到了最后复制的一张图片,此时,我们的ul要快速复原  left改为0;
        if (num == 0) {//图片的长度减一
            num = ul.children.length - 1;
            ul.style.left = -num * focusWidth + 'px';
        }
        num--;
        console.log('num:' + num);
        animate(ul, -num * focusWidth);
        // 8、点击右侧按钮,小圆圈跟随一起变化,可以再声明一个变量控制小圆圈的播放;
        circle--;
        console.log('circle:' + circle);
        // 如果 circle<0,说明第一张图片,则小圆圈要改为第4个小圆圈;
        // 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 = setInterval(function () {
        // 手动调用点击事件
        arrow_r.click();
    }, 1000)
})

节流阀以及逻辑中断应用

节流阀

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

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

    }, 15);
}

案例:返回顶部

  • 滚动窗口至文档中的特定位置,window.scroll(x,y)
  • 注意:里面的x, y 不跟单位,直接写数字即可;
  • 带有动画的返回顶部:此时可以继续使用我们封装的动画函数;只需要把所有的left相关的值改为跟页面垂直滚动距离相关就可以了;页面滚动了多少,可以通过window.pageYOffset得到;
  • 最后是页面滚动,使用window.scroll(x,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>仿淘宝固定右侧侧边栏</title>
    <style>
        .slider-bar {
            position: absolute;
            left: 50%;
            top: 300px;
            margin-left: 600px;
            width: 45px;
            height: 130px;
            background-color: pink;
        }

        .w {
            width: 1200px;
            margin: 10px auto;
        }

        .header {
            height: 150px;
            background-color: purple;
        }

        .banner {
            height: 250px;
            background-color: skyblue;
        }

        .main {
            height: 1000px;
            background-color: yellowgreen;
        }

        span {
            display: none;
            position: absolute;
            bottom: 0;
        }
    </style>
</head>

<body>
    <!-- 
        案例分析:
           1、原先侧边栏是绝对定位;
           2、当页面滚动到一定位置,侧边栏改为固定定位;
           3、页面继续滚动,会让返回顶部显示出来;
           4、需要用到页面滚动事件 scroll,因为是页面滚动,所以事件源是 document;
           5、滚动到某个位置,就是判断页面被卷去的上部值;
           6、页面被卷去的头部,可以通过 window.pageYOffset 获得;如果是被卷去的左侧 window.pageXOffset;
           7、元素被卷去的头部是:element.scrollTop;如果是页面被卷去的头部则是:window.pageYOffset;
     -->
    <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');
        console.log(banner.offsetTop);
        // banner.offsetTop 就是被卷去头部的大小,一定要写到滚动的外面;
        var bannerTop = banner.offsetTop;
        //当我们侧边栏固定定位之后应该变化的数值; 
        var sliderbarTop = sliderbar.offsetTop - bannerTop;
        var main = document.querySelector('.main');
        var goBack = document.querySelector('.goBack');
        var mainTop = main.offsetTop;
        // 2、页面滚动事件 scroll
        document.addEventListener('scroll', function () {
            // 3、当页面被卷去的头部大于等于了 172 时,侧边栏就要改为固定定位;
            if (window.pageYOffset >= bannerTop) {
                sliderbar.style.position = 'fixed';
                sliderbar.style.top = sliderbarTop + 'px';
            } else {
                sliderbar.style.position = 'absolute';
                sliderbar.style.top = '300px';
            }
            // 4、当页面滚动到main盒子,就显示 goBack 模块;
            if (window.pageYOffset >= bannerTop) {
                goBack.style.display = 'block';
            } else {
                goBack.style.display = 'none';
            }
        })
        // 当我们点击了返回顶部模块,就让窗口滚动到页面的最上方;
        goBack.addEventListener('click', function () {
            // window.scroll(x, y); 里面的x, y 不跟单位,直接写数字即可;
            //window.scroll(0, 0); // 滚动的太快
            //缓慢滚动到顶部 
            //因为是窗口滚动,所以对象是window;因为是滚动到页面的最顶端,所以target是0;
            animate(window, 0);
        })

        // 动画函数
        function animate(obj, target, callback) {
            console.log(callback); // callback = function(){} 调用的时候 callback()
            clearInterval(obj.timer);
            obj.timer = setInterval(function () {
                // 步长值写到定时器的里面;把步长值改为整数,不要出现小数问题;
                //var step = Math.ceil((target - obj.offsetLeft) / 10);
                var step = (target - window.pageYOffset) / 10;
                step = step > 0 ? Math.ceil(step) : Math.floor(step);
                if (window.pageYOffset == target) {
                    clearInterval(obj.timer);
                    // 回调函数写到定时器结束里面
                    if (callback) {
                        // 调用函数
                        callback();
                    }
                } else {
                    window.scroll(0, window.pageYOffset + step)
                }
            }, 15)
        }
    </script>
</body>

</html>

案例:筋斗云案例

  • 鼠标经过某个小li,筋斗云跟着到当前小li位置;
  • 鼠标离开这个小li,筋斗云复原为原来的位置;
  • 鼠标点击了某个小li,筋斗云就会留在点击这个小li的位置;
  • 案例分析:
    (1)、利用动画函数做动画效果;
    (2)、原先筋斗云的起始位置是0;
    (3)、鼠标经过某个li,把当前小li的offsetLeft位置作为目标值即可;
    (4)、鼠标离开某个小li,就把目标值设为0;
    (5)、如果点击了某个小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>筋斗云导航栏案例</title>
    <style>
        * {
            margin: 0;
            padding: 0
        }

        ul {
            list-style: none;
        }

        body {
            background-color: black;
        }

        .c-nav {
            width: 900px;
            height: 42px;
            background: #fff url(images/rss.png) no-repeat right center;
            margin: 100px auto;
            border-radius: 5px;
            position: relative;
        }

        .c-nav ul {
            position: absolute;
        }

        .c-nav li {
            float: left;
            width: 83px;
            text-align: center;
            line-height: 42px;
        }

        .c-nav li a {
            color: #333;
            text-decoration: none;
            display: inline-block;
            height: 42px;
        }

        .c-nav li a:hover {
            color: white;
        }

        .cloud {
            position: absolute;
            left: 0;
            top: 0;
            width: 83px;
            height: 42px;
            background: url(images/cloud.gif) no-repeat;
        }
    </style>
    <script src="./animate.js"></script>
    <script>
        window.addEventListener('load', function () {
            // 1、获取元素
            var cloud = document.querySelector('.cloud');
            var c_nav = document.querySelector('.c-nav');
            var lis = c_nav.querySelectorAll('li');
            // 给所有的小li绑定事件
            // 这个current作为筋斗云的起始位置
            var current = 0;
            for (var i = 0; i < lis.length; i++) {
                // (1)、鼠标经过把当前小li的位置作为目标值;
                lis[i].addEventListener('mouseenter', function () {
                    animate(cloud, this.offsetLeft);
                });
                // (2)、鼠标离开就回到起始的位置;(第一次鼠标离开回到0的位置)
                lis[i].addEventListener('mouseleave', function () {
                    animate(cloud, current);
                });
                // (3)、当鼠标点击就把当前位置作为目标值;
                lis[i].addEventListener('click', function () {
                    current = this.offsetLeft;

                });
            }
        })
    </script>
</head>

<body>
    <!-- 
        案例分析:
           (1)、利用动画函数做动画效果;
           (2)、原先筋斗云的起始位置是0;
           (3)、鼠标经过某个li,把当前小li的`offsetLeft`位置作为目标值即可;
           (4)、鼠标离开某个小li,就把目标值设为0-->
    <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>

移动端网页特效

触屏事件

  • 移动端浏览器兼容性较好,我们不需要考虑以前js的兼容性问题,可以放心的使用原生js书写效果,但是移动端也有自己独特的地方,比如:触屏事件touch(也称触摸事件),AndroidIOS都有;
  • touch对象代表一个触摸点。触摸点可能是一根手指,也可能是一根触摸笔。触屏事件可响应用户手指(或触控笔)对屏幕或者触控板操作;
    -
<!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>移动端touch触摸事件</title>
    <style>
        div {
            width: 100px;
            height: 100px;
            background-color: pink;
        }
    </style>
</head>

<body>
    <div></div>

    <script>
        var div = document.querySelector('div');
        div.addEventListener('touchstart', function () {
            console.log('111');
        })
    </script>
</body>

</html>

触摸事件对象TouchEvent

  • touchstart touchmove touchend三个事件都会各自有事件对象;
  • TouchEvent是一类描述手指在触摸平面(触摸屏、触摸板等)的状态变化的事件。这类事件用于描述一个或多个触点,使开发者可以检测触点的移动,触点的增加和减少等等;
    在这里插入图片描述
  • e.targetTouches[0]就可以得到正在触摸dom元素的第一个手指的相关信息,比如:手指的坐标等等;

移动端拖动元素

  • touchstart touchmove touchend可以实现拖动元素;
  • 但是,拖动元素需要当前手指的坐标值,我们可以使用targetTouches[0]里面的pageXpageY
  • 移动端拖动的原理:手指移动中,计算出手指移动的距离。然后用盒子原来的位置 + 手指移动的距离;
  • 手指移动的距离:手指滑动中的位置 减去 手指刚开始触摸的位置;
  • 拖动元素三步曲:
    (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>移动端touch触摸事件</title>
    <style>
        div {
            width: 100px;
            height: 100px;
            background-color: pink;
            position: absolute;
            top: 0;
            left: 0;
        }
    </style>
</head>

<body>
    <div></div>

    <script>
        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>
案例:移动端轮播图
  • 案例分析:
    (1)、自动播放功能;
    (2)、开启定时器;
    (3)、移动端移动,可以使用translate移动;
    (4)、想要图片优雅的移动,添加过渡效果;
    (5)、自动播放功能–无缝滚动:注意:我们判断条件是要等到图片滚动完毕再去判断,就是过渡完成后判断;此时,需要添加检测过渡完成事件transitionend
    (6)、判断条件:如果索引号等于3,说明走到最后一张图片,此时索引号要复原为0;
    (7)、如果索引号小于0,说明是倒着走,索引号等于2;此时图片,去掉过渡效果,然后移动;
补充:classList属性:
  • 是HTML5新增的一个属性,返回元素的类名,但是ie10以上版本支持;
  • 该属性用于在元素中添加,移除及切换CSS类。有以下方法:
    (1)、添加类:element.classList.add('类名')
    (2)、移除类:element.classList.remove('类名')
    (3)、切换类(自动判断有没有某个类名,有就将其去掉,没有就将其加上):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>classList的使用</title>
    <style>
        .bg {
            background-color: black;
        }
    </style>
</head>

<body>
    <div class="one two"></div>
    <button>开关灯</button>

    <script>
        // classList返回元素的类名
        var div = document.querySelector('div');
        //console.log(div.classList);
        // 1、添加类名,是在后面追加类名,不会覆盖以前的类名;
        div.classList.add('three');//注意:three前面不需要加点
        //console.log(div.classList);
        
        // 2、删除类名
        div.classList.remove('three');
        //console.log(div.classList);

        // 3、切换类名(自动判断有没有某个类名,有就将其去掉,没有就将其加上)
        var btn = document.querySelector('button');
        btn.addEventListener('click', function () {
            document.body.classList.toggle('bg');
        })
    </script>
</body>

</html>
window.addEventListener('load', function () {
    // 1、获取元素
    var focus = document.querySelector('.focus');
    var ul = focus.children[0];
    // 获得focus的宽度
    var w = focus.offsetWidth;
    var ol = focus.children[1];
    // 2、利用定时器自动轮播图片
    var index = 0;
    var timer = setInterval(function () {
        index++;
        var translatex = -index * w;
        ul.style.transition = 'all .3s';
        ul.style.transform = 'translateX(' + translatex + 'px)';
    }, 2000)
    // 等着我们过渡完成之后,再去判断,监听过渡完成的事件 transitionend;
    // 判断条件:如果索引号等于3,说明走到最后一张图片,此时索引号要复原为0;
    ul.addEventListener('transitionend', function () {
        // 无缝滚动
        if (index >= 3) {
            index = 0;
            // 去掉过渡效果,这样让我们的ul快速的跳到目标位置;
            ul.style.transform = 'none';
            // 利用最新的索引号乘以宽度 去滚动图片
            var translatex = -index * w;
            ul.style.transform = 'translateX(' + translatex + 'px)';
        } else if (index < 0) {
            index = 2;
            ul.style.transition = 'none';
            var translatex = -index * w;
            ul.style.transform = 'translateX(' + translatex + 'px)';
        }

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

    //  4、手指滑动轮播图:本质就是ul跟随手指移动,简单说就是移动端拖动元素;
    // (1)、触摸元素 touchstart:获取手指初始坐标;
    var startX = 0;
    var moveX = 0;//后面我们会使用这个移动距离所以要定义一个全局变量;
    var flag = false;
    ul.addEventListener('touchstart', function (e) {
        startX = e.targetTouches[0].pageX;
        // 手指触摸的时候就停止定时器
        clearInterval(timer);
    });
    // (2)、移动手指 touchmove:计算手指的滑动距离,并且移动盒子;
    ul.addEventListener('touchmove', function (e) {
        // 计算移动距离
        moveX = e.targetTouches[0].pageX - startX;
        // 移动盒子:盒子原来的位置 + 手指移动的距离
        var translatex = -index * w + moveX;
        // 手指拖动的时候,不需要动画效果所以要取消过渡效果
        ul.style.transition = 'none';
        ul.style.transform = 'translateX(' + translatex + 'px)';
        flag = true;//如果用户手指移动过我们再去判断,否则不做判断效果;
        e.preventDefault();//阻止滚动屏幕的行为;
    });
    //离开手指touchend:根据移动距离去判断是回弹还是播放上一张下一张;
    ul.addEventListener('touchend', function (e) {
        //(1)、如果移动距离大于50像素,我们就播放上一张或者下一张;
        if (flag) {
            if (Math.abs(moveX) > 50) {  //Math.abs() 取绝对值
                // 如果是右滑就是,播放上一张 moveX 是正值
                if (moveX > 0) {
                    index--;
                } else {
                    // 如果是左滑就是,播放下一张 moveX 是负值
                    index++;
                }
                var translatex = -index * w;
                ul.style.transition = 'all .3s';
                ul.style.transform = 'translateX(' + translatex + 'px)';
            } else {
                // (2)、如果移动距离小于50像素,我们就回弹;
                var translatex = -index * w;
                ul.style.transition = 'all .1s';
                ul.style.transform = 'translateX(' + translatex + 'px)';
            }
        }
        // 手指离开重新开启定时器
        clearInterval(timer);
        timer = setInterval(function () {
            index++;
            var translatex = -index * w;
            ul.style.transition = 'all .3s';
            ul.style.transform = 'translateX(' + translatex + 'px)';
        }, 2000)
    })
})
案例:返回顶部
  • 当页面滚动到某个地方,就显示,否则隐藏;点击可以返回顶部;
  • 事件:scroll页面滚动事件;
  • 如果被卷去的头部(window.pageYOffset)大于某个数值;
  • 点击返回顶部,window.scroll(0,0)
window.addEventListener('load', function () {
    // 1、获取元素
    var focus = document.querySelector('.focus');
    var ul = focus.children[0];
    // 获得focus的宽度
    var w = focus.offsetWidth;
    var ol = focus.children[1];
    // 2、利用定时器自动轮播图片
    var index = 0;
    var timer = setInterval(function () {
        index++;
        var translatex = -index * w;
        ul.style.transition = 'all .3s';
        ul.style.transform = 'translateX(' + translatex + 'px)';
    }, 2000)
    // 等着我们过渡完成之后,再去判断,监听过渡完成的事件 transitionend;
    // 判断条件:如果索引号等于3,说明走到最后一张图片,此时索引号要复原为0;
    ul.addEventListener('transitionend', function () {
        // 无缝滚动
        if (index >= 3) {
            index = 0;
            // 去掉过渡效果,这样让我们的ul快速的跳到目标位置;
            ul.style.transform = 'none';
            // 利用最新的索引号乘以宽度 去滚动图片
            var translatex = -index * w;
            ul.style.transform = 'translateX(' + translatex + 'px)';
        } else if (index < 0) {
            index = 2;
            ul.style.transition = 'none';
            var translatex = -index * w;
            ul.style.transform = 'translateX(' + translatex + 'px)';
        }

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

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

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

click延时解决方案

  • 移动端click事件会有300ms的延时,原因是移动端屏幕双击会缩放(double tap to zoom)页面;
  • 解决方案:
    (1)、禁止缩放。浏览器禁用默认的双击缩放行为并且去掉300ms的点击延迟; <meta name="viewport" content="user-scalable=no">
    (2)、利用touch事件自己封装这个事件解决300ms延迟;(该函数缺点:一次只能给一个元素使用)
    原理就是:
    a、当我们手指触摸屏幕,记录当前触摸时间;
    b、当我们手指离开屏幕,用离开的时间减去触摸的时间;
    c、如果时间小于150ms,并且没有滑动过屏幕,那么我们就定义为点击;
    在这里插入图片描述
    (3)、使用插件。fastclick插件解决300ms延迟;
什么是插件?fastclickSwipersuperslide(https://www.superslide2.com)、iscroll(https://github.com/cubiq/iscroll)、移动端视频插件zy.media.js
  • 移动端要求的是快速开发,所以我们经常会借助于一些插件来帮我们完成操作,那么什么是插件呢 ?
  • JS插件就是JS文件。它遵循一定规范编写,方便程序展示效果,拥有特定功能且方便调用。如轮播图和瀑布流插件;
  • 特点:它一般是为了解决某个问题而专门存在,其功能单一,并且比较小。我们以前写的animate.js也算是一个最简单的插件;
  • fastclick插件的使用:
<!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>fastclick插件使用</title>
    <style>
        div {
            width: 50px;
            height: 50px;
            background-color: aqua;
        }
    </style>
    <script src="fastclick.js"></script>
</head>

<body>
    <div></div>

    <script>
        // 从官网github复制而来
        if ('addEventListener' in document) {
            document.addEventListener('DOMContentLoaded', function () {
                FastClick.attach(document.body);
            }, false);
        }
    </script>
</body>

</html>
  • 移动端视频插件zy.media.js;(https://github.com/ireaderlab/zyMedia)
  • H5给我们提供了video标签,但是浏览器的支持情况不同。不同的视频格式文件,我们可以通过source解决;但是,外观样式、还有暂停、播放、全屏等功能我们只能自己写代码解决。这个时候我们可以使用插件方式来制作。
移动端常用开发框架
  • 框架:顾名思义就是一套架构,它会基于自身的特点向用户提供一套较为完整的解决方案。框架的控制权在框架本身,使用者要按照框架所规定的某种规范进行开发。
  • 插件一般是为了解决某个问题而专门存在,其功能单一,并且比较小;
  • 前端常用的框架有:Bootstrap Vue Angular React等。既能开发PC端,也能开发移动端;
  • 前端常用的移动端插件有:swiper iscroll superslide
  • 框架:大而全,一整套解决方案;小而专一,某个功能的解决方案;

本地存储

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

window.sessionStorage

  • 生命周期为关闭浏览器窗口;
  • 在同一个窗口(页面)下数据可以共享;
  • 以键值对的形式存储使用;
  • 存储数据: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>本地存储之sessionStorage</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');
        set.addEventListener('click', function () {
            // 当我们点击了之后,就可以把表单里面的值存储起来
            var val = ipt.value;
            sessionStorage.setItem('uname', val);
        })
        get.addEventListener('click', function () {
            // 当我们点击了之后,就可以把表单里面存储的值获取过来
            console.log(sessionStorage.getItem('uname'));
        })
    </script>
</body>

</html>

window.localStorage

  • 生命周期永久生效,除非手动删除,否则关闭页面也会存在;
  • 可以多窗口(页面)共享(同一浏览器可以共享);
  • 以键值对的形式存储使用;
  • 存储数据: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>记住用户名案例</title>
</head>

<body>
    <!--
         如果勾选记住用户名,下次用户打开浏览器,就在文本框里面自动显示上次登录的用户名;
         案例分析:
           1、把数据存起来,用到本地存储;
           2、关闭页面,也可以显示用户名,所以用到localSorage;
           3、打开页面,先判断是否有这个用户名,如果有,就在表单里面显示用户名,并且勾选复选框;
           4、当复选框发生改变的时候 change 事件;
           5、如果勾选就存储,否则就移除;
        -->
    <input type="text" id="username"> <input type="checkbox" name="" id="remember"> 记住用户名

    <script>
        var username = document.querySelector('#username');
        var remember = document.querySelector('#remember');
        if (localStorage.getItem('username')) {
            username.value = localStorage.getItem('username');
            remember.checked = 'true';
        }
        remember.addEventListener('change', function () {
            if (this.checked) {
                localStorage.setItem('username', username.value);
            } else {
                localStorage.removeItem('username');
            }
        })
    </script>
</body>

</html>
  • 存储时转化成字符串,获取时转化成对象;
    在这里插入图片描述
    在这里插入图片描述
 <script>
        var todolist = [{
            title: '我今天吃八个馒头',
            done: false
        }, {
            title: '我今天学习jq',
            done: false
        }, ];
        // localStorage.setItem("todo", todolist);
        // 1. 本地存储里面只能存储字符串的数据格式 把我们的数组对象转换为字符串格式 JSON.stringify()
        localStorage.setItem("todo", JSON.stringify(todolist));
        var data = localStorage.getItem("todo");
        // console.log(typeof data);
        // console.log(data[0].title);
        // 2. 获取本地存储的数据 我们需要把里面的字符串数据转换为 对象格式 JSON.parse()
        data = JSON.parse(data);
        console.log(data);
        console.log(data[0].title);
    </script>
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值