仰望星空的天台

0%

DOM和BOM学习笔记

document object model and browser object model

众所周知,js是由 ecmascript + BOM + DOM 组成的

ecmascript 指的是js的各种基本语法,数据类型,继承,对象,原型等等等

而BOM和DOM是w3c组织为了我们使用ecmascript能够更好的操控html页面和css,来写的一套程序接口, 有了BOM(browser object model)我们就可以获取当前用户正在使用的浏览器的相关参数,信息 窗口的位置、大小及以下这几个对象 [document frames history location navigator screen]

document下面又包括了 [anchors forms images links location]

而bom之上则是window对象

这里请允许我盗张图 pic

有了DOM我们就可以把html想成一棵节点树 dom-tree

有了这棵树,及其配套的方法,我们就可以通过html任意的控制document下的任意节点,包括他们的子级,父级,兄弟,及其对应的css等等等

我们可以简单的认为 window 包含 bom,bom下面包含了dom

DOM

DOM下面包含的类型有十二种 | | NodeType | Named Constant |

1 ELEMENT_NODE
2 ATTRIBUTE_NODE
3 TEXT_NODE
4 CDATA_SECTION_NODE
5 ENTITY_REFERENCE_NODE
6 ENTITY_NODE
7 PROCESSING_INSTRUCTION_NODE
8 COMMENT_NODE
9 DOCUMENT_NODE
10 DOCUMENT_TYPE_NODE
11 DOCUMENT_FRAGMENT_NODE
12 NOTATION_NODE

我们可以使用nodeType来查看DOM树下的某个元素的属性类型

childNodes & children

对于任意的一个DOM树下的结点,我们可以访问他的childNodes属性来获取他底下所有子结点

childNodes是一个类数组,所以也可以访问其下的length方法,并且你还可以访问并修改其下的元素

例如如下html结构:

<ul id="ul"><li></li><li></li><li></li></ul>

你可以通过遍历来为ul下面的子节点添加背景色

var oUl = document.getElementById("ul");

for(var i = 0; i < oUl.childNodes.length ; i++)
{
	oUl.childNodes[i].style.background = "red";
}

细心的同学可能就会发现,为什么上面的html结构,我没有给li添加缩进 这是因为,缩进也是字符,算是文本结点,也会被标准的浏览器获取到(但是低版本的IE浏览器不能够获取到文本结点),而文本节点并没有style这个属性,如果直接给循环添加颜色,就会报错。

当然没有缩进肯定是令人无法忍受的,如果我们需要使用childNodes来遍历子节点,就需要能判断子节点的类型。

那么,如何判断子节点的类型呢?

这就需要我们使用nodeType属性进行判断了

如果对应缩进的html

<ul id="ul">
	<li></li>
	<li></li>
	<li></li>
</ul>

可以这么写JS

var oUl = document.getElementById("ul");

for(var i = 0; i < oUl.childNodes.length ; i++)
{
	if( oUl.childNodes[i].nodeType == 1 ) // 标号1对应元素结点
	{
		oUl.childNodes[i].style.background = "red";
	}
}

children 和 childnodes用法差不多,但是children只是获取父级下的元素节点,除元素结点之外的子节点都不会获取,所以平时我们可能用的children多一些

child

上面我们介绍了两个集合方法的属性,其实还有其他的属性可以访问父级下的结点

例如firstChild,我们就可以访问父级下的第一个结点,当然如果是有缩进的html结构,在标准浏览器下,获取的是缩进的文本节点,低版本IE浏览器获取的是元素了

那么如果我们要获取第一个元素节点呢?可以标准浏览器为我们提供的firstElementChild(这个属性在低版本浏览器下并不支持)

兼容的写法就是

var oUl = document.getElementById("ul");
var oFirstChild = oUl.firstElementchild || oUl.firstChild

同理也就有lastChildlastElementChild

兄弟结点

我们还可以同通过nextSibling 来获取同级的下一个节点 用nextElementSibling 来获取同级的下一个元素节点

那么同理,获取上一个节点和元素节点我们就可以使用previousSiblingpreviousElementSibling

兼容方法也类似

var oPreviousChild = oUl.previousElementSibling || oUl.previousSibling;
var oNextChild = oUl.nextElementSibling || oUl.nextSibling ;

attributes

这里还有一个有意思的东西,属性结点. 我们可以通过属性结点,来获取一个元素的属性

