二:第二阶段 JS(4)

一: BOM浏览器对象模型

1.1: 目标

能够说出什么是BOM

能够知道浏览器的顶级对象window

能够写出页面加载事件以及注意事项

能够写出两种定时器函数并说出区别

能够说出JS执行机制

能够使用location 对象完成页面之间的跳转

能够知晓navigator对象涉及的属性

能够使用history提供的方法实现页面刷新

1.2: 目录

BOM概述

window对象的常见事件

定时器

JS执行机制

location对象

navigator对象

history对象

1.3:BOM概述

1:什么是BOM?

比如:一个页面如果后退或者前进一个页面,怎么刷新一个页面,浏览器页面的窗口发生变化,或者浏览器页面的滚动条滚动。需要用到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 是浏览器厂商在各自浏览器上定义的,兼容性较差

2;BOM的构成

BOM 比DOM 更大,它包含DOM。

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

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

    2.它是一个全局对象。定义在全局作用域中的变量、函数都会变成window对象的属性和方法。在调用的时候可以省略window,前面学习的对话框都属于window 对象方法,如alert()、prompt()等。

注意:window下的一个特殊属性window.name

二:第二阶段 JS(4)_第1张图片



 

    
    
    
    Document

 

    

 

1.4: window对象的常见事件

1: 窗口加载事件

window.onload = function(){ }

或者

window.addEventListener("load",function( ){ } );

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

注意:

1. 有了window.onload 就可以把JS 代码写到页面元素的上方,因为onload 是等页面内容全部加载完毕,再去执行处理函数。

2. window.onload 传统注册事件方式只能写一次,如果有多个,会以最后一个window.onload 为准。

3. 如果使用addEventListener则没有限制

document.addEventListener('DOMContentLoaded',function(){})

DOMContentLoaded事件触发时,仅当DOM加载完成,不包括样式表,图片,flash等等。Ie9以上才支持。

如果页面的图片很多的话, 从用户访问到onload触发可能需要较长的时间,交互效果就不能实现,必然影响用户的体验,此时用DOMContentLoaded 事件比较合适。





    
    
    Document



    
    


2: 调整窗口大小事件

window.onresize = function(){ }

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

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

注意:

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

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





    
    
    
    Document
    



    
    

1.5: 定时器

1: 两种定时器

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

  • setTimeout()
  • setInterval()

2: setTimeout() 定时器

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

       延时时间是延迟多少毫秒后就开始执行调用函数。

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

注意:

1. window 可以省略。

2. 这个调用函数可以直接写函数,或者写函数名或者采取字符串‘函数名()'三种形式。第三种       不推荐

3. 延迟的毫秒数省略默认是0,如果写,必须是毫秒。

4. 因为定时器可能有很多,所以我们经常给定时器赋值一个标识符。





    
    
    Document



    


2:setTimeout() 定时器

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

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

案例分析:

①核心思路:5秒之后,就把这个广告隐藏起来

②用定时器setTimeout





    
    
    Document



    
    


3: 停止setTimeout() 定时器

window.clearTimeout(timeoutID)

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

注意

1. window 可以省略。

2. 里面的参数就是定时器的标识符





    
    
    Document



    
    


4:  setInterval() 定时器

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

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

注意:

1. window 可以省略。

2. 这个调用函数可以直接写函数,或者写函数名或者采取字符串'函数名()' 三种形式。

3. 间隔的毫秒数省略默认是0,如果写,必须是毫秒,表示每隔多少毫秒就自动调用这个函数。

4.因为定时器可能有很多,所以我们经常给定时器赋值一个标识符。

5. 第一次执行也是间隔毫秒数之后执行,之后每隔毫秒数就执行一次。





    
    
    Document



    


5: 案例-倒计时

二:第二阶段 JS(4)_第2张图片

案例分析

①这个倒计时是不断变化的,因此需要定时器来自动变化(setInterval)

②三个黑色盒子里面分别存放时分秒

③三个黑色盒子利用innerHTML 放入计算的小时分钟秒数

④第一次执行也是间隔毫秒数,因此刚刷新页面会有空白

⑤最好采取封装函数的方式,这样可以先调用一次这个函数,防止刚开始刷新页面有空白问题





    
    
    
    Document
    



    
1 2 3

6:  停止setInterval()定时器

window.clearInterval(intervalID);

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

注意:

1. window 可以省略。

2. 里面的参数就是定时器的标识符。





    
    
    Document



    
    

    


7:发送短信

点击按钮后,该按钮60秒之内不能再次点击,防止重复发送短信

案例分析:

①按钮点击之后,会禁用disabled为true

