- DOM
- 事件对象
- 事件的冒泡Bullbe
- 事件的委派
- 事件监听器绑定 addEventListener
- 事件的传播
- 滚轮事件 onmousewheel
- 键盘事件 onkeydown onkeyup
- BOM
- 类的操作
DOM
简介
- DOM:Document Object Model文档对象模型
- JS通过DOM来对HTML文档进行操作,操作WEB页面
- 文档:网页,一个HTML页面
- 对象:网页的每一部分都转换为对象
- 模型:使用模型表示对象之间的关系,方便获取对象
节点和属性
- 网页的最基本组成部分,网页找那个的每一个部分都可以称为是一个节点。比如html标签、属性、文本、注释、整个文档都是一个节点。
- 节点类型不同。
- 文档节点:整个HTML文档
- 元素节点:HTML中的HTML标签
- 属性节点:元素的属性
- 文本节点:HTML标签中的文本内容
- 节点类型不同,属性和方法也不同
/**
* 浏览器已经提供了文档节点,这个对象是window
* 可以在页面中直接使用,文档节点代表的是整个网页
*/
console.log(document);//document
<button id="btn">我是一个按钮</button>
<script>
//获取btn对象
let btn=document.getElementById("btn");
console.log(btn);//button#btn
//修改按钮文字
btn.innerHTML='我是一个哥哥 按钮';
</script>
事件
用户和浏览器的一些交互行为。例如,点击按钮,鼠标移动,关闭窗口。
- 可以在事件对应的属性中设置一些js代码,这样当事件被触发时,这些代码将会执行。
//这种方式不方便使用,结构耦合
<button id="btn" onclick="alert('你点我干嘛')">我是一个按钮</button>
- 为事件绑定函数处理响应,当事件被触发时,对应的函数会被执行
<button id="btn">我是一个按钮</button>
<script>
var btn=document.getElementById('btn');
btn.onclick=function(){
alert('你还点');
};
</script>
文档加载
浏览器加载页面:
- 自上而下,读取一行执行一行
- 如果将script代码写到html页面上边,则在script代码执行时,页面还没有加载,导致页面加载时无法获取到函数。
- 所以将script代码写到html页面下边,确保页面加载完毕以后再执行js代码。
- 如果想要写在页面上边,需要:onload()事件,将在加载完页面之后触发
<script> window.onload=function(){ //获取id为btn的按钮 let btn=document.getElementById('btn'); //为按钮绑定一个单击响应函数 btn.onclick=function(){ alert('hello'); }; }; </script>
DOM查询
获取元素节点
通过document对象调用
1.getElementById() 根据id获取一个元素节点对象
var bj = document.getElementById("bj");
2.getElementsByTagName() 根据标签名获取一组元素节点对象
- 这个方法会给我们返回一个类数组对象,所有查询到的元素都会封装到对象中
- 即使只有一个元素,返回的也是数组
var lis = document.getElementsByTagName("li");
//可以循环打印
3.getElementsByName() 根据name属性获取一组元素节点对象
- innerHTML用于获取元素内部的HTML代码,对于自结束标签,这个属性没有意义
- 读取元素节点属性,元素.属性名
- 读取class属需要使用 元素.className
var inputs = document.getElementsByName("gender");
获取元素节点的子节点
通过具体的元素节点调用
1. getElementsByTagName() 返回当前节点的指定标签名后代节点
//获取city节点
let city=document.getElementById("city");
//获取city下边的 li
let btn04_lis_num=city.getElementsByTagName("li")
for (let i = 0, len = btn04_lis_num.length; i < len; i++) {
console.log(btn04_lis_num[i].innerHTML);
}
2. childNodes 当前节点的所有子节点
- 会获取包括文本节点在内的所有节点
- 会将空白文本当成文本节点
//获取city节点
let city=document.getElementById("city");
// 返回city的所有子节点
let btn05_lis_num=city.childNodes;
3. children 当前节点的所有子元素(即标签)
let btn05_lis_num_1=city.children;
for (let i = 0, len = btn05_lis_num_1.length; i < len; i++) {
console.log(btn05_lis_num_1[i].innerHTML);//北京、上海、东京、首尔
}
4. firstChild 当前节点的第一个子节点
- firstElementChild 第一个子元素 (推荐使用)
5. lastChild 当前节点的最后一个子节点
-
- lastElementChild 最后一个子元素(推荐使用)
//获取phone节点
let phone=document.getElementById("phone");
// 返回phone的第一个子节点
let btn06_lis_num=phone.firstChild;
console.log(btn06_lis_num.innerHTML);
// 返回phone的最后一个子节点
let btn06_lis_num_1=phone.lastChild;
6. parentChild 当前节点的父节点
- 只返回一个父节点
- innetHtml 内部html代码
- inneText 内部文本内容,自动去除html
let bj=document.getElementById("bj");
let bj_parent=bj.parentNode;
console.log(bj_parent.nodeName);
7. previousSibling 前一个兄弟节点
8. nextibling 后一个兄弟节点
let android=document.getElementById("android");
let android_previous=android.previousSibling;
console.log(android_previous.innerHTML);
其他方法
1.body body的引用
let body=document.body;
console.log(body); //<body>...</body>
2.documentElement html根标签
let de=document.documentElement;
console.log(de);
3.document.all 页面中所有的标签
let all=document.all;
//all=document.getElementsByTagName("*");同效
console.log(all);
//HTMLAllCollection(9)[html, head, meta, meta, meta, title, script, body, script, viewport: meta]
3.getElementsByClassName 根据class名查找节点
CSS选择器查询节点
1.querySelector(str) 根据CSS选择器查询节点
- 只会返回一个节点(多个,则返回1个)
//查询类名box1下的所有div
document.querySelector(".box1 div");
1.querySelectorAll(str)
- 返回一组节点
- 封装到数组中返回(即使符合条件的元素只有一个)
document.querySelectorAll(".box1 div");
DOM增删改
1.createElement(str) 创建元素节点
- document调用
2. appendChild() 将新的子节点添加到指定的父节点末尾 - 父节点调用
let gz=document.createElement("li");
gz.innerHTML="广州";
let city=document.getElementById("city");
city.appendChild(gz);
//city.innerHTML += `<li>广州</li>`
3.insertBefore(新节点,旧节点) 在指定的子节点前插入新的子节点
- 父节点调用
let bj=document.getElementById("bj");
city.insertBefore(gz,bj);
3.replaceChild(新节点,旧节点) 使用指定的子节点替换已有的子节点
- 父节点调用
city.replaceChild(gz,bj);
4.removeChild(指定节点) 删除指定子节点
- 父节点调用
city.removeChild(bj);
//bj.remove();
//bj.parentNode.removeChild(bj)
DOM操作CSS内联样式
- 通过style修改的属性都是内联样式(但是如果在样式中写!important,此时修改js失效)
- 如果CSS样式名中有**-**,需要将样式名修改为驼峰 font-style-->fontStyle
- 通过style属性,读取和设置的都是内联样式,无法读取样式表中的样式
元素名.style.property=a;
box1.style.width="300px";
//修改为驼峰
box1.style.backgroundColor="yellow";
DOM操作CSS当前样式
- 当前元素正在显示的样式(CSS和内联,谁生效,读取谁)
- currentStyle只能在 ie 支持
- 如果获取样式的没有设置,会获取到默认样式
- getComputedStyle(元素,可以传递伪元素,一般为null)
- 返回对象,封装元素对应的样式
- 如果获取样式的没有设置,会获取到真实的值,而不是默认样式
- 不支持ie8及一下浏览器
- 以上二者都是只读样式
//只能在 ie8 使用
元素.currentStyle.样式名
console.log(box1.currentStyle.width);
//getComputedStyle(元素名,null) 正常浏览器
getComputedStyle(box1,null).width;
- getStyle(obj,name) 自定义
//获取元素当前样式 处理兼容性问题
function getStyle(obj,propertyName){
if(window.getComputedStyle){
//正常浏览器
//getComputedStyle是全局属性
return getComputedStyle(obj,null)[name];
}
else{
//ie8
return obj.currentStyle[name];
}
}
DOM操作其他样式的相关属性
1. clientWidth 元素可见宽度
2. clientHeight 元素可见高度
- 不带px,返回数字,可以直接计算
- clientWidth=内容区+内边距
- 这些属性都是只读的
#box1{
width: 100px;
height: 100px;
background-color: red;
padding: 10px;
border: 10px solid blue;
}
console.log("clientWidth="+box1.clientWidth);//120
3. offsetwidth offsetHeight 获取元素整个宽度和高度
- offsetwidth=内容区+内边距+边框
console.log("offsetWidth="+box1.offsetWidth);//140
4. offsetParent 获取当前元素的定位父元素
- 获取到离当前元素最近的开启了定位的祖先元素
- 如果找不到则返回body
let op=box1.offsetParent
console.log(op);//div2
5. offsetLeft offsetTop 当前元素相对于其父元素的偏移量
console.log(box1.offsetLeft);
console.log(box1.offsetTop);
6. scrollHeight scrollWidth 整个滚动区域的宽度和高度
<div id="box4">
<div id="box5"></div>
</div>
#box4{
width: 200px;
height: 300px;
background-color: #bfa;
overflow: auto;
}
#box5{
width: 250px;
height: 600px;
background-color: yellow;
}
console.log(box4.scrollHeight);//600
console.log(box4.scrollWidth);//250
7. scrollLeft scrollTop 滚动条滚动的距离
console.log(box4.scrollLeft);//滚动条的水平偏移量
console.log(box4.scrollTop);//滚动条的垂直偏移量
可以判断滚动条是否到底了
当scrollHeight-srollTop=clientHeight说明,垂直滚动条到底了
事件对象
- 当响应函数触发被触发时,浏览器每次都会将事件对象作为实参传递给响应函数
- 事件对象封装了当前对象的一切信息
e就是事件对象
p.onclick=function(e){}
- 在IE8中,不会传递事件对象。而是将事件对象作为window的对象存储的
let x=window.e.clientX;
处理兼容性
if(!event){
event=window.event;
}
event=event || window.event;
1. onmousemove 鼠标移动事件
let areaDiv=document.getElementById("areaDiv");
let showMsg=document.getElementById("showMsg");
areaDiv.onmousemove=function(e){
e=e || window.e;
// 在showMsg显示鼠标的坐标
//浏览器知道鼠标的坐标
showMsg.innerHTML="("+e.clientX+","+e.clientY+")";
}
clientX 获取鼠标在当前可见窗口的坐标
//div是相对于html页面的
//当页面超出浏览器窗口,鼠标位置和div的偏移量就不同了
pageX 鼠标相当于页面的坐标(ie8不支持)
let x=e.pageX;
let y=e.pageY;
// 获取鼠标滚动条的距离
// 谷歌认为浏览器的滚动条是body的
// let st=document.body.scrollTop;
// 火狐等认为浏览器滚动条是html的
// let st1=document.documentElement.scrollTop;
// 处理兼容性
st=document.body.scrollTop || document.documentElement.scrollTop;
事件的冒泡Bullbe
- 事件的向上传导:当后代元素事件被触发时,其祖先相同的事件也会被触发
点击span,触发span的响应函数,
然后触发div的响应函数,
然后触发body的响应函数
<body>
<div id="box1">
我是box1
<span id="s1">我是span</span>
</div>
</body>
<script>
window.onload=function(){
//为s1绑定单击响应函数
let s1=document.getElementById("s1");
s1.onclick=function(){
alert('我是span的单击函数');
}
//为box1绑定单击响应函数
let box1=document.getElementById("box1");
box1.onclick=function(){
alert('我是div的单击函数');
}
//为body绑定单击响应函数
document.body.onclick=function(){
alert('我是body的单击函数');
}
}
</script>
取消冒泡:用事件对象取消冒泡
s1.onclick=function(e){
e.cancelBubble=true;
}
事件的委派
- 将事件统一绑定给共同的祖先元素,这样当后代元素上的事件触发时,会一直冒泡到祖先元素,从而通过祖先元素的响应函数处理事件
- 通过委派,减少绑定的次数,提高性能
//为每个超链接绑定点击事件
//当创建新的超链接时,需要重新绑定响应事件,代码冗余
for (let i = 0, len = allA.length; i < len; i++) {
allA[i].onclick=function(){
}
//将点击事件绑定到共同的祖先元素ul上
u1.onclick=function(){
// 绑定到ul上,这样点击超链接,事件可以冒泡到ul上,也
alert('msg');
}
target 触发事件的对象
问题:绑定到父元素,父元素的整个内容区都会触发事件,效果不好
//判断点击a才会执行触发事件
u1.onclick=function(event){
//判断点击a才会执行触发事件
event=event || window.event;
//target:触发事件的对象
if(event.target.className=='link'){
alert('msg');
}
}
事件监听器绑定 addEventListener
使用对象.事件绑定函数
- 只能同时为一个元素的一个事件绑定给一个响应函数
- 如果绑定多个,后边的会覆盖前边的
事件监听器绑定函数addEventListener()
- 可以绑定多个函数
- 参数
- 事件类型,不要on,例如click
- 回调函数:当事件触发时调用回调函数
- 是否在捕获阶段触发事件,一般为false
- 多个事件按照函数绑定的顺序执行(1,2,3)
- 返回绑定事件的对象
btn01.addEventListener("click",function(){
alert('msg');
},false);
btn01.addEventListener("click",function(){
alert('dddddd');
},false);
- 不支持IE8及以下浏览器
- 使用attachEvent()
- 参数:1.事件类型,要on
- 2.回调函数
- 后绑定,先执行(3,2,1)
- 返回window
btn01.attachEvent("onclick",function(){
alert('dddddd');
});
处理兼容性
function bind(obj,eventStr,callback){
// 判断对象
if(obj.addEventListener){
//正常浏览器
obj.addEventListener(eventStr,callback,false);
}
else{
//IE8及以下
// obj.attachEvent("on"+eventStr,callback);
//this是由调用方式决定的
//修改this为btn01
// callback是浏览器调用的,所以this=window
//修改callback由匿名函数调用
obj.attachEvent("on"+eventStr,function(){
// 在匿名函数中调用回调函数,由自己调用
//然后使用call()方法改变this的指向
callback.call(obj);
});
}
事件的传播
事件的冒泡--->由内向外传播
事件的捕获--->由外向内传播
W3C综合:
- 捕获阶段
- 在捕获阶段时从最外层的祖先元素,向目标元素进行事件的捕获,但默认此时不会触发事
- 目标阶段
- 事件捕获到目标元素,捕获结束
- 冒泡阶段
-事件从目标元素向祖先元素传播,依次触发祖先元素上的事件 - 如果希望在捕获阶段就触发事件,可以将addEventListener的false设置为true
E8及以下浏览器没有捕获
问题:
- 当我们拖拽网页中的内容时,浏览器会默认去搜索
引擎中搜索内容,导致拖拽功能异常
在对应的事件后添加 return false
- 当调用一个元素的setCapture()方法以后,这个元素会将下一次所有的鼠标按下的相关事件捕获到自身身上
只捕获一次,只有IE支持,需要处理兼容性
box1.setCapture && box1.setCapture()
btn01.setCapture();//点击按钮2时执行的也是按钮1
box1.releaseCapture();//取消捕获,同理
滚轮事件 onmousewheel
火狐不支持该事件。
火狐:DOMMouseScroll,并且使用事件监听器绑定
事件监听器取消默认行为:enevt.preventDefault();
s但是IE8不兼容,所以如下:
enevt.preventDefault && enevt.preventDefault()
box1.onmousewheel=function(e){
//判断鼠标滚轮的方向
e=e||window.e;
console.log(e.wheelDelta);//获取鼠标滚轮的方向
//向上滚 正数 向下滚 负数
//火狐不支持wheelDelta 使用e.detail
if(e.wheelDelta>0 || e.detail<0){
// 向上滚
box1.style.height=box1.clientHeight-10+"px";
}else{
// 向下滚
box1.style.height=box1.clientHeight+10+"px";
}
/**
* 当滚轮滚动时,若浏览器有滚动条,则滚动条会随之移动
* 这是浏览器的默认行为,取消 return false
键盘事件 onkeydown onkeyup
- 键盘事件一般绑定到一些可以获取焦点的对象或者document
document.onkeydown=function(){
//按键被按下
}
document.onkeyup=function(){
//按键松开
onkeydown
- 如果按键一直按着,则事件一直被触发
- 当onkeydown连续触发时,第一次和第二季次隔稍微长一些(放置误操作)
- 可以通过keyCode获取按键的ASCII编码
- altKey alt是否被按下 按下返回true
- ctrlKey 是否被按下 按下返回true
- shiftKey shift是否被按下 按下返回true
e=e||window.e;
console.log(e.keyCode+"被按下");
onkeydown
- 不会连续触发
取消事件默认行为 return false
e=e|| window.e;
if(e.keyCode>=48 && e.keyCode<=57){
return false;
}
BOM
DOM:通过js操作网页
BOM:浏览器对象模型
- 通过js操作浏览器
- BOM提供了一组对象,用来完成浏览器的操作
- window
- navigator
- History
- Screen
window
- 代表占整个浏览器的窗口,同时也是网页中的全局对象
- 属性和方法
- setInterval(fun,time):定时调用(每间隔一段时间调用一次)时间:毫秒
- setInterval返回的数字代表计时器的编号
setInterval(function(){ c.innerHTML++; }, 1000);
- setTimeout():延时调用(隔一段时间以后执行,只执行一次)
setTimeout(function(){ console.log('ddd'); },2000);
- clearInterval(计时器数字标识或名称):关闭计时器
- 可以接受任何参数。
若参数有效,则停止计时器;若参数无效,则什么也不做。
- 可以接受任何参数。
let timer=setInterval(function(){ c.innerHTML++; if(c.innerHTML==11){ c.innerHTML(timer); } }, 1000); clearInterval(timer);
- clearTimeout(timer):关闭延时调用
- setInterval(fun,time):定时调用(每间隔一段时间调用一次)时间:毫秒
navigator
- 当前浏览器的信息,通过该对象识别不同的浏览器
- 由于历史原因,navigator对象中的大部分属性都不能帮助识别浏览器。
- 一般使用userAgent来判断浏览器的信息。字符串中包含描述浏览器信息的内容。
- 若userAgent无法判断,还可以浏览器特有的信息来判断。
//正则表达式判断
if(/firefox/i.test(navigator.userAgent)){
console.log("火狐");
}else if(/chrome/i.test(navigator.userAgent)){
console.log("chrome");
}else if(/msie/i.test(navigator.userAgent)){
console.log("IE");
}
//IE11不好判断时
if("ActiveXObject" in window)
{
console.log("IE11");
}else{
console.log("不是IE");
Location
- 代表浏览器的地址栏信息,通过Location可以操作浏览器跳转页面
- 如果直接将location修改为一个完整的路径或相对路径,页面则跳转到该路径,并生成历史记录。
- 属性和方法
- assign():跳转到其他页面。有历史记录
location.assign();
- reload():重新加载当前页面,刷新
location.reload(); location.reload(ture);强制清空缓存,刷新
- replace():使用新的文档替换当前文档。不能生成历史记录
History
- 代表浏览器的历史记录,可以操作浏览器的历史记录
- 由于隐私的原因,不能获取具体的历史记录,只能操作浏览器前后翻页
- 只在当次访问时有效
- 属性和方法
- length:获取到当次访问链接的数量
console.log(history.length);
- back():回退到上一个页面
history.back();
- forward():跳转到下一个页面
history.forward();
- go(num):跳转到指定页面,参数表示向前跳转几个页面
- 正数向前,负数向后
history.go(2);
Screen
- 代表用户屏幕信息,显示器的相关信息
这些BOM对象在浏览器中都是作为window对象的属性保存的,可以window调用,也可以直接使用。
console.log(navigator);
console.log(location);
console.log(history);
console.log(screen);
类的操作
- 浏览器通过style修改样式,每修改一个样式,浏览器就重新渲染一次页面,效率低下
// 不建议大量用次方法
box1.style.width="200px";
box1.style.height="200px";
box1.style.backgroundColor="yellow";
- 仅用一行代码,修改多个样式
- 修改元素的class属性
//不再通过js改变具体的样式,是js和CSS分离 <div id="box1" class="b1"></div> //js 修改box的class属性 box1.className="b2"; box1.className+=" b2"; addClass(box1,"b2")//用函数添加
/**
* 切换
* 有,则添加
* 没有 则删除
*/
function toggleClassName(obj,cn){
let reg=new RegExp("\\b"+cn+"\\b");
if(!hasClass(obj,cn)){
obj.className +=" "+cn;
}else{
removeClassName(obj,cn);
}
}
// 判断是否已经存在指定的class属性值cn
function hasClass(obj,cn){
//使用正则表达式判断
// let reg=/\bb2\b/;//\b单词边界
let reg=new RegExp("\\b"+cn+"\\b");
return reg.test(obj.className);
}