通过获取元素,并访问其下的attributes属性(只读)

例如一个div

<div id="owen" class="zyz"></div>

我们通过attributes 就能获取div的两个属性 id 和 class

var oDiv = document.getElementById("owen");

for(var i = 0; i < oDiv.attributes.length ; i++)
{
	console.log("第"+ i +"个属性的名称是" + oDiv.attributes[i].name + " 值是" + pDiv.attributes[i].value);
}

父结点

如果我们希望通过子级来访问父级的话呢?

我们可以使用ParentNode这个只读属性来访问父节点

例如如下布局:

<ul>
	<li>段落一 <a href="javascript:;">隐藏</a></li>
	<li>段落二 <a href="javascript:;">隐藏</a></li>
	<li>段落三 <a href="javascript:;">隐藏</a></li>
	<li>段落四 <a href="javascript:;">隐藏</a></li>
</ul>

如果我们平时需要实现点击隐藏的功能,可以当点击的时候获取点击标签索引,然后再根据索引将li隐藏

现在我们可以直接省略获取索引这一步,直接使用parentNode来控制父节点的隐藏

var oA = document.getElementsByTagName('a');

for(var i in oA)
{
	oA[i].onclick = function() {
		this.parentNode.style.display = "none";
	}
}

除了parentNode 还有 offsetParent , 这个属性是获取的子级的定位父级

例如如下布局:

<div id="div1">
	<div id="div2">
		<div id="div3"></div>
	</div>
</div>

如果div3的所有父级都没有设置position,那么就是没有定位的元素 如果这时候我获取div3的定位父级,那么获取的是body

var oDiv3 = document.getElementById("div3");

console.log(oDiv3.offsetParent.tagName); // body

但是如果我给div1 或者 div2 添加了position,那么被添加了position的div父级 就成了 div3的 定位父级

如果div1 和 div2 都添加了position 那么浏览器会选取距div3最近的父级也就是div2作为div3的定位父级

<div id="div1">
	<div id="div2" style="positon:relative;">
		<div id="div3"></div>
	</div>
</div>
var oDiv3 = document.getElementById("div3");

console.log(oDiv3.offsetParent.id); // div2

IE7- 的浏览器还有一些不qi合pa群的特性,如果你要兼容IE7以下的浏览器的话,需要考虑 例如 上面div3之上的父级都没有定位,这时候div3默认的定位父级就是body标签,但是如果给div3添加了定位,那么div3的父级就变成了html

还有IE7的 layout , 有些特殊的Css属性会触发layout 把当前元素变成定位元素 例如 zoom属性

定位、宽高的获取

页面的各种宽高获取

① 获取浏览器窗口的宽和高

在标准浏览器下面,我们可以使用 window.innerWidth[Height]

在非标准浏览器下,我们可以使用 document.documentElement.clientWidth[Height]

在jq中,我们可以使用 $(window).width[height]

② 获取整个文档的宽和高

无论标准非标准,我们都使用 document.body.clientWidth[Height]

在jq中,我们可以使用 $(document).width[height]

没有 window.body.clientWidth[clientHeight]

③ 获取整个电脑屏幕的宽和高

window.screen.height 屏幕分辨率的高 window.screen.width 屏幕分辨率的宽

④ 获取可用工作区宽和高 (即电脑屏幕 - 底部任务栏的高) window.screen.availHeight 屏幕可用工作区高度 window.screen.availWidth 屏幕可用工作区宽度

③和④都是一个定值固定死的,例如我的VAIO 电脑 就是 1600900的,减去任务栏后是1600860

⑤ 获取去除滚动条宽的可视区宽高(浏览器窗口 - 滚动条的宽)

document.body.scrollWidth document.body.scrollHeight

⑥ 被卷起来的页面宽和高

document.body.scrollTop[Left]

或者

document.documentElement.scrollTop[Left]

这里涉及兼容性的问题我们下面会提到

!!!!不得不吐槽没总结之前真的好乱!!!!

offsetLeft[Top]

offsetLeft 和 Top 可以获取自己元素到定位父级的距离

var oDiv3 = document.getElementById("div3");

console.log(oDiv3.offsetLeft);

如果要获取一个元素到浏览器最顶部的距离(最左部的距离)呢? 我们可以封装一个函数

var obj = document.getElementById("div3");
var Left = 0;
var Top  = 0;