②同时按钮里面的内容会变化,注意button 里面的内容通过innerHTML修改

③里面秒数是有变化的,因此需要用到定时器

④定义一个变量,在定时器里面,不断递减

⑤如果变量为0 说明到了时间,我们需要停止定时器,并且复原按钮初始状态。





    
    
    Document



  手机号:   

    


8: this

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

现阶段,我们先了解一下几个this指向

1. 全局作用域或者普通函数中this指向全局对象window(注意定时器里面的this指向window)

2.方法调用中谁调用this指向谁

3.构造函数中this指向构造函数的实例





    
    
    Document



    

    


1.6: JS执行机制

1: JS是单线程

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

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

1.1: 一个问题

以下代码执行的结果是什么?

console.log(1);

setTimeout(function () {

console.log(3);

}, 1000);

console.log(2);

2: 同步和异步

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

同步:

前一个任务结束后再执行后一个任务,程序的执行顺序与任务的排列顺序是一致的、同步的。比如做饭的同步做法:我们要烧水煮饭,等水开了(10分钟之后),再去切菜,炒菜。

异步:

你在做一件事情时,因为这件事情会花费很长时间,在做这件事的同时,你还可以去处理其他事情。比如做饭的异步做法,我们在烧水的同时,利用这10分钟,去切菜,炒菜。

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

2.1:一个问题

那么以下代码执行的结果又是什么?

console.log(1);

setTimeout(function () {

   console.log(3);

   }, 0);

  console.log(2);

3: 异步和同步

同步任务

同步任务都在主线程上执行,形成一个执行栈。

异步任务

JS 的异步是通过回调函数实现的。

一般而言,异步任务有以下三种类型:

1、普通事件,如click、resize 等

2、资源加载,如load、error 等

3、定时器,包括setInterval、setTimeout 等

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

二:第二阶段 JS(4)_第3张图片

4: JS执行机制

1. 先执行执行栈中的同步任务

2. 异步任务(回调函数)放入任务队列中。

3. 一旦执行栈中的所有同步任务执行完毕,系统就会按次序读取任务队列中的异步任务,于是被读取的异步任务结束等待状态,进入执行栈,开始执行。

二:第二阶段 JS(4)_第4张图片

console.log(1);

document.onclick = function() {

    console.log('click');}
   
}

console.log(2);

setTimeout(function() {

    console.log(3)

  },3000}

分析上面代码流程:(遇到多个异步任务是怎么进行处理的)

 1: 先把所有的任务分成同步任务和异步任务;

 2:所有的同步任务放在主线程执行栈里面;而异步任务有好几个,都要放在异步任务队列里      吗?或者说主线程执行线,和异步现场执行线谁放在前面,谁放在后面? 这个时候就用异.  步进程来处理;

 处理过程:

3:先执行同步任务 console.log(1), 就先把1打印出来;

4: 再执行第二句话 document.onclick ,并且有一个异步回调函数fn,属于异步任务;这个时候        会把这个提交给异步进程来处理,它来决定你这个要不要写入异步任务队列里,所以这个      需要等待用户是否点击了。用户点击了才会把这个回调任务写入异步任务队列里,不点击      就不写入异步队列中。假如现在不点击,不写入异步队列里,这个时候就略过。

5: 再执行console.log (2) ,打印出来即可;

6: 看 setTimeout(fn,3000); 这个也是异步任务,也会提交给异步进程处理,异步进程也是等.        着3秒到了才能把这个异步任务写入异步队列中去。这个时候3秒到了,并写入了异步队.        列中console.log(3),但现在不会里面执行异步队列的任务,先把主线程的执行完成才执.        行异步队列中的;

7: 往下看是否还有代码是主线程处理的,没有就表示同步任务执行完成。再回到异步的任. 务.    队列中看是否有 异步任务,有定时器的console.log(3), 就会把这个拿到执行栈中执行,并打印3。异步队列中的任务执行完成后,队列里就没有任务了。 

二:第二阶段 JS(4)_第5张图片

8: 回到document.onclick = fn;如果点击了,就提交给异步进程处理,就把这个回调函数fn写.       入异步任务队列中console.log('click'); 这个时候任务队列里又有任务了。

9:虽然同步任务已经结束了,但还是会看到任务队列里 有没有新的异步任务,现在是又有新       的异步任务进来,并且把这个任务拿到执行栈中执行,并打印出来click;

二:第二阶段 JS(4)_第6张图片

10:  如果又点击了,就会执行8,9过程。

11: 上面过程是3秒之后点击的,所以先输出的是3 ,再输出的是click

12: 如果是在3秒钟之前点击的鼠标,那就是先输出click,再输出3;

