# BOM

ECMAScriptjavascript 的核心,但是如果要在 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 您点击了鼠标右键

# 事件委托

事件委托,又名事件代理。就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。

通过监听一个父元素,来给不同的子元素绑定事件,减少监听次数,从而提升速度。 由于事件的冒泡机制,可以使用事件委托的方式给元素添加事件

# 原因

  1. 在 JavaScript 中,添加到页面上的事件处理程序数量将直接关系到页面的整体运行性能,因为需要不断的与 dom 节点进行交互,访问 dom 的次数越多,引起浏览器重绘与重排的次数也就越多,就会延长整个页面的交互就绪时间。
  2. 如果给一类元素添加事件,再添加过事件之后新添加的元素不会被绑定上之前事件

# 解释

使用取快递来解释这个现象: 有三个同事预计会在周一收到快递。为签收快递,有两种办法:一是三个人在公司门口等快递;二是委托给前台 MM 代为签收。现实当中,我们大都采用委托的方案(公司也不会容忍那么多员工站在门口就为了等快递)。前台 MM 收到快递后,她会判断收件人是谁,然后按照收件人的要求签收,甚至代为付款。这种方案还有一个优势,那就是即使公司里来了新员工(不管多少),前台 MM 也会在收到寄给新员工的快递后核实并代为签收。

这里面有两个要点:

  1. 现在委托前台的同事是可以代为签收的,即程序中的现有的 dom 节点是有事件的;
  2. 新员工也是可以被前台 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

# offsetWidthoffsetHeight

检测盒子自身宽高

这两个属性,他们绑定在了所有的节点元素上。获取之后,只要调用这两个属性,我们就能够获取元素节点的宽和高。

offset宽/高 = 盒子自身的宽/高(width/height) + padding +border

# offsetLeftoffsetTop

检测距离有定位的父盒子的左/上面的距离

如果父级都没有定位则以 body 为准, offsetLeft 从父亲的 padding 开始算,父亲的 border 不算。 在父盒子有定位的情况下,offsetLeft == style.left(去掉 px)

# offsetTop/Leftstyle.top/left 的区别:
  1. 最大区别在于 offsetTop/Left 可以返回没有定位盒子的距离左侧的位置。而 style.top/left 不可以
  2. 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>
  1. offsetTop/Left 只读,而 style.top/left 可读写。(只读是获取值,可写是赋值)
  2. obj.style.xxx 只能获取行内样式

# offsetParent

检测父系盒子中带有定位的父盒子节点

1、返回该对象的父级 (带有定位)

  如果当前元素的父级元素没有进行 CSS 定位(absolute,relative,fixed) offsetParentbody 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

# scrollWidthscrollHeight

检测盒子的宽高 内容高度 + 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 获取

内联样式和外联样式可以通过以下两种方式获取

  1. window.getComputedStyle(element, [pseudoElt]).styleName 返回的是一个字符串

    • 仅用于谷歌和火狐等标准浏览器
    • element 用于获取计算样式的元素。
    • pseudoElt 指定一个要匹配的伪元素的字符串。必须对普通元素省略(或null),一般都写成 null. 如果要获取伪元素的样式,则写上要获取的伪元素的名字
  2. element.currentStyle.styleName 仅用于 IE

  3. 兼容写法:

/**
 * 获取任意元素的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 的 userAgent 属性来检查用户浏览器的版本。

# screen 对象

screen 对象基本上只用来表明客户端的能力,其中包括浏览器窗口外部的显示器的信息,如像素宽度和高度等。

该对象作用不大,我们一般不太使用。

# history

history 对象保存着用户上网的历史记录,从窗口被打开的那一刻算起。

  • go() 方法

    使用 go() 方法可以在用户的历史记录中任意跳转,可以向后也可以向前。

  • back() 方法

    向后跳转

  • forward() 方法

    向前跳转

上次更新: 10/9/2019, 2:56:35 PM