BOM: Browser Object Model
1. location对象:
(1). 什么是: 专门保存浏览器地址栏中正在打开的一个url的信息的对象
(2). 属性: 分段获得url中各个部分的信息
location.href 获得完整的url
location.protocol 获得url中协议部分
location.host 获得url中主机名+端口号
location.hostname 获得主机名
location.port 端口号
location.pathname 可获得从主机名后的/往后的相对路径
location.search 可获得从?往后的查询字符串参数
location.hash 可获得从#往后的锚点地址
(3). 方法:
a. 在当前窗口打开,禁止后退: location.replace("新url")
b. 在当前窗口打开,可后退: location.href="新url"
相当于: window.open("新url", "_self")
c. 也能刷新页面: location.reload();
相当于: history.go(0)
2. navigator:
(1). 什么是: 专门保存浏览器配置信息的对象
(2). 何时: 今后,只要想获得浏览器的配置信息时,都用navigator。
(3). 常用:
a. navigator.userAgent: 获取浏览器的名称、内核、版本号
b. navigator.plugins: 获取浏览器中安装的所有创建信息的列表
navigator.plugins["完整插件名"]!==undefined 说明装了。
c. 示例: 检查浏览器是否安装PDF和Flash插件
<!DOCTYPE HTML>
<html>
<head>
<title>navigator对象常用属性</title>
<meta charset="utf-8" />
</head>
<body>
<script>
console.log(navigator.userAgent);
console.log(navigator.plugins);
if(navigator.plugins["Chrome PDF Viewer"]!==undefined){
//向网页内容中输入一行话
document.write(`<h3>已安装PDF插件,可查看PDF电子书</h3>`)
}else{
document.write(`<h3>未安装PDF插件,<a href="#">点此下载安装</a></h3>`)
}
if(navigator.plugins["Shockwave Flash"]!==undefined){
//向网页内容中输入一行话
document.write(`<h3>已安装Flash插件,可正常播放视频</h3>`)
}else{
document.write(`<h3>未安装Flash插件,<a href="#">点此下载安装</a></h3>`)
}
</script>
</body>
</html>
运行结果:
已安装PDF插件,可查看PDF电子书
未安装Flash插件,点此下载安装
事件:
1. 回顾:
(1). 什么是事件: 浏览器自动触发的或用户手动触发的页面中内容或状态的改变
(2). 什么是事件处理函数: 当事件发生时,我们希望自动调用执行的函数
(3). 什么是事件绑定:
a. 每个元素上都有一批on开头的事件属性
b. 事件绑定就是提前将事件处理函数保存到元素的事件属性上。
c. 结果: 当事件发生时
1). 浏览器先找到元素身上的对应事件属性
2). 自动调用事件属性中保存的事件处理函数
2. 事件绑定: 3种:
(1). 在HTML中手工绑定:
<元素 on事件名="事件处理函数()">
<script>
function 事件处理函数(){ ... }
问题: 麻烦,不便于今后维护
(2). 在js中以赋值方式绑定:
查找触发事件的元素对象
绑定事件: 元素对象.on事件名=function(){ ... this ... }
问题: 如果希望给一个元素的一个事件属性同时绑定多个事件处理函数,就无法实现。后赋值的事件处理函数会把原事件处理函数覆盖。
(3). 在js中以添加事件监听对象的方式绑定:
a. 如何: 2步:
1). 查找触发事件的元素
添加事件监听对象
2). 元素对象.addEventListener("事件名", 事件处理函数)
b. 强调:
1). 添加事件监听时,事件名称不要加on前缀:
其实,DOM标准中事件名本来是没有on的!
比如: click, blur, change
元素对象中为了将事件属性和其它普通属性区分开,便于识别,才在事件属性名前加on。
2). 添加事件监听对象时,事件处理函数结尾不要加()!
因为事件处理函数都不是立刻执行!而是将来某个时间,触发事件时,才自动调用执行。
c. 原理:
1). 浏览器中有一个巨大的事件监听对象队列。
2). addEventListener做了2件事:
i. 新建一个事件监听对象,其中保存三样东西:元素对象、事件名、事件处理函数
ii. 自动将事件监听对象添加到事件监听对象队列中。
3). 重复执行几次addEventListener()就会反复添加几个重复的事件监听对象
4). 将来事件发生时:
i. 浏览器先查找元素对象自己身上的事件属性中保存的事件处理函数,优先自动执行.
ii. 然后浏览器才会去事件监听对象队列中遍历查找所有符合要求(元素相同,事件名也相同)的事件监听对象。找到几个,就触发几个事件处理函数。
d. 移除事件监听对象:
移除 事件监听对象
1). 元素.removeEventListener("事件名", 原事件处理函数)
2). 问题: 如果绑定时使用匿名函数绑定,移除时也使用完全相同的匿名函数定义移除,则无法成功移除!
3). 原因: function底层等于new Function。添加事件监听时的function会创建一个新函数对象。而移除事件监听时的function也会再创建另一个新函数对象。添加和移除时两次function创建的函数对象,肯定地址不同!不是同一个函数,所以,无法移除
4). 解决: 今后只要一个事件监听对象有可能被移除,则绑定时就不要用匿名函数。绑定时就必须用有名称的函数。然后,移除监听对象时,也用函数名移除.
5). 坑: 一旦绑定时,使用有名称的函数添加事件监听,则完全相同的事件监听对象只能添加一个。
6). 解决:
i. 自己用数组保存所有事件处理函数(学有余力,看扩展视频)
ii. jq中才解决
e. 示例: 为发射按钮添加事件监听,可同时发射多种子弹,也可移除子弹
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>...</title>
</head>
<body>
<button id="btnShoot">shoot</button><br>
<button id="btnAward">获得跟踪导弹</button><br>
<button id="btnBreak">失去跟踪导弹</button><br>
<script>
var btnShoot = document.getElementById("btnShoot");
var btnAward = document.getElementById("btnAward");
var btnBreak = document.getElementById("btnBreak");
//开局,btnShoot默认只能发射一种普通子弹
btnShoot.onclick = function () {
console.log(`发射普通子弹......`)
}
var shoot2=function(){
console.log(`发射跟踪导弹=>=>=>`)
}
//希望点btnAward按钮后,
btnAward.onclick=function(){
//为btnShoot多绑定一个事件处理函数,可同时发生两种子弹: 普通子弹和跟踪导弹
btnShoot.addEventListener(
"click",
shoot2
)
}
//希望点击第三个btnBreak按钮后
btnBreak.onclick=function(){
//从btnShoot元素上移除跟踪导弹
btnShoot.removeEventListener(
"click",
shoot2
)
}
</script>
</body>
</html>
运行结果:
*****事件模型:
(1). 问题: 点在内层元素上,也会触发外层元素的单击事件
(2). 原因: 事件模型:
a. 什么是: 全球的浏览器开发者一致认为,点在内层元素上,也相当于点在了外层元素上。所以,就有了事件模型。
b. 包括: 3个阶段:
1). 捕获: 从树根节点开始,到当前触发事件的目标元素结束,由外向内依次遍历并记录各级父元素上是否绑定了事件处理函数。只记录,不执行!
2). 目标触发: 总是优先触发实际点击的目标元素上的事件处理函数。
目标元素(target): 实际点击到的那个元素
3). 冒泡: 又目标元素开始,到树根节点结束,由内向外依次触发各级父元素上绑定的事件处理函数。
事件对象:
(1). 什么是: 当事件发生时,浏览器自动创建的,保存事件相关信息的对象。
(2). 何时: 2种:
a. 想获得事件相关的信息时
b. 想改变事件默认的行为
(3). 如何获得:
a. 事件对象总是作为事件处理函数的第一个实参值,自动传入
b. 所以:
//当事件发生时
//浏览器自动创建事件对象event
↓
元素对象.on事件名=function( e ){ ... }
或
元素对象.addEventListener("事件名",function(e){ ... })
(4). 事件对象的功能:
a. 停止冒泡: e.stopPropagation()
停止 扩展
示例: 点内层元素,也触发父元素上的事件处理函数:
<!DOCTYPE HTML>
<html>
<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>
var d1=document.getElementById("d1");
var d2=document.getElementById("d2");
var d3=document.getElementById("d3");
d1.onclick=function(){
alert("d1疼!");
}
d2.onclick=function(e){
alert("d2疼!");
e.stopPropagation();
}
d3.onclick=function(e){
e.stopPropagation();
alert("d3疼!");
}
</script>
</body>
</html>
运行结果:
b. 利用冒泡/事件委托
1). 问题: 浏览器触发事件采用的是遍历事件监听对象队列形式查找事件监听对象。如果添加的事件监听对象越多,遍历时间越长,事件响应的速度越慢
2). 解决: 优化,今后,应该尽量减少事件绑定的次数。但是,功能不能少!
3). 如何: 事件委托/利用冒泡,3步:
i. 当多个子元素都要绑定相同的事件处理函数时,其实,我们只要只给父元素绑定一个事件处理函数即可!
结果: 所有子元素都可通过冒泡的方式触发父元素上统一的事件处理函数——共用!
问题: 因为事件处理函数绑定在父元素上的,触发也是在父元素上触发的,所以this->父元素了,不再指子元素。再使用this,就无法获得实际点击的子元素了。
ii. 解决: 今后,只要使用事件委托,都要用e.target代替this.
e.target: 是事件对象中专门保存最初点击的那个目标元素的特殊属性。
优点: 不随冒泡而改变!始终如一。
问题: 用户有可能点在其它元素上,而我们不希望其它元素触发事件!
iii. 解决: 事件处理函数中要先验证当前点击的目标元素的特征,是否符合要求。只有目标元素的特征符合要求,才继续执行后续操作。如果目标元素的特征不是想要的,就什么也不做!
强调: 一切目标元素与其他元素不同的特征,都可作为判断依据
.className,.nodeName,.name, ... ...
4). 示例: 使用事件委托实现计算器效果:
<!DOCTYPE HTML>
<html>
<head>
<title>取消与利用冒泡</title>
<meta charset="utf-8" />
</head>
<body>
<div id="keys">
<button>1</button>
<button>2</button>
<button>3</button>
<button>4</button><br>
<button>C</button>
<button>+</button>
<button>-</button>
<button>=</button>
</div>
<textarea id="sc" style="resize:none;width:200px; height:50px;" readonly></textarea>
<script>
//DOM4步
//1. 查找触发事件的元素
//本例中: 因为多个子元素都要触发单击事件,所以,应该用事件委托方式优化。事件只绑定在父元素上一份即可!
var div = document.getElementById("keys");
//2. 绑定事件处理函数
div.onclick = function (e) {
if (e.target.nodeName === "BUTTON") {
// e.target.innerHTML="❀";
//先获得充当显示屏的id为sc的文本域元素
var sc = document.getElementById("sc");
//先判断当前点击的按钮的内容
//复习第一阶段分支结构
switch (e.target.innerHTML) {
case "C"://如果点在C按钮上
//就清空显示屏的内容
sc.value = "";
break;
case "="://否则如果点在=按钮上
//就获得显示屏中的表达式,计算结果
var str = sc.value;
//先尝试计算显示屏中的内容
//复习第一个阶段
try {
//将显示屏的内容交给eval做计算,将结果再替换回显示屏中
sc.value = eval(str);
} catch (err) {//万一执行失败
//将错误提示显示在显示屏上
sc.value = err;
}
break;
//否则如果点在其它普通的按钮上
default:
//就把当前按钮的内容追加到显示屏上
sc.value += e.target.innerHTML;
}
}
}
</script>
</body>
</html>
运行结果:
c. 阻止默认行为:
1). 问题: 有些HTML元素身上带有我们不想要的默认行为。
2). 比如: <a href="#">仅当做按钮用的a</a>
会自动在地址栏url结尾添加#,不是我们想要的
3). 解决: 对于a,有两种方式:
a. <a href="javascript:;">文本</a> 只对a元素有效
1). javascript: href中的javascript意思是不让a跳转到其它页面,而是在当前页面的内容中执行一条js语句。
2). ; 意为是一条什么也不做的空语句
3). javascript:; 合起来表示执行一条空语句,相当于什么也不做。
b. e.preventDefault() 对任意元素都有效
阻止 默认行为
4). 示例: 点a,返回页面顶部,并阻止a修改地址栏中url
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
a{
position:fixed;
right:0;
bottom:150px;
}
</style>
</head>
<body style="height:2000px">
<!--希望:点a可返回顶部,但是不要在地址栏结尾加#top-->
<!-- <a id="a1" href="javascript:;">返回顶部</a>
<script>
var a1=document.getElementById("a1");
a1.onclick=function(){
//让窗口 滚动 到 顶部0位置
window.scrollTo(0,0);
}
</script> -->
<a id="a1" href="#">返回顶部</a>
<script>
var a1=document.getElementById("a1");
a1.onclick=function(e){
//阻止 默认行为
e.preventDefault();
//让窗口 滚动 到 顶部0位置
window.scrollTo(0,0);
}
</script>
</body>
</html>
运行结果:
d. 获取鼠标位置: 3组坐标
1). 鼠标相对于屏幕左侧、上方的距离
e.screenX, e.screenY
2). 鼠标相对于浏览器内部文档显示区左侧、上方的距离
e.clientX, e.clientY
客户端 客户端
3). 鼠标相对于当前点击的HTML元素左侧、上方的距离
e.offsetX, e.offsetY
偏离 偏离
4). 示例: 点击图片,获得鼠标三组坐标位置
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>在当前显示区范围内实现点不到的小方块</title>
<style>
div {
position: fixed;
top:100px;
left:150px;
width: 100px;
height: 100px;
background-image: url(images/xiaoxin.gif);
background-size: 100%;
}
</style>
</head>
<body>
<div id="pop"></div>
<script>
var pop=document.getElementById("pop");
pop.onclick=function(e){
console.log(e.screenX, e.screenY);
console.log(e.clientX, e.clientY);
console.log(e.offsetX, e.offsetY);
}
</script>
</body>
</html>
运行结果:
总结:
BOM:
4. location:
(1). 属性: 分段获得url中各个部分:
a. location.href 完整url
b. location.protocol 协议
c. location.host 主机名+端口号
d. location.hostname 主机名
e. location.port 端口号
f. location.pathname 相对路径
g. location.search ?及其之后的查询字符串参数列表
h. location.hash #锚点地址
(2). 方法:
a. 在当前窗口打开,可后退:
location.assign("新url") 或 location.href="新url"
b. 在当前窗口打开,禁止后退:
location.replace("新url")
c. 刷新: location.reload();
5. navigator
(1). 查看浏览器的名称和版本号: navigator.userAgent
(2). 查看浏览器中安装的插件列表: navigator.plugins
总结: 事件:
1. 绑定事件: js中:
(1). 一个事件只绑定一个处理函数
元素.on事件名=function(){ ... }
(2). 一个事件绑定多个处理函数
元素.addEventListener("事件名", 事件处理函数)
(3). 移除一个事件监听:
元素.removeEventListener("事件名", 原事件处理函数对象)
2. 事件模型: 捕获,目标触发,冒泡
3. 事件对象:
(1). 获得事件对象:
元素.on事件名=function(e){ ... }
(2). 阻止冒泡: e.stopPropagation()
(3). 当多个子元素都要绑定相同事件时,利用冒泡/事件委托3步:
a. 事件只在父元素上绑定一次
b. e.target代替this
c. 判断e.target的任意特征是否是我们想要的元素
(4). 阻止元素默认行为:
e.preventDefault()
(5). 获取鼠标位置:
a. 相对于屏幕左上角的x,y坐标:
e.screenX, e.screenY
b. 相对于文档显示区左上角的x,y用坐标:
e.clientX, e.clientY
c. 相对于事件所在元素左上角的x,y坐标:
e.offsetX e.offsetY