注意: 就算同步任务完成后,还是会返回看任务队列里还有没有异步任务,有就执行;这个就是事件循环 event loop.

按照下图小总结:

在JS中会把任务分成同步任务和异步任务,把同步任务放入执行栈中,异步任务是提交给异步进程来处理,等着异步事件触发,才把回调函数异步任务放入任务队列里。当同步·任务全部执行完毕后,才到任务队列里看有没有异步任务,如果有就取出来到执行栈中去执行,然后再回头看是否有异步任务,如果有再取出来到执行栈中执行,这种循环的过程叫事件循环。二:第二阶段 JS(4)_第7张图片

1.7: location对象

1: 什么是location对象?

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

2: URL

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

URL 的一般语法格式为:

protocol://host[:port]/path/[?query]#fragment

http://www.itcast.cn/index.html?name=andy&age=18#link

二:第二阶段 JS(4)_第8张图片

3: location对象的属性

二:第二阶段 JS(4)_第9张图片

重点记住:href 和search

4: 案例-5秒钟之后自动跳转页面

案例分析

①利用定时器做倒计时效果

②时间到了,就跳转页面。使用location.href





    
    
    Document



    
    

5: 案例-获取URL 参数数据

案例分析:

①第一个登录页面,里面有提交表单,action 提交到index.html页面

②第二个页面,可以使用第一个页面的参数,这样实现了一个数据不同页面之间的传递效果

③第二个页面之所以可以使用第一个页面的数据,是利用了URL 里面的location.search参数

④在第二个页面中,需要把这个参数提取。

⑤第一步去掉? 利用substr

⑥第二步利用=号分割键和值split(‘=‘)

⑦第一个数组就是键 第二个数组就是值

表单要有表单域才能提交有效;





    
    
    Document



    

6: location对象的方法

二:第二阶段 JS(4)_第10张图片





    
    
    Document



    

    


 1.9: navigator对象

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

下面前端代码可以判断用户那个终端打开页面,实现跳转:

if((navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|

  Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS

|Symbian|Windows Phone)/i))){

    window.location.href = "";     //手机

} else {

   window.location.href = "";  //电脑

}

1.10: history对象

window 对象给我们提供了一个history 对象,与浏览器历史记录进行交互。该对象包含用户(在浏览器窗口中)访问过的URL。二:第二阶段 JS(4)_第11张图片

history 对象一般在实际开发中比较少用,但是会在一些OA 办公系统中见到。 二:第二阶段 JS(4)_第12张图片

index.html





    
    
    Document



    点击我去往列表页
    

    


list.html





    
    
    Document



    点击我去往首页
    

    


二: PC端网页特效(网页交互) 

2.1:目标

  • 能够说出常见offset系列属性的作用
  • 能够说出常见client系列属性的作用
  • 能够说出常见scroll系列属性的作用
  • 能够封装简单动画函数
  • 能够写出网页轮播图案例

2.2: 目录

  • 元素偏移量offset系列
  • 元素可视区client系列
  • 元素滚动scroll系列
  • 动画函数封装
  • 常见网页特效案例

2.3:元素偏移量offset系列

1: offset概述

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

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

offset 系列常用属性:

二:第二阶段 JS(4)_第13张图片





    
    
    Document

    



    

2: offset与style的区别

offset

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

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

offsetWidth 包含padding+border+width

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

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

style

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

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

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

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

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





    
    
    Document
    



    

3: offset概述

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

  • 获得元素距离带有定位父元素的位置
  • 获得元素自身的大小(宽度高度)

二:第二阶段 JS(4)_第14张图片

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

案例分析:

①我们在盒子内点击,想要得到鼠标距离盒子左右的距离。

②首先得到鼠标在页面中的坐标(e.pageX, e.pageY)

③其次得到盒子在页面中的距离( box.offsetLeft, box.offsetTop)

④用鼠标距离页面的坐标减去盒子在页面中的距离,得到鼠标在盒子内的坐标

⑤如果想要移动一下鼠标,就要获取最新的坐标,使用鼠标移动事件mousemove





    
    
    Document
    



    

5: 案例-模态框拖拽

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

1.点击弹出层,会弹出模态框,并且显示灰色半透明的遮挡层。

2.点击关闭按钮,可以关闭模态框,并且同时关闭灰色半透明遮挡层。

3.鼠标放到模态框最上面一行,可以按住鼠标拖拽模态框在页面中移动。

4.鼠标松开,可以停止拖动模态框移动。

案例分析:

①点击弹出层,模态框和遮挡层就会显示出来display:block;

②点击关闭按钮,模态框和遮挡层就会隐藏起来display:none;