while( obj ) {	
	Top += obj.offsetTop;
	Left += obj.offsetLeft;
	obj = obj.offsetParent;
}

思路就是,不断的取到定位父级的距离,一直到Body, 而Body的定位父级是Null(不是html) Body的offsetTop(Left)是0 所以当循环到Body的时候,循环停止,返回的就是一个元素到最顶端的距离

盒模型的三种宽高

style.width & height 内容区域宽高 包括: 实际宽高 (带px单位) offsetWidth & Height 可视区域宽高 包括: 实际宽高 + padding clientWidth & Height 盒子实际宽高 包括: 实际宽高 + padding + border

获取页面滚动距离

获取页面滚动距离使用的scrollTop这个属性来获取,但是依然存在一些兼容性问题

① 火狐和iE认为,滚动条类属于documentElement ② chrome认为,滚动条类属于body

所以当我们需要获取页面已经滚动的高度的时候,就需要做这样的兼容

var ScrollTop = document.body.scrollTop || document.documentElement.scrollTop;

scrollLeft也可以类比如此

获取滚动条总高度

如果一个盒子带有滚动条,那么如果要获取总高度的话,使用上文提到的 style.width & height offsetWidth & Height clientWidth & Height

获取的高度是不包括被滚动条隐藏的高度,所以如果需要获取包含隐藏条隐藏的高度的内容,我们必须使用scrollHeight 这一属性

var ScrollHeight = document.body.scrollHeight;

同时Width也是如此

