# BOM
ECMAScript
是 javascript
的核心,但是如果要在 web
中使用 javascript
,那么 BOM
(浏览器对象模型)才是真正的核心。BOM 提供了很多对象,用于访问浏览器的功能,这些功能与任何网页内容无关。
BOM
的核心对象是 window
,它表示浏览器的一个实例。在浏览器中, window
对象有双重角色,它既是通过 javascript
访问浏览器窗口的一个接口,又是 ECMAScript
规定的 Global
对象。所有全局作用域中声明的变量、函数都会变成 window
对象的属性和方法。
# Event
# 事件对象
Event
对象代表事件的状态,比如事件在其中发生的元素、键盘按键的状态、鼠标的位置、鼠标按钮的状态
获得 event 对象兼容性写法 event || window.event
event.target
触发事件的目标 获得 target 兼容型写法 event.target || event.srcElement
event.currentTarget
绑定事件的目标
e.pageX/Y
获取鼠标点击的相对于页面的位置
e.clientX/Y
获取鼠标点击的相对于可视区域的位置
e.screenX/Y
获取鼠标点击的相对于屏幕的位置
e.offsetX/Y
表示鼠标指针位置相对于触发事件的对象的位置
获取鼠标相对于绑定事件的元素的位置 e.pageX/Y - 绑定事件的元素距离页面的左/上边距(offsetLeft/Top)
event.type
事件的类型
event.key
键盘按键码
event.button
鼠标点击的按键(只认识三个键) 可在 onmousedown
事件中测试
- === 0 您点击了鼠标左键
- === 1 您点击了鼠标中键
- === 2 您点击了鼠标右键
# 事件委托
事件委托,又名事件代理。就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。
通过监听一个父元素,来给不同的子元素绑定事件,减少监听次数,从而提升速度。 由于事件的冒泡机制,可以使用事件委托的方式给元素添加事件
# 原因
- 在 JavaScript 中,添加到页面上的事件处理程序数量将直接关系到页面的整体运行性能,因为需要不断的与 dom 节点进行交互,访问 dom 的次数越多,引起浏览器重绘与重排的次数也就越多,就会延长整个页面的交互就绪时间。
- 如果给一类元素添加事件,再添加过事件之后新添加的元素不会被绑定上之前事件
# 解释
使用取快递来解释这个现象: 有三个同事预计会在周一收到快递。为签收快递,有两种办法:一是三个人在公司门口等快递;二是委托给前台 MM 代为签收。现实当中,我们大都采用委托的方案(公司也不会容忍那么多员工站在门口就为了等快递)。前台 MM 收到快递后,她会判断收件人是谁,然后按照收件人的要求签收,甚至代为付款。这种方案还有一个优势,那就是即使公司里来了新员工(不管多少),前台 MM 也会在收到寄给新员工的快递后核实并代为签收。
这里面有两个要点:
- 现在委托前台的同事是可以代为签收的,即程序中的现有的 dom 节点是有事件的;
- 新员工也是可以被前台 MM 代为签收的,即程序中新添加的 dom 节点也是有事件的。
# 使用例子
# closet 方法
closest 所有直接在给定元素之上的元素都被称为它的“祖先”。
换句话说,祖先是:父类,父类的父类,它的父类等。祖先们一起组成了从元素到顶端的父类链。
elem.closest(css) 方法会查找与 CSS 选择器匹配的最接近的祖先。elem 自己也会被搜索。
换句话说,方法 closest 在元素中得到了提升,并检查每个父类。如果与选择器匹配,则停止搜索并返回祖先。
例如:
<h1>Contents</h1>
<div class="contents">
<ul class="book">
<li class="chapter">Chapter 1</li>
<li class="chapter">Chapter 1</li>
</ul>
</div>
<script>
let chapter = document.querySelector(".chapter"); // LI
alert(chapter.closest(".book")); // UL
alert(chapter.closest(".contents")); // DIV
alert(chapter.closest("h1")); // null(因为 h1 不是祖先)
</script>
常见的事件委托的写法
# 阻止冒泡
chrome/Opera 等
event.stopPropagation();
IE <= 10 专用event.cancelBubble = true
兼容写法event.stopPropagation ? event.stopPropagation() : event.cancelBubble = true;
# 阻止默认事件
阻止默认事件:
event.preventDefault ? event.preventDefault() : event.returnValue = false
# 三大家族
# 一、 Offset
家族
家族成员:
offsetWidth
offsetHeight
offsetLeft
offsetTop
offsetParent
# offsetWidth
和offsetHeight
检测盒子自身宽高
这两个属性,他们绑定在了所有的节点元素上。获取之后,只要调用这两个属性,我们就能够获取元素节点的宽和高。
offset宽/高 = 盒子自身的宽/高(width/height) + padding +border
# offsetLeft
和 offsetTop
检测距离有定位的父盒子的左/上面的距离
如果父级都没有定位则以
body
为准,offsetLeft
从父亲的padding
开始算,父亲的border
不算。 在父盒子有定位的情况下,offsetLeft == style.left
(去掉 px)
# offsetTop/Left
和 style.top/left
的区别:
- 最大区别在于
offsetTop/Left
可以返回没有定位盒子的距离左侧的位置。而style.top/left
不可以 offsetTop/Left
返回的是整数,而style.top/left
返回的是字符串,除了数字外还带有单位:px
<div class="d1" style="position:relative;width: 300px;height: 200px;background-color: green;"></div>
<script>
var d1= document.getElementsByClassName('d1')[0];
d1.onclick = function(){
d1.style.width = '400.499999999999999px';
console.log(d1.offsetWidth); // 401
console.log(d1.style.width); // "400.5px"
d1.style.top = "50.49999999999999px";
console.log(d1.style.top) // "50.5px"
console.log(d1.offsetTop) // 51
}
</script>
offsetTop/Left
只读,而style.top/left
可读写。(只读是获取值,可写是赋值)obj.style.xxx
只能获取行内样式
# offsetParent
检测父系盒子中带有定位的父盒子节点
1、返回该对象的父级 (带有定位)
如果当前元素的父级元素没有进行 CSS 定位(absolute,relative,fixed)
offsetParent
为body
2、如果当前元素的父级元素中有 CSS 定位offsetParent
取最近的那个父级元素。
# 匀速、缓动动画
动画原理
- 物体运动: 起点,终点,速度(距离/时间)
- 盒子的位置 = 盒子本身所在的位置 + 步长
缓动动画计算方式:
leader = leader + (target - leader) / 10;
起点 = 起点 + (终点 - 起点) / 10;
// 匀速动画的封装
function averageMove(ele, target) {
// 先清除上一个元素上绑定的定时器 防止定时器冲突
clearInterval(ele.timer);
// 设置定时器
ele.timer = setInterval(function() {
// 起点 style.left 只能获取行内样式
var start = ele.offsetLeft;
// 步长 需要判断方向
var step = target > start ? 10 : -10;
// 运动 运动距离 = 起点 + 步长
ele.style.left = start + step + "px";
// 需要停止定时器的条件 终点与起点的距离 <= 步长
if (Math.abs(target - start) <= Math.abs(step)) {
clearInterval(ele.timer);
// 直接到终点
ele.style.left = target + "px";
}
}, 17);
}
// 缓动动画封装
function slowlyMove(ele, target) {
// 先清除上一个元素上绑定的定时器 防止定时器冲突
clearInterval(ele.timer);
// 设置定时器
ele.timer = setInterval(function() {
// 起点 style.left 只能获取行内样式
var start = ele.offsetLeft;
// 步长 需要判断方向
var step = (target - start) / 10;
// 判断步长区间
if (Math.abs(step) < 1) {
// [-1,1]
step = step > 0 ? 1 : Math.floor(step);
}
// 运动 运动距离 = 起点 + 步长
ele.style.left = start + step + "px";
// 需要停止定时器的条件 终点与起点的距离 <= 步长
if (start + step === target) {
console.log("stop ...");
clearInterval(ele.timer);
}
}, 17);
}
#
# 二、 Scroll
家族
家族成员:
scrollWidth
,scrollHeight
,scrollTop
,scrollLeft
# scrollWidth
和 scrollHeight
检测盒子的宽高 内容高度 + padding。(调用者:节点元素。属性。) 盒子内容的宽高。(如果有内容超出了,显示内容的宽/高度)
# scrollTop
, scrollLeft
可读写
前提: 目标元素有滚动条 被浏览器或父类遮挡的头部和左边部分。 可以获取或设置一个元素的内容垂直滚动的像素数。element.scrollTop/Left = XXX
**获取页面卷入高度的浏览器兼容问题: ** 各浏览器下 scrollTop 的差异
- IE6/7/8: 对于没有 doctype 声明的页面里可以使用 document.body.scrollTop 来获取 scrollTop 高度 ; 对于有 doctype 声明的页面则可以使用 document.documentElement.scrollTop ;
- Safari: safari 比较特别,有自己获取 scrollTop 的函数 : window.pageYOffset ;
- Firefox: 火狐等等相对标准些的浏览器就省心多了,直接用 document.documentElement.scrollTop ;
兼容写法:
var scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop
var scrollLeft = document.documentElement.scrollLeft || window.pageXOffset || document.body.scrollLeft
# onscroll
事件
解释:当元素的滚动条滚动时触发的事件。
onscroll
事件貌似任何实体元素都可以绑定,这里的实体元素包括 DOM 元素、window 元素、document 元素。
用法即:element.onscroll=function(){};
需要注意的是,设置此事件的元素一定要有滚动条
# window 的滚动事件
window.scroll(x,y)
是让 window 滚动条滚动到那个 x,y 坐标。//x 是水平坐标,y 是垂直坐标。
window.scrollBy(-x,-y)
是让 window 滚动条相对滚动到某个坐标,- 10 即相对向左/向上滚动 10 像素。
window.scrollTo(x,y)
和window.scroll(x,y)
一样,但是它不兼容 IE。
element.scrollIntoView(boolean)
让元素贴顶或者贴底,相对于可视区域
# 三、Client 家族
家族成员
clientWidth
clientHeight
clientTop
clientLeft
# clientWidth
clientHeight
检测盒子的宽高 clientHeight/W 盒子 自身宽高 + padding 内容溢出不算,滚动条(默认宽度 17)不算
document.documentElement.clientWidth/clientHeight 获取浏览器可视区域的宽高 没有兼容问题
window.innerWidth/innerHeight IE <= 8 不支持 表示获取 window 可视区域的内部大小(带滚动条)
window.outerWidth/outerHeight IE <= 8 不支持 表示整个浏览器窗体的大小
当有滚动条的时候 clientWidth/clientHeight 不算滚动条的宽度
# clientTop
clientLeft
只读
表示内容区域的左上角相对于整个元素左上角的位置 实际上就是 border 的宽度
内容区域 内容+padding padding 之外就剩 border
一个元素顶部和左侧边框(border)的宽度
# 获取元素内联或外联的样式
行内样式 可以通过
ele.style.styleName
获取内联样式和外联样式可以通过以下两种方式获取
window.getComputedStyle(element, [pseudoElt]).styleName
返回的是一个字符串- 仅用于谷歌和火狐等标准浏览器
element
用于获取计算样式的元素。pseudoElt
指定一个要匹配的伪元素的字符串。必须对普通元素省略(或null
),一般都写成null
. 如果要获取伪元素的样式,则写上要获取的伪元素的名字
element.currentStyle.styleName
仅用于 IE兼容写法:
/**
* 获取任意元素的css样式
**/
function getStyle(ele, styleName) {
if (ele.currentStyle) {
return ele.currentStyle[styleName];
} else {
return window.getComputedStyle(ele, null)[styleName];
}
}
# 打开窗口
使用 window.open() 方法既可以导航到一个特定的 URL,也可以打开一个新的浏览器窗口。这个方法需要四个参数:
- 需要加载的 url 地址
- 窗口的目标
- 一个特性的字符串
- 是否创建新的历史记录
# location
location 对象提供了与当前窗口中加载的文档有关的信息,还提供了一些导 航功能。
href 属性
href 属性可以获取或修改当前页面的完整的 URL 地址,使浏览器跳转到指定页面。
assign() 方法
所用和 href 一样,使浏览器跳转页面,新地址错误参数传递到 assign ()方法中
replace() 方法
功能一样,只不过使用 replace 方法跳转地址不会体现到历史记录中。
reload() 方法
用于强制刷新当前页面
# navigator
navigator 对象包含了浏览器的版本、浏览器所支持的插件、浏览器所使用的语言等各种与浏览器相关的信息。
有时候会使用 navigator 的 userAgent 属性来检查用户浏览器的版本。
# screen 对象
screen 对象基本上只用来表明客户端的能力,其中包括浏览器窗口外部的显示器的信息,如像素宽度和高度等。
该对象作用不大,我们一般不太使用。
# history
history 对象保存着用户上网的历史记录,从窗口被打开的那一刻算起。
go() 方法
使用 go() 方法可以在用户的历史记录中任意跳转,可以向后也可以向前。
back() 方法
向后跳转
forward() 方法
向前跳转