本文已参与「新人创作礼」活动,一起开启掘金创作之路。
12 DOM
DOM是JavaScript操作网页的接口,全称为“文档对象模型”(Document Object Model)。它的作用是将网
页转为一个JavaScript对象,从而可以用脚本进行各种操作(比如对元素增删内容)I
浏览器会根据DOM模型,将结构化文档HTML解析成一系列的节点,再由这些节点组成一个树状结构(DOM
Tree)。所有的节点和最终的树状结构,都有规范的对外接口
DOM只是一个接口规范,可以用各种语言实现。所以严格地说,DOM不是JavaScript 语法的一部分,但是
DOM操作是JavaScript最常见的任务,离开了DOM,JavaScript就无法控制网页。另一方面,JavaScript也
是最常用于DOM操作的语言
12.1节点
DOM的最小组成单位叫做节点(node)。文档的树形结构(DOM树),就是由各种不同类型的节点组成。
每个节点可以看作是文档树的一片叶子
节点的类型有7种:
·Document:整个文档树的顶层节点
·DocumentType:doctype标签
·Element:网页的各种HTML标签
·Attribute:网页元素的属性(比如class="right")
·Text:标签之间或标签包含的文本
·Comment:注释
·DocumentFragment:文档的片段
节点树:
一个文档的所有节点,按照所在的层级,可以抽象成一种树状结构。这种树状结构就是DOM树。它有一个顶层节点,下一层都是顶层节点的子节点,然后子节点又有自己的子节点,就这样层层衍生出一个金字塔结构,倒过来就像一棵树
浏览器原生提供document节点,代表整个文档
除了根节点,其他节点都有三种层级关系:
父节点关系(parentNode):直接的那个上级节点
子节点关系(childNodes):直接的下级节点
同级节点关系(sibling):拥有同一个父节点的节点
Node.node Type属性
不同节点的nodeType属性值和对应的常量如下
文档节点(document):9,对应常量Node.DOCUMENT_NODE
元素节点(element):1,对应常量Node.ELEMENT_NODE
属性节点(attr):2,对应常量Node.ATTRIBUTE_NODE
文本节点(text):3,对应常量Node.TEXT_NODE
文档片断节点(DocumentFragment):11,对应常量Node.DOCUMENT_FRAGMENT_NODE
document.nodeType//9
document.nodeType === Node.DOCUMENT_NODE//true
12.2document对象/获取元素
12.2.1 document getElementsByTagName()
document.getElementsByTagName方法搜索HTML标签名,返回符合条件的元素。它的返回值是一个类似数组对象(HTMLCollection实例),可以实时反映HTML文档的变化。如果没有任何匹配的元素,就返回一个空集
var paras=document. getElementsByTagName('p')[0];//以数组的方式读取
如果传入*,就可以返回文档中所有HTML元素
var allElements=document. getElementsByTagName('*");
12.2.2 document.getElementsByClassName()
document.getElementsByClassName 方法返回一个类似数组的对象(HTMLCollection实例),包括了所有class名字符合指定条件的元素,元素的变化实时反映在返回结果中
var elements=document. getElementsByClassName(names)[0];
由于class是保留字,所以JavaScript一律使用className 表示CSS的class参数可以是多个class,它们之间使用空格分隔
var elements=document. getElementsByClassName(' foo bar');
12.2.3document.getElementsByName()
不常用
document.getElementsByName 方法用于选择拥有name 属性的HTML元素(比如
、、//表单为<form name="itbaizhan"></form>
var forms=document.getElementsByName('itbaizhan');
12.2.4document.getElementByld()
document.getElementByld 方法返回匹配指定id属性的元素节点。如果没有发现匹配的节点,则返回null
var elem=document. getElementById('paral');
注意,该方法的参数是大小写敏感的。比如,如果某个节点的id属性是main,那么document.getElementByld("Main)将返回null
12.2.5document.querySelector()
document.querySelector方法接受一个CSS选择器作为参数,返回匹配该选择器的元素节点。如果有多个节点满足匹配条件,则返回第一个匹配的节点。如果没有发现匹配的节点,则返回null
var el1=document. queryselector('. myclass');
12.2.6document.querySelectorAll()
document.querySelectorAll 方法与querySelector 用法类似,区别是返回一个NodeList对象,包含所有匹配给定选择器的节点
var elementList=document. queryselectorA('. myclass');
12.3document对象/创建元素
12.3.1document.createElement()
document.createElement 方法用来生成元素节点,并返回该节点
var newDiv=document. createElement('div');
12.3.2document.createTextNode()
document.createTextNode方法用来生成文本节点(Text实例),并返回该节点。它的参数是文本节点的内容
var newDiv=document. createElement(' div');
var newcontent=document. createTextNode(' Hello');
newDiv. appendchild(newContent);//appendChild:将内容或者子元素放到元素中
12.3.3document.createAttribute()
document.createAttribute 方法生成一个新的属性节点(Attr实例),并返回它
var attribute=document. createAttribute(name);
var root=document. getElementById(' root');
var it=document. createAttribute(' itbaizhan');
it. value=' it';
root. setAttributeNode(it);//添加属性用setAttributeNode
12.4Element对象_属性
Element对象对应网页的HTML元素。每一个HTML元素,在DOM树上都会转化成一个Element节点对象
(以下简称元素节点)
12.4.1Element.id
Elementid 属性返回指定元素的id属性,该属性可读写
//HTML代码为<pid="foo">
var p=document.queryselector('p');
p.id//"foo"
12.4.2Element.className
className属性用来读写当前元素节点的class属性。它的值是一个字符串,每个class之间用空格分割
//HTML 代码<div class="one two three"id="myDiv"></div>
var div=document.getElementById('myDiv');
div.className
12.4.3Element.classList
clasList对象有下列方法
·add():增加一个class。
·remove():移除一个class。
·contains():检查当前元素是否包含某个class。
·toggle():将某个class移入或移出当前元素。
var div=document.getElementById('myDiv');
div.classList.add('mycssclass');
div.classList.add('foo','bar');
div.classList.remove('mycssclass');
div.classList.toggle('mycssclass');//如果mycssclass不存在就加入,否则移除
div.classList.contains('mycssclass');//返回true 或者false
12.4.4Element.innerHTML
Element.innerHTML 属性返回一个字符串,等同于该元素包含的所有HTML代码。该属性可读写,常用来设置某个节点的内容。它能改写所有元素节点的内容,包括和元素
el.innerHTML='';//读取内容
el.innerHTML='hello';//修改内容
12.4.5Element.innerText
immerText 和innerHTML类似,不同的是innerText无法识别元素,会直接渲染成字符串
12.5Element获取元素位置
12.5.1Element.clientHeight,Element.clientWidth
ElementclientHeight 属性返回一个整数值,表示元素节点的CSS高度(单位像素),只对块级元素生效,对于行内元素返回o。如果块级元素没有设置CSS高度,则返回实际高度
除了元素本身的高度,它还包括padding部分,但是不包括border、margin。如果有水平滚动条,还要减去水平滚动条的高度。注意,这个值始终是整数,如果是小数会被四舍五入。
Element.clientWidth属性返回元素节点的CSS宽度,同样只对块级元素有效,也是只包括元素本身的宽度和padding,如果有垂直滚动条,还要减去垂直滚动条的宽度。
document.documentElement的clientHeight属性,返回当前视口的高度(即浏览器窗口的高度)。document.body的高度则是网页的实际高度。
//视口高度(屏幕高度)
document.documentElement.clientHeight
//网页总高度(内容撑开的高度)
document.body.clientHeight
12.5.2Element.scrollHeight,Element.scrollWidth
Element.scrollHeight 属性返回一个整数值(小数会四舍五入),表示当前元素的总高度(单位像素),它包括padding,但是不包括border、margin以及水平滚动条的高度(如果有水平滚动条的话)
Elementscrollwidth 属性表示当前元素的总宽度(单位像素),其他地方都与scrollHeight属性类似。这两个属性只读整张网页的总高度可以从document.documentElement 或documentbody上读取
//返回网页的总高度
document.documentElement.scrollHeight
document.body.scrollHeight
12.5.3Element.scrollLeft,Element.scrollTop
Element.scrollLeft属性表示当前元素的水平滚动条向右侧滚动的像素数量,Element.scrollTop属性表示当前元素的垂直滚动条向下滚动的像素数量。对于那些没有滚动条的网页元素,这两个属性总是等于0
如果要查看整张网页的水平的和垂直的滚动距离,要从document.documentElement元素上读取
document.documentElement.scrollLeft
document.documentElement.scrollTop
12.5.4Element.offsetHeight,Element.offsetWidth
Element.offsetHeig ht 属性返回一个整数,表示元素的CSS垂直高度(单位像素),包括元素本身的高度、padding和border,以及水平滚动条的高度(如果存在滚动条)。
Element.ofsetwidth 属性表示元素的CSS水平宽度(单位像素),其他都与Element.offsetHeight一致。
12.5.5offsetLeft,offsetTop
ps:父级必须要有定位,否则会一直向上找有定位的父级直到body
13设置css样式的三种方法
1:利用setAttribute
<div class="box"id="box"></div>
<script>
var box=document. getElementById("box")
//box. setAttribute("style","width:20epx; height:2eepx; background: red;")
</script>
2:利用.style(常用)
box. style. width ="300px";
box. style. height ="300px";
box. style. backgroundColor="red";
3:利用style.cssText
box.style.cssText ="width:200px;height:200px;background:red;"
14事件
14.1事件处理程序:
HTML事件处理
<body>
<button onclick="clickhandle()">按钮</button>
<script>
//HTML事件缺点:HTML和JS没有分开
function clickhandle(){
console.log("点击了按钮")
}
</script>
</body>
DOM0级事件处理(比较常用)
<body>
<button id = "btn">按钮</button>
<script>
//DOM0事件优点:HTML和js分离 缺点无法同时添加多个事件,会被覆盖
var btn = document.getElementById("btn")
btn.onclick = function(){
console.log('点击了')
}
</script>
</body>
DOM2级事件处理
<body>
<button id = "btn">按钮</button>
<script>
// 优点:事件不会被覆盖 缺点:写起来麻烦
var btn = document.getElementById('btn' );
btn.addEventListener("click",function(){
console.log("点击了");
})
</script>
</body>
14.2鼠标事件
鼠标事件指与鼠标相关的事件,具体的事件主要有以下一些
写代码时记得在前面加on
click:按下鼠标时触发
dblclick:在同一个元素上双击鼠标时触发
mousedown:按下鼠标键时触发
mouseup:释放按下的鼠标键时触发
mousemove:当鼠标在节点内部移动时触发。当鼠标持续移动时,该事件会连触发。
mouseenter:鼠标进入一个节点时触发,进入子节点不会触发这个事件
mouseleave:鼠标离开一个节点时触发,离开父节点不会触发这个事件
mouseover:鼠标进入一个节点时触发,进入子节点会再一次触发这个事件
mouseout:鼠标离开一个节点时触发,离开父节点也会触发这个事件
wheel:滚动鼠标的滚轮时触发
14.3Event事件对象
事件发生以后,会产生一个事件对象,作为参数传给监听函数。
14.3.1Event对象属性:
\
Event.Target:返回事件所在节点
Event.type:返回事件类型
<body>
<div>
<button type="button" id="btn">按钮</button>
</div>
<script>
var btn = document.getElementById("btn");
btn.onclick = function(event){
console.log(event.target);
event.target.innerHTML = "点击之后"
console.log(event.type);
}
</script>
</body>
14.3.2Event对象方法:
Event.preventDefault():
Event.preventDefault Event.preventDefault方法取消浏览器对当前事件的默认行为。
比如点击链接后,浏览器默认会跳转到另一个页面,使用这个方法以后,就不会跳转了
Event.stopPropagation():
stopPropagation方法阻止事件在DOM中继续传播,防止再触发定义在别的节点上的监听函数,
但是不包括在当前节点上其他的事件监听函数
14.4键盘事件
键盘事件由用户击打键盘触发,主要有keydown、keypress、keyup三个事件
keydown:按下键盘时触发。
keypress:按下有值的键时触发,即按下Ctrl、Alt、Shift、Meta这样无值的键,这个事件不会触发。对于有值的键,按下时先触发keydown事件,再触发这个事件。
keyup:松开键盘时触发该事件
<input type="text" id="username">
<script>
var username = document.getElementById("username");
username.onkeydown = function(e){
console.log("按下了");
username.onkeyup = function(e){
console.log(e.target.value);//value属性就是输入框的数据
}
</script>
keyCode:每个按键的唯一标识
var username=document.getElementById("username");
username.onkeydown =function(e){
if(e.keycode === 13){
console.log("开始搜索");
}
}
14.5表单事件
表单事件是在使用表单元素及输入框元素可以监听的一系列事件
常用的事件有:
input事件
select事件
Change事件
reset事件
submit事件
14.5.1input事件
input事件当、、的值发生变化时触发。对于复选框()或单选框(),用户改变选项时,也会触发这个事件
input事件的一个特点,就是会连续触发,比如用户每按下一次按键,就会触发一次input事件。
<script>
var username = document.getElementById("username");
username.oninput = function(e){
console.log(e.target.value);//读取数据
</script>
14.5.2select事件
select事件当在、里面选中文本时触发
14.5.3Change事件
Change事件当、<selects、stextarea>的值发生变化时触发。它与input事件的最大不同,就是不会连续触发,只有当全部修改完成时才会触发
触发条件:失去焦点或者回车
var email = document. getElementById("email");
email. onchange = function(e){
console. log(e.target.value);
}
14.5.4reset事件,submit事件
这两个事件发生在表单对象上,而不是发生在表单的成员上。
reset事件当表单重置(所有表单成员变回默认值)时触发。
submit事件当表单数据向服务器提交时触发。注意,submit事件的发生对象是元素,而不是元素,因为提交的是表单,而不是按钮
ps:form里面的按钮type默认是submit,点击之后页面会刷新,如果有onsubmit事件的话会先执行完submit事件再刷新页面,一般会调用preventDefault()事件将页面刷新关闭掉
<form action="" onsubmit="submitHandle(event)">
<input type="submit" value="button" />
</form>
<script>
function submitHandle(event){
console.log("想做的事");
event.preventDefault()
}
</script>
14.6定时器事件setTimeout
JavaScript 提供定时执行代码的功能,叫做定时器(timer),主要由 setTimeout()和setnterval()这两个函数来完成。它们向任务队列添加定时任务
setTimeout 函数用来指定某个函数或某段代码,在多少毫秒之后执行。它返回一个整数,表示定时器的编号,以后可以用来取消这个定时器。
var timerId = setTimeout(func|code,delay);
setTimeout函数接受两个参数,第一个参数unclcode是将要推迟执行的函数名或者一段代码,第二个参数delay是推迟执行的毫秒数
setTimeout(function(){
console.log("定时器")
},1000)
还有一个需要注意的地方,如果回调函数是对象的方法,那么setTimeout 使得方法内部的this关键字指向全局环境,而不是定义时所在的那个对象
var name="sxt";
var user={
name:"itbaizhan",
getName: function(){
setTimeout(function(){
console.log(this. name);},1000)
};
user. getName();
//打印结果name是sxt
解决方法:
声明一个变量that,将this指针赋给that,然后定时器指针指向that,如:
var name="sxt";
var user={
name:"itbaizhan",
getName: function(){
setTimeout(function(){
var that = this
console.log(that. name);},1000)
};
user. getName();
//打印结果name是sxt
定时器取消:clearTimer(timer)
var timer=setTimeout(function(){
console.log("大家好,3秒之后执行");
},3000)
//取消定时器
clearTimeout(timer)
14.7定时器之setInterval
setinterval 函数的用法与setTimeout 完全一致,区别仅仅在于setinterval指定某个任务每隔一段时间就执行一次,也就是无限次的定时执行
设置定时器:setInterval
清除定时器:clearInterval
<div id="someDiv"></div>
<script>
var div=document.getElementById("someDiv");
//透明度:opacity:取值范围0-1
var opacity=1;
var fade=setInterval(function(){
if(opacity>0){
opacity-=0.05
div.style.opacity = opacity
}else{ clearInterval(fade)}
},60)
</script>
14.8防抖debounce
防抖严格算起来应该属于性能优化的知识,但实际上遇到的频率相当高,处理不当或者放任不管就容易引起浏览器卡死。
补充:滚动事件:window.onscroll
window.onscroll=function(){
console.log("页面滚动了");
}
\
获取滚动高度:
function showTop(){
var scrollTop = document.documentElement.scrollTop;
console.log('滚动条位置:'+scrollTop);
}
window.onscroll = showTop
在运行的时候会发现存在一个问题:这个函数的默认执行频率,太!高!了!。高到什么程度呢?以chrome为例,我们可以点击选中一个页面的滚动条,然后点击一次键盘的【向下方向键】,会发现函数执行了8-9次!
然而实际上我们并不需要如此高频的反馈,毕竟浏览器的性能是有限的,不应该浪费在这里,所以接着讨论如何优化这种场景。
基于上述场景,首先提出第一种思路:在第一次触发事件时,不立即执行函数,而是给出一个期限值比如200ms,然后:
如果在200ms内没有再次触发滚动事件,那么就执行函数
如果在200ms内再次触发滚动事件,那么当前的计时取消,重新开始计时
效果:如果短时间内大量触发同一事件,只会执行一次函数
实现:既然前面都提到了计时,那实现的关键就在于setTimeout这个函数,由于还需要一个变量来保存计
时,考虑维护全局纯净,可以借助闭包来实现
<body>
<h3>haha</h3>
<h3>haha</h3>
<h3>haha</h3>
<h3>haha</h3>
<h3>haha</h3>
<h3>haha</h3>
<h3>haha</h3>
<h3>haha</h3>
<script>
function debounce(fn,delay){
var timer = null;
return function(){
//此函数内调用timer属于闭包
if(timer)
{
clearTimeout(timer);
}
timer = setTimeout(fn,delay);
}
}
function showtop(){
var scrollTop = document.documentElement.scrollTop;
console.log('滚动条位置:'+scrollTop);
}
window.onscroll = debounce(showtop,200);
</script>
</body>
防抖定义
对于短时间内连续触发的事件(上面的滚动事件),防抖的含义就是让某个时间期限(如上面的200毫秒)内,事件处理函数只执行一次
14.9节流throttle
节流严格算起来应该属于性能优化的知识,但实际上遇到的频率相当高,处理不当或者放任不管就容易引起浏览器卡死。
如上一小节中的防抖问题中:
如果在限定时间段内,不断触发滚动事件(比如某个用户闲着无聊,按住滚动不断的拖来拖去),只要不停止触发,理论上就永远不会输出当前距离顶部的距离
但是如果产品同学的期望处理方案是:即使用户不断拖动滚动条,也能在某个时间间隔之后给出反馈呢?
其实很简单:我们可以设计一种类似控制阀门一样定期开放的函数,也就是让函数执行一次后,在某个时间段内暂时失效,过了这段时间后再重新激活(类似于技能冷却时间)效果:如果短时间内大量触发同一事件,那么在函数执行一次之后,该函数在指定的时间期限内不再工作,直至过了这段时间才重新生效
<h3>haha</h3>
<h3>haha</h3>
<h3>haha</h3>
<h3>haha</h3>
<h3>haha</h3>
<h3>haha</h3>
<h3>haha</h3>
<h3>haha</h3>
<script>
function throttle(fn,delay){
var valid = true;
return function(){
if(!valid){
return false;
}
valid = false;
setTimeout(function(){
fn();
valid = true;
},delay)
}
}
function showtop(){
var scrollTop = document.documentElement.scrollTop;
console.log('滚动条位置:'+scrollTop);
}
window.onscroll = throttle(showtop,1000);
</script>
\
ps:节流和防抖都是面试可能会问的题,比较重要