③在页面中拖拽的原理:鼠标按下并且移动,之后松开鼠标

④触发事件是鼠标按下mousedown,鼠标移动mousemove鼠标松开mouseup

⑤拖拽过程:鼠标移动过程中,获得最新的值赋值给模态框的left和top值,这样模态框可以跟着鼠标走了

⑥鼠标按下触发的事件源是最上面一行,就是id为title

⑦鼠标的坐标减去鼠标在盒子内的坐标,才是模态框真正的位置。

⑧鼠标按下,我们要得到鼠标在盒子的坐标。

⑨鼠标移动,就让模态框的坐标设置为:鼠标坐标减去盒子坐标即可,注意移动事件写到按下事件里面。

⑩鼠标松开,就停止拖拽,就是可以让鼠标移动事件解除





    
    
    Document
    



    

    

    
    

    


6:案例:仿京东放大镜

6.1: 案例分析

①整个案例可以分为三个功能模块

②鼠标经过小图片盒子,黄色的遮挡层和大图片盒子显示,离开隐藏2个盒子功能

③黄色的遮挡层跟随鼠标功能。

④移动黄色遮挡层,大图片跟随移动功能。

6.2.1: 功能一分析

①鼠标经过小图片盒子,黄色的遮挡层和大图片盒子显示,离开隐藏2个盒子功能

②就是显示与隐藏

6.2.2: 功能二分析

①黄色的遮挡层跟随鼠标功能。

②把鼠标坐标给遮挡层不合适。因为遮挡层坐标以父盒子为准。

③首先是获得鼠标在盒子的坐标。④之后把数值给遮挡层做为left 和top值。

⑤此时用到鼠标移动事件,但是还是在小图片盒子内移动。

⑥发现,遮挡层位置不对,需要再减去盒子自身高度和宽度的一半。

⑦遮挡层不能超出小图片盒子范围。

⑧如果小于零,就把坐标设置为0

⑨如果大于遮挡层最大的移动距离,就把坐标设置为最大的移动距离⑩遮挡层的最大移动距离:小图片盒子宽度减去遮挡层盒子宽度

6.2.3: 功能三分析

①移动黄色遮挡层,大图片跟随移动功能。

二:第二阶段 JS(4)_第15张图片

②求大图片的移动距离公式

二:第二阶段 JS(4)_第16张图片





    
    手机详情页!
    
    
    
    
    
    
    
    
    
    
    
    



    
    
    
    
    
    
    
    
    

    
    
Apple iPhone 6s(A1700)64G玫瑰金色 移动通信电信4G手机
推荐选择下方[移动优惠购],手机套餐齐搞定,不用换号,每月还有花费返
价格
¥5299.00 降价通知
累计评价612188
促销
加购价 满999.00另加20.00元,或满1999.00另加30.00元,或满2999.00另加40.00元,即可在购物车换 购热销商品 详情 》
支持
以旧换新,闲置手机回收 4G套餐超值抢 礼品购
选择颜色
玫瑰金 金色 白色 土豪色
选择版本
公开版 移动4G
购买方式
官方标配 移动优惠购 电信优惠购
  • 相关分类
  • 推荐品牌
  • 商品介绍
  • 规格与包装
  • 售后保障
  • 商品评价(50000)
  • 手机社区
  • 分辨率:1920*1080(FHD)
  • 后置摄像头:1200万像素
  • 前置摄像头:500万像素
  • 核 数:其他
  • 频 率:以官网信息为准
  • 品牌: Apple ♥关注
  • 商品名称:APPLEiPhone 6s Plus
  • 商品编号:1861098
  • 商品毛重:0.51kg
  • 商品产地:中国大陆
  • 热点:指纹识别,Apple Pay,金属机身,拍照神器
  • 系统:苹果(IOS)
  • 像素:1000-1600万
  • 机身内存:64GB

查看更多参数

detail.css

/*详情页的样式文件*/

.de_container {
    margin-top: 20px;
}

.crumb_wrap {
    height: 25px;
}

.crumb_wrap a {
    margin-right: 10px;
}

.preview_wrap {
    width: 400px;
    height: 590px;
}

.preview_img {
    position: relative;
    height: 398px;
    border: 1px solid #ccc;
}

/*  js课程+ */
.mask {
    display: none;
    position: absolute;
    top: 0;
    left: 0;
    width: 300px;
    height: 300px;
    background: #FEDE4F;
    opacity: .5;
    border: 1px solid #ccc;
    cursor: move;
}

/*  js课程+ */
.big {
    display: none;
    position: absolute;
    left: 410px;
    top: 0;
    width: 500px;
    height: 500px;
    background-color: pink;
    z-index: 999;
    border: 1px solid #ccc;
    overflow: hidden;
}

