一天学完JavaScript(下)

130 阅读14分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

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元素(比如

、、等),返回一个类似数组的的对象(NodeList实例),因为name属性相同的元素可能不止一个

//表单为<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:节流和防抖都是面试可能会问的题,比较重要