Browser Object Model
- 什么是: 专门操作浏览器软件或访问浏览器软件相关信息的一组对象和函数的统称
- 问题: 没有标准,兼容性极差!
- 何时: 只要希望操作浏览器软件或访问浏览器软件相关信息时,才用BOM。
window对象: 三个角色:
- 代替ES标准中的global充当全局作用域对象
(1). 用var声明的所有全局变量和函数,默认都保存在window中
(2). 保存在window中的变量,在任何位置都可访问! - 负责保存所有DOM和BOM原生的可直接使用的对象和函数定义。
(1). document,其实是window.document
(2). alert() 其实是window.alert() - 代表当前正在打开的这个窗口
(1). 可获得当前窗口的大小: 2组
a. 完整窗口大小: window.outerWidth/outerHeight
b. 文档显示区大小: window.innerWidth/innerHeight
(2). 可监控窗口大小变化: window.οnresize=function(){ … }
示例: 监控窗口大小:
<h1>width:<span id="spWidth">0</span></h1>
<script>
spWidth.innerHTML=`${window.innerWidth}px`;
window.onresize=function(){
spWidth.innerHTML=`${window.innerWidth}px`;
}
</script>
(3). 控制滚动:
a. 当页面滚动时,会自动触发:
window.οnscrοll=function(){
//获得页面滚动过的距离
var scrollTop=document.documentElement.scrollTop
||document.body.scrollTop;
}
示例: 根据滚动位置执行动画效果
<head>
<meta charset="UTF-8">
<title>根据页面滚动位置显示浮动框</title>
<style>
body{height:2000px;}
#toTop{
position:fixed;
background-color:pink;
bottom:100px;
right:-100px;
/* display:none; */
overflow:hidden;
transition:all .5s linear;
}
#toTop.show{
right:0;
}
</style>
</head>
<body>
<div id="toTop">
<a href="javascript:;">返回顶部</a>
</div>
<script>
//当窗口中滚动条滚动时
window.onscroll=function(){
//获得当前网页滚动过的距离:
var scrollTop=
document.documentElement.scrollTop
||document.body.scrollTop;
console.log(scrollTop);
//如果scrollTop>=500
if(scrollTop>=500){
//toTop就显示
toTop.className="show";
}else{//否则
//toTop就隐藏
toTop.className="";
}
}
var a=document.querySelector("#toTop>a");
a.onclick=function(){
window.scrollTo(0,0);
}
</script>
</body>
b. 主动滚动到指定位置:
(1). 滚动到: window.scrollTo(0, 垂直方向滚动到的位置)
(2). 滚动过: window.scrollBy(0, 垂直方向滚动过的距离)
带动画的页面滚动,一定是用scrollBy实现的。而不是scollTo瞬间到达位置。
(4). 打开和关闭窗口:
window.open();
window.close();
示例: 倒计时自动关闭窗口:
<h1 id="h1">5s 后自动关闭</h1>
<a id="a1" href="javascript:;">留在本页</a>
<script>
//用周期性定时器让倒计时递减
var timer=setInterval(function(){
//parseInt和parseFloat除了转整数外,还可去掉数字后,所有非数字字符
var n=parseInt(h1.innerHTML);
n--;
//当n减到0,就自动关闭当前页面
if(n==0){
close();
}else{
h1.innerHTML=`${n}s 后自动关闭`
}
},1000)//每隔1秒
a1.onclick=function(){
clearInterval(timer);
}
</script>
四. 打开新链接共有几种方式: 4种
-
在当前窗口打开,可后退:
(1).HTML:<a href="url" target="_self">
(2).JS: window.open(“url”,"_self"); -
在当前窗口打开,禁止后退:
后退的原理: -
每个窗口的内存中都有一个history对象
-
history对象用来保存当前窗口打开后,成功访问过的url的历史记录的数组。
-
每当当前窗口地址栏中成功访问一个新url,则新url就会被push到history对象中保存
-
当前窗口能否前进后退,取决于:
(1). 当前窗口正在打开的url,处于history中哪个位置
(2). 在当前url位置的前后,是否有其它多余的url
a. 如果当前url下方有旧url历史记录,则可以后退
b. 如果当前url上方有较新的url历史记录,则可以前进
c. 如果当前url上下同时又历史记录,则既可前进,又可后退 -
如何禁止后退:
(1). 不要以push方式追加新url
(2). 可用新的url代替当前位置旧的url(1).JS: location.replace(“新url”)
用当前地址栏中新的url代替history中当前正在看的url -
在新窗口打开,可同时打开多个:
(1).HTML:<a href="url" target="_blank">
(2).JS: window.open(“url”,"_blank"); -
在新窗口打开,只能打开一个:
(1).HTML:<a href="url" target="自定义窗口名">
(2).JS: window.open(“url”,“自定义窗口名”);
原理: -
每个窗口在内存中都有唯一的名字: window.name
强调: 绝对不能用name当我们自己程序的全局变量名 -
浏览器规定,同名的窗口只能开一个!
-
<a>
的target属性和open()的第二个参数都是在为要打开的新窗口起名字!<a>
的target属性值和open()的第二个参数值会自动成为新窗口的window.name名 -
结果: 相同name属性的窗口只能打开一个。如果强行打开第二个name属性的窗口,则也会新打开一个窗口,但是会覆盖旧同名窗口!
-
预定义的窗口名:
(1). _self: 用当前窗口的名字,作为新窗口的名字!
结果: 只要打开新窗口,必然覆盖当前旧窗口
(2). _blank: 不指定窗口名。但是浏览器不会让窗口名空着,会自动随机在内存中生成一个不重复的窗口名(人看不到)。因为随机生成的窗口名和之前的窗口名一定都不一样,所有,可反复打开多个新窗口! -
示例: 分别实现四种打开新链接的效果:
<head>
<meta charset="utf-8"/>
<title>打开新链接方式总结</title>
<script>
/*打开新链接方式总结:
1. 在当前窗口打开,可后退
2. 在当前窗口打开,不可后退
3. 在新窗口打开,可打开多个
4. 在新窗口打开,只能打开一个
*/
function open1(){
window.open("http://tmooc.cn","_self");
}
function open2(){
location.replace("http://tmooc.cn");
}
function open3(){
window.open("http://tmooc.cn","_blank");
}
function open4(){
window.open("http://tmooc.cn","tmooc");
}
</script>
</head>
<body>
<a href="http://tmooc.cn" target="_self">1. 在当前窗口打开,可后退</a><br/>
<a href="javascript:open1()">1. 在当前窗口打开,可后退(js)</a><br/>
<hr/>
<a href="javascript:open2()">2. 在当前窗口打开,不可后退(js)</a><br/>
<hr/>
<a href="http://tmooc.cn" target="_blank">3. 在新窗口打开,可打开多个</a><br/>
<a href="javascript:open3()">3. 在新窗口打开,可打开多个(js)</a><br/>
<hr/>
<a href="http://tmooc.cn" target="tmooc">4. 在新窗口打开,只能打开一个</a><br/>
<a href="javascript:open4()">4. 在新窗口打开,只能打开一个(js)</a><br/>
</body>
五. history对象:
- 什么是: history对象用来保存当前窗口打开后,成功访问过的url的历史记录的数组。
- 只开放了前进,后退,刷新:
(1). history.go(1) 前进一步
(2). history.go(-1) 后退一步
有些页面后退一次可能不管用,需要后退两次
history.go(-2)
(3). history.go(0) 刷新
六. location对象:
- 什么是: 专门保存当前窗口正在打开的url的信息的对象,并提供了页面跳转和刷新的函数
- 何时: 只要想获得当前页面正在打开的url信息时,或想执行一些跳转操作时都用location对象
- 属性: 可分段获得当前url的各个组成部分
location.href 获得完整的url
location.protocol 获得协议
location.host 主机名+端口号
location.hostname 主机名
location.port 端口号
location.pathname 相对路径
location.search ?后的查询字符串
location.hash #锚点地址 - 方法:
(1). 也可以实现在当前窗口打开,可后退:
location.href=“新url”
location.assign(“新url”)
location=“新url”
(2). 也可以实现在当前窗口打开,禁止后退:
location.replace(“新url”)
(3). 也可以刷新页面:
location.reload()
七. Navigator对象:
- 什么是: 专门保存浏览器配置信息的对象
- 何时: 只要想获得浏览器的配置信息,都用navigator对象
- 判断浏览器是否安装了某个插件
(1). 浏览器安装的所有插件的信息,都记录在navigator的plugins数组中。
(2). 可通过用插件全名强行访问plugins数组中的元素,来判断当前浏览器是否安装某个插件:
navigator.plugins[“完整插件名”]!==undefined 说明已经装了!
===undefined,说明没装!
(3). 示例: 判断浏览器是否安装某个插件
var hasPDF=navigator.plugins["Chrome PDF Viewer"]!==undefined;
//如果判断flash,只需要把插件名换成: "Shockwave Flash"
if(hasPDF==true){
document.write(`<h1>已安装PDF插件,可浏览PDF文档</h1>`)
}else{
document.write(`<h1>未安装PDF插件,<a href="javascript:;">点此下载安装</a>`)
}
- 获得浏览器的名称和版本号: navigator.userAgent
事件
-
什么是事件: 浏览器自动触发的或用户手动触发的页面内容或状态的改变
-
什么是事件处理函数: 每当事件发生时,我们希望自动执行的函数
-
什么是事件绑定: 在事件发生前,就提前将事件绑定到指定元素的事件属性上。
-
事件绑定的结果: 一旦浏览器中某个元素上发生事件,则浏览器会自动找到这个元素上绑定的事件处理函数,自动执行!
-
如何事件绑定: 3种:
(1). 在HTML中绑定:
Html中: <元素 on事件名=“函数名()”>
Js中: function 函数名(){
…
}
问题: js语句会分散在HTML中各个元素上,不符合内容与行为分离的原则!不便于维护!
(2). 在js中用赋值方式绑定:
Js中: 元素对象.on事件名=function(){
… …
}
问题: 一个元素的一个事件,只能绑定一个处理函数
(3). 在js中用添加事件监听对象的方式来绑定事件:
Js中: 元素对象.addEventListener(“事件名”,处理函数对象)
addEventListener原理: -
每调用一次addEventListener,就创建一个事件监听对象
-
事件监听对象中包含三部分内容:
元素对象
事件名
处理函数对象 -
addEventListener会将这个事件监听对象添加到浏览器的事件队列中。
-
反复执行了几次addEventListener,就会向浏览器的事件监听对象队列中反复添加几个事件监听对象
-
当某个元素触发事件时:
(1). 浏览器先执行当前元素对象的事件属性上绑定的处理函数。
(2). 然后浏览器会去事件队列中查找是否包含与当前元素和当前事件名匹配的事件监听对象。如果找到匹配的事件监听对象,就自动执行事件监听对象内提前封装好的事件处理函数
(3). 浏览器在事件监听对象队列中找到几个和当前元素和当前事件名匹配的事件监听对象,就调用几个处理函数执行。
所以,addEventListener(),可给一个元素的一个事件,绑定多个事件处理函数. -
移除事件监听:
(1). 移除onclick绑定的事件处理函数: 元素.οnclick=null;
(2). 移除addEventListener()绑定的事件处理函数:
a. 元素.removeEventListener(“事件名”,原处理函数名)
b. 强调:原处理函数只能通过函数名来获得原函数对象的地址。所以,如果一个处理函数有可能被移除,在绑定时,就要用有名称的函数!而不能用匿名函数!匿名函数用过一次后!就找不到了!
(3). 问题: 如果绑定时使用有名称的函数绑定,则同名的处理函数,在一个元素的一个事件上只能绑定一次!
因为事件监听队列规定: 相同元素对象,相同事件名,相同处理函数的事件监听对象,只能创建一个!不能反复创建完全相同的两个事件监听对象。 -
DOM事件模型: 3个阶段
(1). 捕获: 从根元素开始,由外向内,遍历当前点中的元素的所有父级元素。记录各级父元素上绑定的处理函数。只记录,不执行!
(2). 目标触发: 优先触发目标元素上的处理函数。
目标元素: 我们本意想点中的那个元素
(3). 冒泡: 由内向外,依次触发各级父元素上的事件处理函数
为什么: 全球所有浏览器的开发者都一致公认: 点在子元素,也等效于点在父元素上了!
强调: 即使不绑定事件处理函数,事件模型也会在点击时触发,只不过,一个处理函数都没找到,什么也不做而已! -
事件对象:
(1). 什么是: 当事件发生时,浏览器自动创建的,保存事件信息的对象
(2). 何时:
a. 获得事件相关的信息!
b. 修改事件默认的行为!
(3). 如何获得:
a. 无需手动创建!
b. 事件对象总是作为事件处理函数的第一个实参值默认传入
c. 元素.on事件名=function(e){
//形参e-> 事件对象event
}
(4). 事件对象可以:
a. 取消冒泡: e.stopPropagation();
停止 蔓延
强调: 只阻止父元素事件继续执行,不阻止自己的事件执行!
示例: 冒泡和取消冒泡
<head>
<title>事件处理</title>
<meta charset="utf-8"/>
<style>
#d1 #d2 #d3{cursor:pointer}
#d1 {
background-color: green;
position: relative;
width: 150px;
height: 150px;
text-align: center;
cursor: pointer;
}
#d2 {
background-color: blue;
position: absolute;
top: 25px;
left: 25px;
width: 100px;
height: 100px;
}
#d3 {
background-color: red;
position: absolute;
top: 25px;
left: 25px;
width: 50px;
height: 50px;
line-height: 50px;
}
</style>
</head>
<body>
<div id="d1">
<div id="d2">
<div id="d3">
</div>
</div>
</div>
<script>
d1.onclick=function(){
alert(`${this.id}疼!`)
}
d2.onclick=function(e){
e.stopPropagation();
alert(`${this.id}疼!`)
}
d3.onclick=function(e){
e.stopPropagation();
alert(`${this.id}疼!`)
}
</script>
</body>
b. 事件委托/代理/利用冒泡: event delegate
1). 优化: 尽量减少事件监听对象的个数!
2). 为什么: 浏览器触发事件时,通过遍历事件监听队列的方式,查找到符合条件的事件监听对象,才执行其中的处理函数。所以,事件监听对象的个数,决定了遍历的效率!事件监听对象越少,遍历越快!事件响应就越快!
3). 何时: 当多个平级子元素都要绑定相同的事件时,必须用事件委托!
4). 如何:
i. 事件处理函数只在父元素上统一绑定一次即可!
结果: 所有子元素都可通过冒泡,触发父元素上的事件
问题1: 想获得当前触发事件的子元素,不能用this了!
因为: 事件绑定在了父元素上,将来也是冒泡到父元素上触发,所以this->父元素!
ii. 想获得当前触发事件的子元素,用e.target代替this
e.target : 事件对象中始终保存目标元素的属性
优点: 不随冒泡而改变!
问题2: 父元素中出了想触发事件的主角元素之外,可能还有很多装饰性的元素。这些装饰性的元素因为也在父元素,所以,也会触发事件。这是我们不希望的。我们只希望个别主角元素才能触发事件!
iii. 判断目标元素的特征:
可以判断 目标元素的元素名: 元素.nodeName
元素.nodeName返回元素全大写的标签名
比如:
btn.nodeName->“BUTTON”
可以判断 class: 元素.className
可以判断任何这个元素与别的元素不同的特征
5). 示例: 计算器效果:
<div id="keys">
<button>1</button><span>*</span>
<button>2</button><span>*</span>
<button>3</button><span>*</span>
<button>4</button><span>*</span><br>
<button>C</button><span>*</span>
<button>+</button><span>*</span>
<button>-</button><span>*</span>
<button>=</button><span>*</span>
</div>
<textarea id="sc" style="resize:none;width:200px; height:50px;" readonly></textarea>
<script>
//1. 只在父元素上统一绑定一次事件处理函数
keys.onclick=function(e){
//alert("疼!");
//2. 用e.target代替this
//3. 只允许按钮触发事件
if(e.target.nodeName=="BUTTON"){
//e.target.style.backgroundColor="red";
/*接下来,计算器的正常流程...*/
//判断e.target的内容
//当所有条件都是等于比较时,首先switch case
switch(btn.innerHTML){
case "C"://如果是C
//就清除显示屏sc的内容
sc.value="";
break;
case "="://如果是=
//如果显示屏sc的内容不为空
if(sc.value!==""){
try{//才尝试
//将显示屏sc的内容放入全局函数eval()中计算得到计算结果
//再将结果放回显示屏sc中
sc.value=eval(sc.value);
}catch(err){//一旦发生错误,就捕获错误
//并将错误信息显示到显示屏sc中
sc.value=err;
}
}
break;
default://其余按钮
//将e.target的内容追加到显示屏sc的内容末尾
sc.value+=btn.innerHTML;
}
}
}
</script>
6). 示例: 使用事件委托优化表格中删除行案例
<div id="data">
<table>
<thead>
<tr>
<th>ename</th>
<th>salary</th>
<th>age</th>
<th>删除</th>
</tr>
</thead>
</table>
</div>
<script>
var json=[
{"ename":"Tom", "salary":11000, "age":25},
{"ename":"John", "salary":13000, "age":28},
{"ename":"Mary", "salary":12000, "age":25}
];
//最后再查找table: id为data下的直接子元素table
var table=document.querySelector(
"#data>table"
);
//1. 创建<tbody>,但是,暂不追加到table下
var tbody=document.createElement("tbody");
//2. 遍历json数组中每个员工对象
for(var emp of json){
//3. 每遍历一个员工对象就创建一个<tr>,追到<tbody>下
// var tr=document.createElement("tr");
// tbody.appendChild(tr);
var tr=tbody.insertRow();
//4. 遍历当前员工对象中每个属性
for(var key in emp){
//5. 每遍历一个员工对象的属性,就创建一个<td>,设置当前员工的当前属性值为td的内容,并将td追加到<tr>下
// var td=document.createElement("td")
// tr.appendChild(td);
var td=tr.insertCell();
td.innerHTML=emp[key];
}
//在这一行中所有内容格添加完之后,再多添加一个td
var td=tr.insertCell();
//创建一个按钮,放入td中
var btn=document.createElement("button")
btn.innerHTML="x";
td.appendChild(btn);
//不要在for循环里为每个删除按钮添加单击事件。
}
//利用事件委托优化表格中删除行操作
//1. 因为每行的按钮都能点击,所以事件应该统一绑定在table上一份即可!
table.onclick=function(e){
//2. 将所有this替换为e.target
//3. 只允许button元素触发事件
if(e.target.nodeName=="BUTTON"){
//以下代码保持不变
//删除按钮所在行:
//问题:如何知道当前按钮在第几行?
//解决:通过当前按钮找到它所在的行对象
var tr=e.target.parentNode.parentNode;
//td tr
//获得当前行中第1个格的内容中的员工名
var ename=tr.cells[0].innerHTML;
//在删除之前,先向用户确认,是否继续删除
var bool=confirm(`是否继续删除 ${ename} 吗?`);
//如果用户同意删除,才执行删除操作
if(bool==true){
//今后删除行都用table来删除,传入tr.rowIndex作为参数
table.deleteRow(tr.rowIndex);
}//否则,什么也不做
}
}
//最后再将包含tr的tbody追加到table中
table.appendChild(tbody);
c. 阻止元素的默认行为:
1). 个别元素身上带着默认的行为:
i. <a href="#top">
会擅自修改地址栏,在结尾加#top
ii. <input type="submit">
会自动提交表单
2). 如果不希望元素执行它身上带的默认行为:
e.preventDefault();
3). 示例: 阻止a元素擅自修改地址栏
<head>
<meta charset="UTF-8">
<title>根据页面滚动位置显示浮动框</title>
<style>
body{height:2000px;}
#toTop{
position:fixed;
background-color:pink;
bottom:100px;
right:-100px;
/* display:none; */
overflow:hidden;
transition:all .5s linear;
}
#toTop.show{
right:0;
}
</style>
</head>
<body>
<div id="toTop">
<a href="#">返回顶部</a>
</div>
<script>
//当窗口中滚动条滚动时
window.onscroll=function(){
//获得当前网页滚动过的距离:
var scrollTop=
document.documentElement.scrollTop
||document.body.scrollTop;
console.log(scrollTop);
//如果scrollTop>=500
if(scrollTop>=500){
//toTop就显示
toTop.className="show";
}else{//否则
//toTop就隐藏
toTop.className="";
}
}
var a=document.querySelector("#toTop>a");
a.onclick=function(e){
e.preventDefault();
window.scrollTo(0,0);
}
</script>
</body>
d. 获得鼠标位置: 在事件对象中包含着三组鼠标位置:
1). 相对于屏幕左上角的距离
2). 相对于文档显示区左上角的距离
3). 相对于当前点击的元素左上角的距离
示例: 点击元素获得鼠标的三组坐标:
<head>
<meta charset="UTF-8">
<title>在当前显示区范围内实现点不到的小方块</title>
<style>
div{position:fixed;width:100px;height:100px;
background-image:url(images/xiaoxin.gif);
background-size:100%;
}
</style>
</head>
<body>
<div id="pop" style="left:200px; top:100px;"></div>
<script>
pop.onclick=function(e){
console.log(`距离屏幕左上角:${e.screenX}, ${e.screenY}`);
console.log(`距离文档显示区左上角: ${e.clientX},${e.clientY}`);
console.log(`距离当前div左上角: ${e.offsetX},${e.offsetY}`);
}
</script>
总结
事件相关:
-
事件绑定:
(1). 一个元素的一个事件只绑定一个处理函数:
元素.on事件名=function(){ … }
(2). 一个元素的一个事件想绑定多个处理函数:
元素.addEventListener(“事件名”,事件处理函数)
(3). 移除事件绑定:
元素.removeEventListener(“事件名”, 原事件处理函数名) -
事件模型: 捕获, 目标触发, 冒泡
-
事件对象:
(1). 取消冒泡: e.stopPropagation()
(2). 获得目标元素: e.target
(3). 阻止默认行为: e.preventDefault()
(4). 获得事件坐标:
a. e.screenX e.screenY
b. e.clientX e.clientY
c. e.offsetX e.offsetY -
窗口大小改变事件:
window.οnresize=function(){}
-
页面滚动事件:
(1). window.οnscrοll=function(){
var scrollTop=document.documentElement.scrollTop
||document.body.scrollTop
}
(2). window.scrollTo(0, 滚动到的位置)
window.scrollBy(0, 滚动过的距离)