/* 赋值left top一定要定位postion才能移动 */
.big img {
    position: absolute;
    top: 0;
    left: 0;
}

.preview_list {
    position: relative;
    height: 60px;
    margin-top: 60px;
}

.list_item {
    width: 320px;
    height: 60px;
    margin: 0 auto;
}

.list_item li {
    float: left;
    width: 56px;
    height: 56px;
    border: 2px solid transparent;
    margin: 0 2px;
}

.list_item li.current {
    border-color: #c81623;
}

.arrow_prev,
.arrow_next {
    position: absolute;
    top: 15px;
    width: 22px;
    height: 32px;
    background-color: purple;
}

.arrow_prev {
    left: 0;
    background: url(../img/arrow-prev.png) no-repeat;
}

.arrow_next {
    right: 0;
    background: url(../img/arrow-next.png) no-repeat;
}

.itemInfo_wrap {
    width: 718px;
}

.sku_name {
    height: 30px;
    font-size: 16px;
    font-weight: 700;
}

.news {
    height: 32px;
    color: #e12228;
}

.summary dl {
    overflow: hidden;
}

.summary dt,
.summary dd {
    float: left;
}

.summary dt {
    width: 60px;
    padding-left: 10px;
    line-height: 36px;
}

.summary_price,
.summary_promotion {
    position: relative;
    padding: 10px 0;
    background-color: #fee9eb;
}

.price {
    font-size: 24px;
    color: #e12228;
}

.summary_price a {
    color: #c81623;
}

.remark {
    position: absolute;
    right: 10px;
    top: 20px;
}

.summary_promotion {
    padding-top: 0;
}

.summary_promotion dd {
    width: 450px;
    line-height: 36px;
}

.summary_promotion em {
    display: inline-block;
    width: 40px;
    height: 22px;
    background-color: #c81623;
    text-align: center;
    line-height: 22px;
    color: #fff;
}

.summary_support dd {
    line-height: 36px;
}

.choose_color a {
    display: inline-block;
    width: 80px;
    height: 41px;
    background-color: #f7f7f7;
    border: 1px solid #ededed;
    text-align: center;
    line-height: 41px;
}

.summary a.current {
    border-color: #c81623;
}

.choose_version {
    margin: 10px 0;
}

.choose_version a,
.choose_type a {
    display: inline-block;
    height: 32px;
    padding: 0 12px;
    background-color: #f7f7f7;
    border: 1px solid #ededed;
    text-align: center;
    line-height: 32px;
}

.choose_btns {
    margin-top: 20px;
}

.choose_amount {
    position: relative;
    float: left;
    width: 50px;
    height: 46px;
    background-color: pink;
}

.choose_amount input {
    width: 33px;
    height: 44px;
    border: 1px solid #ccc;
    text-align: center;
}

.add,
.reduce {
    position: absolute;
    right: 0;
    width: 15px;
    height: 22px;
    border: 1px solid #ccc;
    background-color: #f1f1f1;
    text-align: center;
    line-height: 22px;
}

.add {
    top: 0;
}

.reduce {
    bottom: 0;
    /*禁止鼠标样式*/
    cursor: not-allowed;
    /* pointer  小手  move  移动  */
}

.addcar {
    float: left;
    width: 142px;
    height: 46px;
    background-color: #c81623;
    text-align: center;
    line-height: 46px;
    font-size: 18px;
    color: #fff;
    margin-left: 10px;
    font-weight: 700;
}

.product_detail {
    margin-bottom: 50px;
}

.aside {
    width: 208px;
    border: 1px solid #ccc;
}

.tab_list {
    overflow: hidden;
    height: 34px;
}


/*把背景颜色 底边框都给 li*/

.tab_list li {
    float: left;
    background-color: #f1f1f1;
    border-bottom: 1px solid #ccc;
    height: 33px;
    text-align: center;
    line-height: 33px;
}


/*鼠标单击 li 变化样式   背景变白色 去掉下边框 文字变颜色*/

.tab_list .current {
    background-color: #fff;
    border-bottom: 0;
    color: red;
}

.first_tab {
    width: 104px;
}

.second_tab {
    width: 103px;
    border-left: 1px solid #ccc;
}

.tab_con {
    padding: 0 10px;
}

.tab_con li {
    border-bottom: 1px solid #ccc;
}

.tab_con li h5 {
    /*超出的文字省略号显示*/
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    font-weight: 400;
}

.aside_price {
    font-weight: 700;
    margin: 10px 0;
}