这里为什么不获取`documentElement的scrollHeight呢? 因为body有外marin和padding,如果使用documentElement获取的话会把body的外边距和内边距包含进来,如果我们没有对body进行reset的话,就会有一定的偏差(body的内外边距)。

动态操纵元素

使用createElement 添加元素

document.createElement("需要创建的标签名");

使用appendChild 创建元素

被添加的元素对象.appendChild( 添加的元素对象 );

我们也可以使用insertBefore 在每个元素的之上插入元素

插入元素的父级.insertBefore( 被添加的元素 , 某个定位元素 );

但是如果在父级元素一个子节点都没有的情况下 在IE下,使用insertBefore会报告错误 但是在标准浏览器下,使用insertBefore会默认以appendChild进行插入

所以如果我们需要兼容IE和标准浏览器的话,可以这样写

if( !父级.children[0] )
{
	父级.appendChild( 被添加的元素对象 );
}else {
	父级.insertBefore( 被添加的元素对象  某个定位元素 );
}

除此之外,我们还可以使用removeChild 删除元素

父级.removeChild( 被删除的元素 );

还可以使用replaceChild 来替换元素

父级.replaceChild( 替换元素 , 被替换元素 );

需要注意的是,以上的全部方法,都需要在父级下进行操作,也就是说如果需要动态的操作元素,一定要获取到这些元素的父级

自己封装一个getByClassName

选取页面的元素,我们可以使用getElementById 或者 getElementsByTagName 但是有没有一个方法能够通过class来选取元素呢?

在标准浏览器下是有的, getElementsByClassName 但是在非标准浏览器下却没有这个函数,所以如果需要兼容,就的自己写一个

function getByClass( parent  , sClass , tag ) {
	var tag = tag || '*';
	var aTag = parent.getElementsByTagName(tag);
	var result = [];

	for( var i = 0 ; i < aTag.length ; i++ )
	{
		var aTagClass = aTag[i].className.split(' ');

		for( var j in aTagClass )
		{
			if( aTagClass[j] == sClass )
			{
				result.push(aTag[i]);
				break;
			}
		}
	}

	return result;
}

调用的时候就将需要 第一个参数获取类元素的父级传参,第二个参数为匹配的class类名,第三个参数选填,为匹配的标签名称(如果没填默认全部选中)

注意:使用的时候返回的是一个数组,如果获取的类只有一个,那么就需要使用[0]

表格的DOM方法

如下表格结构

<table width="100%" boder="1px">
	<thead>
		<tr>
			<th>编号</th>
			<th>姓名</th>
			<th>性别</th>
			<th>年龄</th>	
		</tr>
	</thead>

	<tbody>
		<td>01</td>
		<td>欧阳</td>
		<td></td>
		<td>19</td>
	</tbody>

	<tbody>
		<td>02</td>
		<td>老王</td>
		<td></td>
		<td>20</td>
	</tbody>

	<tbody>
		<td>03</td>
		<td>老李</td>
		<td></td>
		<td>21</td>
	</tbody>

	<tfoot>
		<td>平均年龄</td>
		<td></td>
		<td></td>
		<td>20</td>
	</tfoot>
</table>

如果我们需要对表格进行操作,DOM为我们提供了一些DOM方法进行操作 例如我们要把每行都变色

var oTable = document.getElementsByTagName('table')[0];
		
	oTable.tBodies[0].style.background = 'yellow';
	oTable.tBodies[1].style.background = 'red';
	oTable.tBodies[2].style.background = 'pink';

我们可以操作每个tBodies属性来进行改变

如果我们只需要修改表格头,那么就使用tHead

var oTable = document.getElementsByTagName('table')[0];
	oTable.tHead.style.background = 'yellow';

当然我们也可以操作他们的tfoot

var oTable = document.getElementsByTagName('table')[0];
	oTable.tFoot.style.background = 'yellow';

但是如果我们需要修改某个表格内的元素呢? 例如第一行列第四列,希望将其值修改成20

var oTable = document.getElementsByTagName('table')[0];
	oTable.tBodies[0].rows[0].cells[3].innerHTML = "20";

当然我的例子里面是一个body对应一行,当然也可以一个body里有任意多行,照葫芦画瓢

DOM表单

表单这个东西十分常用,当我们需要操纵表单的时候,DOM也给我们提供了操纵的方法 例如:

<form>
	<input type="text" name="text1" value="内容">
</form>

在表单中,最有用的一个属性就是name属性了,通过name我们可以标识发送给后端的内容是什么,也就是说name在JS里面基本可以替代ID 和 class来获取和操控表单元素,所以DOM也为name提供了相应的方法

例如我们可以这样来获取一个表单底下的表单元素的内容

var oForm = document.getElementsByTagName("form")[0];

console.log(oForm.text1.value);// 内容

submit 和 onsubmit

表单内有一个特殊的按钮submit提交,使用这个可以进行表单的提交,提交的结果会在URI后面添加一个信息项

?提交按钮的name=其value

当我们进行提交的时候,我们可以使用onsubmit来控制提交

<form action="http://www.baidu.com">
	<input type="text" name="text1" >
	<input type="submit" name="dosubmit" value="提交">
</form>
var oForm = document.getElementById('oform');

	oForm.onsubmit = function() {
		if( this.text1.value == '' ) {
			alert("请输入内容");
			return false;
		}
	}

当用户没有输入任何东西的时候,我们就可以阻止用户提交数据

除了onsubmit还有一个submit事件,submit事件可以让我们使用js动态的触发提交表单

setTimeout(function() {
	oForm.submit();
},1000)

例如我们可以让js加载1秒后,自动提交表单。

reset 和 onreset

当我们需要重置输入内容的时候,我们可以使用reset

<form action="http://www.baidu.com" id="oform">
	<input type="text" name="text1" >
	<input type="reset" name="doreset" value="reset">
</form>

点击reset事件就可以重置输入框内的内容

当这个按钮被点击的时候,我们可以使用onreset来控制callback

var oForm = document.getElementById('oform');

	oForm.onreset = function() {
		return confirm("你是否要重置文本框的内容");
	}

reset事件类比submit事件,功能也是使用js动态控制内容重置

event对象

所谓event对象就是当事件执行的时候,存储事件执行的各种属性的对象。 在ie/chrome底下 event对象是一个全局的内置对象 但是在标准浏览器下 event对象是根据事件执行的时候传入的(chrome既有全局event又有根据第一参数传入)

但是在非标准浏览器下,只有一个全局的内置对象

如果我们需要兼容两者的话可以这样写

document.click = function(ev) {
	var ev = ev || event; // event兼容非标准的IE浏览器
}

鼠标位置ClientX/Y

通过访问ev下的ClientX/Y,我们可以得到鼠标在页面可视区中的位置 例如

<style>
	#div{
		width: 100px; height: 100px; border-radius: 50%; background: blue; transition: .6s;
	  	margin-left: -50px;
	  	margin-top:  -50px;
	  	position: absolute;
	  }
</style>

</head>
<body>
	<div id="div"></div>
</body>
var oDiv = document.getElementById("div");

document.onmousemove = function(ev) {
	var ev = ev || event;
	var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
	
	oDiv.style.left = ev.clientX + "px";
	oDiv.style.top  = ev.clientY + scrollTop + "px";
}

demo

这里注意一点,就是clientXY是在可视区的定位,而style.top[left] 是面对整个页面进行定位,所以我们在給盒子进行定位的时候,一定要加上 scrollTop[left] 及被卷起来的那部分的高度[宽度]

鼠标事件 pageX/Y

相对与clientX/Y 而言 pageX/Y 是指鼠标相对于整个页面的位移坐标 即: pageX[Y] = clientX[Y] + scrollTop[left] - clientTop

caveats

① IE678都不支持pageX[Y],也就是说,只有标准浏览器才能支持Page属性

鼠标事件 screenX[Y]

有了上面的基础,我们也可以理解screenX[Y]了, clientX[Y] 是鼠标到浏览器的坐标 而screenX[Y] 则是鼠标到屏幕的坐标

区别就在于浏览器的大小可以改变,但是屏幕的大小是绝对不会改变的

事件流

事件冒泡

我们先看一个栗子 demo

<style>
	#div1 {
		height: 200px;
		background: blue;
		padding: 50px;
	}

	#div2 {
		height: 100px;
		background: red;
		padding: 50px;
	}

	#div3 {
		height: 100px;
		background: yellow;
	}
</style>

</head>
<body>
	<div id="div1">
		<div id="div2">
			<div id="div3"></div>
		</div>
	</div>
</body>
var oDiv1 = document.getElementById("div1");
var oDiv2 = document.getElementById("div2");
var oDiv3 = document.getElementById("div3");

function fn() {
	alert(this.id);
}

oDiv1.onclick = fn;
oDiv2.onclick = fn;
oDiv3.onclick = fn;

当我们点击黄色盒子的时候,会发现不单单是黄色盒子的点击事件被触发了,第二层的红色盒子也被触发了,然后是最上层的蓝色盒子的点击事件被触发

为什么会发生这种事呢?因为,当我们点击一个盒子的时候,浏览器会把我们点击的这个行动传递给父级,就像鱼吐气冒泡那样一直传递到最上级

这种机制,我们称之为事件冒泡机制

为什么需要这种机制呢?

比如当我们需要在蓝色盒子上添加事件,但是蓝色盒子里面有红色和黄色盒子被挡住了,如果没有冒泡机制的话,点击的面积是不是大幅度减小了(我们只能点击蓝色的区域) 但是如果有了事件冒泡机制,当我们点击黄色盒子的时候,点击的动作就会传递到蓝色盒子,然后就能执行蓝色盒子附带的事件函数

如果有些时候,我们不希望事件传入到父级呢?

我们可以使用事件对象下的一个方法cancelbubble

ev.cancelbubble = "true";

这样就能取消当前对象的当前事件的冒泡机制

第二种声明事件的方法

我们之前一般都是使用on + 事件名 来申明一个事件的

但是样声明事件,只能有且设置一个事件,如果需要对同一个对象设置另外一个事件的时候,那么上一个事件的函数就会被覆盖

那么怎么解决这个问题呢?

我们可以使用第二种声明事件的方法来实现,这里有一美名曰:事件监听 在IE下,我们使用attachEvent来实现监听事件 但是在标准浏览器下,我们使用addEventListener来实现监听事件 obj.attachEvent(on + 事件名称 , 事件函数) obj.addEventListener(事件名称 , 事件函数 , 是否捕获)

例如如果我要同时给document添加fn1 和 fn2两个函数

// 低版本的IE
document.attachEvent("onclick" , fn1);
document.attachEvent("onclick" , fn2);

// 实现标准的IE和其他标准浏览器
document.addEventListener("click" , fn1 , false);
document.addEventListener("click" , fn2 , false);

而关于是否事件捕获呢?

默认是false 也就是冒泡 , 如果写true的话则是捕获

这里还有一个bug 如果使用的是attachEvent的时候,我们还会遇到一个bug,那就是使用attachEvent来绑定事件函数的时候,其中的this会指向window

我们可以使用call和apply方法来扩展绑定函数的生存域

document.attachEvent("onclick" , function() {
	fn1.call(document , 参数)
});

我们可以声明一个函数bind来实现兼容

function bind(obj , evname , fn) {
	if(obj.addEventListener) {
		obj.addEventListener(evname , fn , false);
	} else {
		obj.attachEvent("on" + evname , function() {
			fn.call(obj);
		});
	}
}

事件捕获

在事件冒泡之前还会发生事件的捕获机制

什么意思呢?让我们看一张图

shootpic

当我们将addEventListener的第三个参数设置为true的时候,就会实行事件捕获机制,这样绑定的事件函数,就会在捕获阶段执行,如果设置为false 则绑定的事件函数就会在冒泡阶段执行

对比冒泡和捕获,无非就是遍历父级底下的全部子元素,并给它们刺激事件,不同点是执行的顺序不同

我们一般使用的是冒泡机制,因为冒泡机制相对于捕获机制的兼容性要好,所以一般 使用addEventListener的第三个参数都是设置为false

事件冒泡机制还有一个好处,就是可以执行事件委托 , 具体内容可以看本站的关于事件委托

删除事件

当我们不需要在某个事件上绑定函数的时候,我们就可以将其清空了

清空的方式有两种

① 当我们使用on + 事件 绑定函数的时候

document.onclick = null;

很简单直接给赋值为null就行了

② 当我们使用attachEvent 或者 addEventListener 的时候 我们得需要使用特殊的取消事件方法来取消,因为attachEventaddEventListener 可以绑定多个执行函数,并且 addEventListener 还可以声明捕获和冒泡机制,在不同机制下还可以绑定同一个函数。所以取消执行函数需要明确声明是那个函数 并且是那个机制下绑定的函数

document.detachEvent("onclick" , fn1);
document.removeEventListener("click" , fn1 , false);

拖拽运动

我们可以使用根据三个事件来实现拖拽

① 被拖拽对象的.onmousedown ② document.onmousemove ③ document.onmouseup

demo 代码比较简单,可以自行查看源码理解

这里会有一个问题,如果有文字被选中的时候,会出现问题 因为当鼠标按下的时候,如果页面中有文字被选中,就会触发浏览器默认的文字拖拽效果

在标准中很简单解决,需要在down事件下使用return false阻止默认事件就可以

但是在非标准的IE下这种方法还是无法解决这个问题 所以我们可以在鼠标按下的时候添加一句全局捕获

obj.setCapture && obj.setCapture();

全局捕获是只在IE下才会有的一句代码,如果有了这句代码,那么在鼠标按下的过程中,会把浏览器默认的事件捕获到拖拽函数中,这样浏览器的默认事件就不会触发了

当鼠标放开的时候在把全局捕获去掉,不至于影响其他事件的执行

obj.releaseCapture && obj.releaseCapture();

拖拽限制

我们可以把拖拽的范围限制在可视区范围内 只要在给对象的定位赋值前,对其范围进行控制就行

我们可以先把ev.clientX - disX 和 ev.clientY - disY

var L = ev.clientX - disX;
var T = ev.clientY - disY;
( L <= 0 ) && L = 0;
( L >= document.documentElement.clientWidth - obj.offsetWidth ) && L = document.documentElement.clientWidth - obj.offsetWidth;

( T <= 0 ) && T = 0;
( T >= document.documentElement.clientHeight - obj.offsetHeight ) && T = document.documentElement.clientWidth - obj.offsetHeight;

// 然后再把L 和 T 赋值给对象定位就可以
obj.style.width = L + "px";
obj.style.Height = T + "px";

碰撞检测

我们如果要检测两个对象是否相撞,可以根据九宫格原理来实现 pic

当蓝盒子的四个条边的位置都不在 黑盒子内 那么就是未碰撞状态 如果四边位置进入了黑盒子内,那么就是碰撞状态

所以我们要获取这八条边的位置

var T1 = obj1.offsetTop;
var L1 = obj1.offsetLeft;
var R1 = obj1.offsetLeft + obj1.offsetWidth;
var B1 = obj1.offsetTop  + obj1.offsetHeight;
 
var T2 = obj2.offsetTop;
var L2 = obj2.offsetLeft;
var R2 = obj2.offsetLeft + obj2.offsetWidth;
var B2 = obj2.offsetTop  + obj2.offsetHeight;

if( B1 < T2 || R1 < L2 || T1 > B2 || L1 > R2 ) {
	// 未碰撞执行的代码
} else {
	// 碰撞了执行的代码
}

改变拖拽对象的大小

栗子: demo

这里我设定的边距是 10px 也就是在10px的内边距内拖动盒子会改变盒子的大小

主要思路就是要将盒子的

定位 offsetTop 和 offsetLeft 宽高 offsetWidth 和 offsetHeight 点击位置 clientX/Y scrollTop scrollLeft

8个值全部获取到 在点击的时候获取到点击位置(要加上scrollTop 和 scrollLeft)

然后判断点击位置(如果在四边就给b赋值)

如果在中间则进行位移

键盘事件

键盘事件有两个 onkeyuponkeydown

onkeyup 的效果是弹起按钮 ,按钮的值输入过之后执行绑定函数 onkeydown的效果是按下按钮 , 按钮的值输入之前执行绑定函数 如果一直按住按钮,则会连续执行

我们怎么判断用户按下了什么按钮呢? 这里我们需要使用event对象下的一个属性,keyCode,键码

键码是每一个按钮的Ascii码值,功能相同的按键(比如左右shift)的键码相同

document.addEventListener = function("keydown" , function(ev) {
	var ev = ev || event;
	alert(ev.keyCode);
},false);

浏览器还给我们提供了三个功能键, shift ctrl alt

因为 keyCode 一次只能获取一个值,如果需要组合键 就需要使用功能键

我们使用键盘事件可以来实现一个留言本的功能

<input type="text" id="text1">
<ul id="list"></ul>
var oText = document.getElementById('text1');
var oList = document.getElementById('list');

oText.addEventListener('keyup' , function(ev) {
	
	var ev = ev || event;
	if( this.value != '' )
	{
		if( ev.keyCode == 13 && ev.ctrlKey )
		{
			var aLi = document.createElement('li');
			aLi.innerHTML = this.value;

			if( oList.children[0] )
			{	
				oList.insertBefore( aLi , oList.children[0] );
			} else {
				oList.appendChild( aLi );
			}
		}
	}
} , false);

demo

还有一点需要强调 : 不是所有的元素都能响应键盘事件

只有能获取焦点的元素才可以响应键盘事件

BOM

Browser object model 浏览器对象模型,我们通过这个模型可以操控窗口的各种方法

open

open方法可以接受4个参数,但是后两个参数,存在兼容性问题,所以暂不介绍,可以在《javascript高级程序设计》 中查找到相关方法。

window.open();

如果什么参数都不加,则默认新创建一新窗口,并打开一个空白页

第一个参数是需要打开的网页url,例如

window.open('www.baidu.com');

则创建一个新窗口,并跳转到百度

第二个参数是打开方式,默认为_blank

window.open('www.baidu.com','_self');

当我们给其设置_self的时候,则在本页面跳转到百度

window.open方法可以返回一个返回值 即返回打开新窗口的window对象

通过窗口的window对象我们可以控制新打开窗口的内容

var newWindow = null;

newWindow = window.open();
console.log(newWindow);

不过如果是非服务器环境,则会被浏览器阻止弹窗…

还有一个问题,就是跨域控制问题,也就是说我们并不能被允许控制打开的有域名的页面的内容

var newWindow = null;

newWindow = window.open('http://www.baidu.com');

newWindow.document.body.background = "red";

你当然是没有权利去改百度提供的页面的DOM内容的

当然如果要解决这个问题,可以参考html5跨域通信

close

我们可以使用close方法来关闭窗口

setTimeout(function() {
	window.close()
} , 3000);

例如,我们可以打开窗口后3秒使用close方法来关闭窗口

但是close方法存在兼容性问题

① chrome下我们可以正常关闭 ② firefox下我们的关闭操作被禁止 ③ iE 下会弹出是否运行关闭的对话框

所以,平时我们可能比较少使用关闭方法

但是,如果是我们使用代码新打开的一个窗口的话,各个浏览器就不会有限制性问题 例如如下布局:

<input type="button" value = "打开新窗口" id="btn1">
<input type="button" value = "关闭新窗口" id="btn2">
var oBtn1 = document.getElementById("btn1");
	var oBtn2 = document.getElementById("btn2");

	var newWindow = [];

	oBtn1.onclick = function() {
		newWindow.push(window.open());
	}

	oBtn2.onclick = function() {
		if( newWindow.length != 0 )
		{
			newWindow[(newWindow.length-1)].close();
			newWindow.length--;	
		}
	}

我们打开新的页面在不同浏览器下都能关闭

浏览器检测

window底下其中有一个对象是navigator(英:领航者)我们可以根据这个对象底下的userAgent属性来获知用户的浏览器类型,以及版本型号。

console.log(window.navigator.userAgent);

这样我们就可以判断用户的浏览器类型了

function Testing () {
    var uA = navigator.userAgent;
    if(/Trident\/\d+\.\d+/.test(uA)) {
        return uA.match(/Trident\/\d+\.\d+/);
    }else if(/OPR\/(\d+\.){3}\d+/.test(uA)) {
        return uA.match(/OPR\/(\d+\.){3}\d+/)[0];
    } else if(/Chrome\/(\d+\.){3}\d+/.test(uA)) {
        return uA.match(/Chrome\/(\d+\.){3}\d+/)[0];
    } else if(/Firefox\/\d+\.\d+/.test(uA)) {
        return uA.match(/Firefox\/\d+\.\d+/)[0];
    } else if(/Mac\sOS\sX/.test(uA)) {
        return uA.match(/Safari\/(\d+\.){2}\d+/)[0]
    }
}

location

location则是一个表明当前浏览器地址栏信息的一个对象。

直接alert会默认调用location的href方法,然后打印出当前地址栏上的全部信息

alert(window.location);

但是如果使用console.log则会打印出location对象底下的全部方法和属性

hash: “” search: “”

pathname: “/E:/Html/%E7%A8%8B%E5%BA%8F/%E7%BB%83%E4%B9%A0/JavaScript%E7%BB%83%E4%B9%A0.html”

port: “” hostname: “”

href: “file:///E:/Html/%E7%A8%8B%E5%BA%8F/%E7%BB%83%E4%B9%A0/ JavaScript%E7%BB%83%E4%B9%A0.html”

ancestorOrigins: DOMStringList protocol: “file:” reload: reload() replace: () arguments: null caller: null length: 1 name: “” toString: toString() valueOf: valueOf() proto: Location

主要的属性有这么些

href

打印出当前地址栏目的全部地址

主地址?之后的内容(结果会包含?)

如果需要把?去掉的话可以这样写

console.log(window.location.search.substring(1));

获取这些东西有什么用处呢? 当我们学到前后端交互的时候,就知道有两种方式进行交互getpost 使用get方式进行信息的发送的时候就需要以 网址?name=owen&age=18 这样的形式进行 所以获取?后面的内容是必要的

hash

我们可以使用location的另外一个属性hash来获取地址栏后面#的内容 这个方法也是会带有一个#的

去除的方法上同

console.log(window.location.hash.substring(1));

hash音译哈希,我们可以通过动态的添加哈希值来给每个网页标记索引,或者解决缓存问题 简单来说就是让同一类网页不同的信息进行不同的标记

用户输入交互事件

focus 和 onfocus

当用户需要进行内容的交互的时候,需要标中焦点才可以进行输入 而标中焦点常用的方法有三种 ① 使用鼠标点击 ② 使用Tab切换 ③ Js控制

我们可以使用 focus 来主动为用户标中焦点 例如 如下布局

<input type="text" value = "输入内容" id="text">
var otext = document.getElementById('text');

otext.focus();

当某元素的被标中焦点的时候,我们可以使用onfocus来执行标中焦点事件

var otext = document.getElementById('text');

otext.focus();
otext.onfocus = function() {
	console.log(焦点被标中了);
}

blur 和 onblur

我们还可以使用 blur 来主动取消已被标中的焦点,不过可能不太常用

使用onblur激发焦点被取消时候的callback

var otext = document.getElementById('text');

otext.focus();
otext.onblur = function() {
	console.log(焦点被取消了);
}

结合blurfocus , 我们可以做出一点小效果

var otext = document.getElementById('text');

otext.onfocus = function() {
	if( this.value === '输入内容' )
	{
		this.value = '';
	}
}

otext.onblur = function() {
	if( this.value === '' )
	{
		this.value = '输入内容';
	}
}

demo

select 和 onselect

使用select可以主动选取一个结点对象中的文本内容

<input type="text" value = "输入内容" id="text">
<input type="button" value ="选取内容" id="btn">
var otext = document.getElementById('text');
var obtn  = document.getElementById('btn');

obtn.onclick = function() {
	otext.select();	
}

demo

我们当然,还有一个onselect当我们选中文本内容的时候触发

var otext = document.getElementById('text');
var obtn  = document.getElementById('btn');

obtn.onclick = function() {
	otext.select();	
}

otext.onselect = function() {
		alert("文本被选中了");
}

demo

感谢

妙味课堂

事件流

引用出自

《javascript高级程序设计》

Table of Contents