.as_addcar {
    display: block;
    width: 88px;
    height: 26px;
    border: 1px solid #ccc;
    background-color: #f7f7f7;
    margin: 10px auto;
    text-align: center;
    line-height: 26px;
}

.detail {
    width: 978px;
}

.detail_tab_list {
    height: 39px;
    border: 1px solid #ccc;
    background-color: #f1f1f1;
}

.detail_tab_list li {
    float: left;
    height: 39px;
    line-height: 39px;
    padding: 0 20px;
    text-align: center;
    cursor: pointer;
}

.detail_tab_list .current {
    background-color: #c81623;
    color: #fff;
}

.item_info {
    padding: 20px 0 0 20px;
}

.item_info li {
    line-height: 22px;
}

.more {
    float: right;
    font-weight: 700;
    font-family: 'icomoon';
}

detail.js

// 页面加载是从上往下:css---> html ----> js
// 先页面加载完后,才执行js
document.addEventListener('load', function () {
    var preview_img = document.querySelector('.preview_img');
    var mask = document.querySelector('.mask');
    //右边的图
    var big = document.querySelector('.big');

    //1:当鼠标经过的时候  preview_img 就显示和隐藏 mask 遮挡层 和 big 大盒子
    preview_img.addEventListener('mouseover', function () {
        mask.style.display = 'block';
        big.style.display = 'block';
    })

    preview_img.addEventListener('mouseout', function () {
        mask.style.display = 'none';
        big.style.display = 'none';
    })

    //2:鼠标移动的时候,让黄色的盒子跟着鼠标来走
    //注意:得到盒子与页面的距离时要注意:这个盒子是否有父盒子,并且父盒子有定位的话,那么offsetleft算出来的就是盒子与父盒子之间的距离
    preview_img.addEventListener('mousemove', function (e) {
        //2.1:先计算出鼠标在盒子内的坐标
        var x = e.pageX - this.offsetWidth;
        var y = e.pagey - this.offsetHight;

        //2.把鼠标的坐标赋值给阴影盒子mask
        // mask.style.left = x + 'px';
        // mask.style.top = y + 'px';

        //2.3:因为鼠标要在mask中央,所以盒子是300,鼠标要在一半 150 才到正中央;
        var maskX = x - (mask, offsetWidth / 2);
        var maskY = y - (mask.offsetHight / 2);
        // //把鼠标的坐标赋值给阴影盒子mask
        // mask.style.left = maskX + 'px';
        // mask.style.top = maskY + 'px';


        //3: mask盒子只能在 preview_img内移动,不能超出这个盒子
        //3.1: mask以preview_img的左右边框位准;如果x 坐标小于0 就让他停在0 的位置

        //遮挡层的最大移动距离:正方形的宽高是一样的
        maskMax = preview_img.offsetWidth - mask.offsetWidth;
        //左
        if (maskX <= 0) {
            maskX = 0;

            //右:preview盒子-mask盒子= mask移动的最大值
        } else if (maskX >= maskMax) {
            maskX = maskMax;
        }

        //上
        if (maskY <= 0) {
            maskY = 0;
        } else if (maskY >= maskMax) {
            maskY = maskMax;
        }


        //2:把鼠标的坐标赋值给阴影盒子mask
        mask.style.left = maskX + 'px';
        mask.style.top = maskY + 'px';

        //3: 大图片的移动距离 = 遮挡层移动距离 * 大图片最大移动距离 / 遮挡层的最大移动距离
        //大图
        var bigImg = document.querySelector('.bigImg');
        //大图移动的最大距离(大盒子和大图之间最大的距离) = 大图到页面的距离 - 大盒子到页面的距离
        var bigMax = bigImg.offsetWidth = big.offsetWidth;
        //大图片的移动距离 x Y;
        
        var bigX = maskX * bigMax / maskMax;
        var bigX = maskY * bigMax / maskMax;

        //赋值left top一定要定位postion才能移动
        // 因为小图片移动和大图片移动相反,所以用-
        bigImg.style.left = -bigX + 'px';
        bigImg.style.top = -bigY + 'px';





    })
})

2.4: 元素可视区 client系列

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

二:第二阶段 JS(4)_第17张图片

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

二:第二阶段 JS(4)_第18张图片





    
    
    Document
    



    

2: 案例-淘宝flexible.js 源码分析 

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

主要作用:创建一个独立的作用域。避免了命名冲突问题





    
    
    Document



    


 3: js分析

(function flexible(window, document) {
    // 获取的html 的根元素
    var docEl = document.documentElement
    // dpr 物理像素比(看当前的浏览器是否能拿到物理像素比,如果能拿到就显示当前的,不能拿到就显示1 )
    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() {
        // 把屏幕分成10等份
        var rem = docEl.clientWidth / 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))

2.5: 元素滚动scroll系列

1:元素scroll 系列属性

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

二:第二阶段 JS(4)_第19张图片

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

 二:第二阶段 JS(4)_第20张图片

2: 页面被卷去的头部

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





    
    
    Document
    



    
我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容

3:案例-仿淘宝固定侧边栏

需求:

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

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

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

案例分析:

①需要用到页面滚动事件scroll因为是页面滚动,所以事件源是document

②滚动到某个位置,就是判断页面被卷去的上部值。

③页面被卷去的头部:可以通过window.pageYOffset获得如果是被卷去的左侧window.pageXOffset

④注意,元素被卷去的头部是element.scrollTop ,如果是页面被卷去的头部则是window.pageYOffset

⑤其实这个值可以通过盒子的offsetTop可以得到,如果大于等于这个值,就可以让盒子固定定位了



 

    
    
    
    Document
    

 

    
返回顶部
头部区域
主体部分

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

  5: 三大系列总结

二:第二阶段 JS(4)_第21张图片二:第二阶段 JS(4)_第22张图片他们主要用法:

1.offset系列经常用于获得元素位置  offsetLeftof fsetTop

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

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

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

6: mouseenter 和mouseover的区别

mouseenter 鼠标事件

当鼠标移动到元素上时就会触发mouseenter 事件类似mouseover,它们两者之间的差别是mouseover 鼠标经过自身盒子会触发,经过子盒子还会触发。mouseenter 只会经过自身盒子触发

之所以这样,就是因为mouseenter不会冒泡

跟mouseenter搭配鼠标离开mouseleave 同样不会冒泡



 

    
    
    
    Document
    

 

    

2.6: 动画函数封装

1:  动画实现原理

核心注意:要移动位置必须使用定位才能移动;动画里面必须添加定位

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

实现步骤:

1. 获得盒子当前位置

2. 让盒子在当前位置加上1个移动距离

3. 利用定时器不断重复这个操作

4. 加一个结束定时器的条件

5.注意此元素需要添加定位,才能使用element.style.left





    
    
    Document
    



    

2: 动画片函数简单封装

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





    
    
    Document
    



    
夏雨荷

3-动画函数简单封装优化(动画函数给不同元素记录不同定时器)

原因:

1:之前用var来声明,每次调用都开辟一次新的空间,会照成浪费。

2:并且名字都一样,会出现奇异;所以用对象属性的办法来处理优化,不需要每次都用var声明,并且属性每个调用者自己的函数来。

3:有按钮的情况下,一旦按钮越来越快,那么动画也会跑的越来越快,解决办法就是只让一个定时器执行,就行了。那么就删除其他的定时器。



 

    
    
    
    Document
    

 

    
    
夏雨荷

4:  缓动效果原理

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

思路:

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

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

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

4.注意步长值需要取整

二:第二阶段 JS(4)_第23张图片

5: 动画函数多个目标值之间移动

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

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

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

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





    
    
    
    Document
    



    
    

    
    夏雨荷
    


6: 缓动动画添加回调函数

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





    
    
    
    Document
    



    
    

    
    夏雨荷
    


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

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

1.单独新建一个JS文件。

function animate(obj, target, callback) {
    //先清除一切的定时器,只保留当前的一个定时器执行
    clearInterval(obj.timer);
    obj.timer = setInterval(function () {

        //因为每次移动的步长都是变化的,所以步长值写在定时器里面
        //步长公式:(目标值 - 现在的位置) / 10
        //当盒子在左边,往右走是得到小数的步数,那么就向上去值,小数改为整数
        var step = Math.ceil(target - obj.offsetLeft) / 10;

        //当盒子走到左边时,要回退,如果有小时,就要往小值取;与上面step整合
        var step = (target - obj.offsetLeft) / 10;
        //三目运算符:如果是step是正的,则值往大的取;否则往小的取
        step = step > 0 ? Math.ceil(step) : Math.floor(step);

        if (obj.offsetLeft == target) {
            //停止动画 本质是停止定时器
            clearInterval(obj.timer);
            //回调:如果有回调,则执行回调函数;执行背景颜色是粉色
            if (callback) {
                callback();
            }
        }
        //把每次加1 这个步长值改为一个 慢慢变小的值 步长公式:(目标值 - 现在的位置) / 10
        obj.style.left = obj.offsetLeft + step + 'px';
    }, 15);

}

2.HTML文件引入JS文件。





    
    
    Document
    
    
    



    
问题反馈

2.7: 常见网页特效案例

1:网页轮播图 结合之前的品优购 直接添加js

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

1.1: 功能需求

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

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

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

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

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

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

1.2: 案例分析

二:第二阶段 JS(4)_第24张图片

二:第二阶段 JS(4)_第25张图片

二:第二阶段 JS(4)_第26张图片

二:第二阶段 JS(4)_第27张图片

二:第二阶段 JS(4)_第28张图片

二:第二阶段 JS(4)_第29张图片

 二:第二阶段 JS(4)_第30张图片

二:第二阶段 JS(4)_第31张图片

代码只显示js部分:完整版可以看百度网盘,或者视频提供的资料

window.addEventListener('load', function() {
    // 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++) {
        // 创建一个小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 的移动距离 小圆圈的索引号 乘以 图片的宽度 注意是负值
            // 当我们点击了某个小li 就拿到当前小li 的索引号
            var index = this.getAttribute('index');
            // 当我们点击了某个小li 就要把这个li 的索引号给 num  
            num = index;
            // 当我们点击了某个小li 就要把这个li 的索引号给 circle  
            circle = index;
            // num = circle = index;
            console.log(focusWidth);
            console.log(index);

            animate(ul, -index * focusWidth);
        })
    }
    // 把ol里面的第一个小li设置类名为 current
    ol.children[0].className = 'current';
    // 6. 克隆第一张图片(li)放到ul 最后面
    var first = ul.children[0].cloneNode(true);
    ul.appendChild(first);
    // 7. 点击右侧按钮, 图片滚动一张
    var num = 0;
    // circle 控制小圆圈的播放
    var circle = 0;
    // flag 节流阀
    var flag = true;
    arrow_r.addEventListener('click', function() {
        if (flag) {
            flag = false; // 关闭节流阀
            // 如果走到了最后复制的一张图片,此时 我们的ul 要快速复原 left 改为 0
            if (num == ul.children.length - 1) {
                ul.style.left = 0;
                num = 0;
            }
            num++;
            animate(ul, -num * focusWidth, function() {
                flag = true; // 打开节流阀
            });
            // 8. 点击右侧按钮,小圆圈跟随一起变化 可以再声明一个变量控制小圆圈的播放
            circle++;
            // 如果circle == 4 说明走到最后我们克隆的这张图片了 我们就复原
            if (circle == ol.children.length) {
                circle = 0;
            }
            // 调用函数
            circleChange();
        }
    });

    // 9. 左侧按钮做法
    arrow_l.addEventListener('click', function() {
        if (flag) {
            flag = false;
            if (num == 0) {
                num = ul.children.length - 1;
                ul.style.left = -num * focusWidth + 'px';

            }
            num--;
            animate(ul, -num * focusWidth, function() {
                flag = true;
            });
            // 点击左侧按钮,小圆圈跟随一起变化 可以再声明一个变量控制小圆圈的播放
            circle--;
            // 如果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 = setInterval(function() {
        //手动调用点击事件
        arrow_r.click();
    }, 2000);

})

2: 节流阀

防止轮播图按钮连续点击造成播放过快。

节流阀目的:当上一个函数动画内容执行完毕,再去执行下一个函数动画,让事件无法连续触发。

核心实现思路:利用回调函数,添加一个变量来控制,锁住函数和解锁函数。

开始设置一个变量var flag = true;

If(flag) {flag = false; do something} 关闭水龙头

利用回调函数动画执行完毕,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);
}

3: 返回顶部

滚动窗口至文档中的特定位置。

window.scroll(x, y)

注意,里面的x和y 不跟单位,直接写数字

案例分析:

①带有动画的返回顶部

②此时可以继续使用我们封装的动画函数

③只需要把所有的left 相关的值改为跟页面垂直滚动距离相关就可以了

④页面滚动了多少,可以通过window.pageYOffset得到

⑤最后是页面滚动,使用window.scroll(x,y)



 

    
    
    
    Document
    

 

    
返回顶部
头部区域
主体部分

4:按钮-筋斗云

鼠标经过某个小li,筋斗云跟这到当前小li

位置鼠标离开这个小li,筋斗云复原为原来的位置

鼠标点击了某个小li,筋斗云就会留在点击这个小li 的位置

案例分析:

①利用动画函数做动画效果

②原先筋斗云的起始位置是0

③鼠标经过某个小li,把当前小li 的offsetLeft 位置做为目标值即可

④鼠标离开某个小li,就把目标值设为0

⑤如果点击了某个小li,就把li当前的位置存储起来,做为筋斗云的起始位置





    
    
    Document
    
    

    



    


你可能感兴趣的:(javascript,开发语言,ecmascript)