dom

106 阅读11分钟

dom简介

  • dom是文档对象模型(Document Object Model,简称dom),是w3c组织推荐的可处理可扩展编辑语言(HTML或XML)的标准编程接口
  • w3c已经定义了一系列的dom接口,通过这些dom接口可以改变网页的内容、结构和样式
  • dom在实际开发中主要用来操作元素
  • 对于javascript,为了能够使javascript操作HTML,javascript有了一套自己的dom编程接口
  • 对于HTML,dom使得html形成一棵dom树,包含文档、元素、节点
  • 我们获取过来的dom元素是一个对象(object),所以称为文档对象模型

dom树

  • 文档:一个页面就是一个文档,dom中使用document表示
  • 元素:页面中的所有逼啊钱都是元素,dom中使用element表示),dom中使用node来表示
  • dom把以上内容都看作是对象

一、获取元素

  • 根据ID获取document.getElementById('id')
  • 根据标签名获取document.getElementByTagName('标签')
  • 根据HTML5新增的方法获取-类名/选择器document.getElementsByClassName('类名')/document.querySelector('选择器')
  • 特殊元素获取-body/htmldocument.body/document.documentElement

1.document.getElementById('id')

  • 通过ID获取元素
  • id是大小写敏感的字符串,需要加引号
  • 返回的是dom的一个document对象

2.document.getElementByTagName('标签')

  • 返回带有指定标签名的对象的集合
  • 以伪数组的形式存储,即使只有一个元素,也是以伪数组的形式存储,假如找不到元素,则以空数组的形式存储
  • 还可以获得某个父元素内部所有指定标签名的子元素element.getElementsByTagName()
  • 父元素必须是单个对象(必须指明是哪一个元素对象)获取的时候不包括父元素自己

3.document.getElementsByClassName('类名')

  • 通过类名获取元素,根据类名返回元素对象集合

4.document.querySelector('选择器')

  • 根据制定选择器返回第一个元素对象
  • document.querySelectorAll('选择器')根据指定选择器返回所有元素对象集合,以伪数组的形式存储

二、事件三要素

  • 事件源:事件被触发的对象
  • 事件类型: 事件如何触发
  • 事件处理程序:通过一个函数赋值的方式完成

例如

<button id="btn">qaq</button>


var btn = document.getElementById('btn')//事件源
btn.onclick/*事件类型*/ = function(){
alert('qaq')
}//事件处理程序

执行事件的步骤

  • 获取事件源
  • 注册事件(绑定事件)
  • 添加事件处理程序(采取函数赋值形式)

修改元素的内容

  • element.innerText从起始位置到终止位置的内容,但它去除html标签,同时空格和换行也会去掉
  • element.innerHTML起始位置到终止位置的全部内容,包括html标签,同时保留空格和换行

修改表单属性

  • 利用dom可以操作如下表单元素的属性
  • type、value、checked、selected、disabled

className修改元素样式

修改元素样式的两种方法:

  • element.style行内样式操作
  • element.className类名样式操作(当前元素的类名改为某个类)

注意:

  • 如果样式较多,可以采取操作类名方式更改元素样式
  • 因为class是个保留子,因此使用className来操作元素类名属性
  • className会直接更改元素的类名,会覆盖原先的类名,如果想要保留原先的类名可以this.className = '原先的类名 覆盖的类名'

三、获得自定义的属性值

  • element.属性获得内置属性值(元素本身自带的属性)
  • element.getAttribute('属性')主要获得自定义的属性(程序员自定义的属性)
  • data-index="1"就是自定义属性(程序员自己添加的属性)

设置属性值

  • element.属性 = '值'设置内置属性值
  • element.setAttribute('属性','值')主要针对于自定义属性

移除属性

  • element.removeAttribute('属性')

自定义属性

  • 自定义属性目的:是为了保存并使用数据。有些数据可以保存到页面中而不用保存到数据库中
  • 自定义属性值从0开始编号
  • 有些自定义属性很容易引起歧义,不容易判断是元素的内置属性还是自定义属性
  • H5新增了自定义属性规范,规定自定义属性data-开头作为属性名并赋值,比如data-index="1"
  • 或者使用js设置element.setAttribute('data-index,2')
  • 兼容性获取element.getAttribute('data-index')
  • H5新增获取element.dataset.index或者element.dataset['index'](ie11才开始支持)
  • H5新增的方法只能获取data-开头的自定义属性

四、节点

  • 利用dom提供的方法获取元素逻辑性不强,非常繁琐。利用节点层级关系获取元素(父子兄弟关系),逻辑性强,但是兼容性差
  • 网页中所有的内容都是节点(标签、属性、文本、注释等),在dom中,节点用node来表示,html dom树中所有节点都可以通过javascript进行访问,所有html元素(节点)都可以被修改,也可以创建或删除
  • 一般情况,节点至少有nodeType节点类型,nodeName节点名称和nodeValue节点值这三个基本属性
  • 元素节点nodeType为1,属性节点nodeType为2,文本节点nodeType为3(文本节点包括文字、空格、换行等)
  • 实际开发中,节点操作主要操作的是元素节点

节点操作之父节点

  • node.parentNode得到是离元素最近的父节点,不是爷爷节点,如果找不到父节点就返回为空null

节点操作之子节点

  • parentNode.childNodes返回包括指定节点的子节点的集合,该集合为即时更新的集合,包含元素节点文本节点等等
  • 如果只想要获得里面的元素节点,则需要专门处理,所以一般不提倡使用childNodes
var ul = document.querySelector('ul')
for(var i = 0;i < ul.childNodes.length;i++){
    if(ul.childNodes[i].nodeType == 1){
        console.log(ul.childNodes[i])
    }
}
  • parentNode.children只返回所有的子元素节点,是一个只读属性

节点操作之第一个子节点和最后一个

  • parentNode.firstChild返回第一个子节点,找不到则返回null,同样也是包含所有节点
  • parentNode.lastChild返回最后一个子节点,其余同上
  • parentNode.firstElementChild返回第一个子元素节点,找不到则返回null,有兼容性问题,ie9以上才支持
  • parentNode.lastElementChild返回最后一个子元素节点,其余同上
  • 解决方案:如果想要获得第一个子元素节点可以使用parentNode.children[0]
  • parentNode.children[parentNode.children.length - 1]获得最后一个子元素节点,例如ol.children[ol.children.length - 1]

节点操作之兄弟节点

  • node.nextSibling返回当前元素的下一个兄弟节点,找不到则返回null,同样,也是包含所有节点
  • node.previousSibling返回当前元素的上一个兄弟节点,其余同上
  • node.nextElementSibling返回当前元素的下一个兄弟元素节点,找不到则返回null,有兼容性问题ie9以上才支持
  • node.previousElementSibling返回当前元素的上一个兄弟元素节点,其余同上
  • 解决兼容性问题只能自己封装一个兼容性函数
function getNextElementSibling(element){
    var el = element;
    while (el = el.nextSibling){
        if(el.nodeType === 1){
            return el;
        }
    }
    return null;
}

节点操作之创建和添加节点

  • document.createElement('tagName')创建由tagName指定的HTML元素,因为这些元素原先并不存在,是根据我们的需求动态生成的,所以也称为动态创建元素节点,接受的参数是字符串
  • node.appendChild(child)将创建的节点添加到指定父节点的子节点的列表末尾,类似于css里面的after伪元素,类似于数组中的push,node是父级,child是子级,接受的参数是变量
  • node.insertBefore(child,指定元素)例如ul.insertBefore(li,ul.children[0]将一个节点添加到父节点的指定子节点前面,类似于before伪元素
  • 想要页面添加一个新的元素需要两步:1.创建元素 2.添加元素

节点操作之删除节点

  • node.removeChild(child)从dom中删除一个子节点,返回删除的节点

节点操作之复制节点

  • node.cloneNode()返回调用该方法的节点的一个副本,也称为克隆节点/拷贝节点
  • 注意:如果括号参数为空或者false,是浅拷贝,即只克隆复制节点本身,不克隆里面的子节点(只复制标签不复制文本等内容)
  • 深拷贝括号里为true

三种动态创建元素的区别

  • document.write()是直接将内容写入页面的内容流,但是如果文档流执行完毕,它会导致页面全部重绘
  • element.innerHTML是将内容写入某个dom节点不会导致页面全部重回,创建多个元素时效率比较低,时间比较慢,解决方案,不要拼接字符串,采取数组醒睡拼接,效率会更高,结构稍微复杂
  • document.createElement()创建多个元素时效率比较高,时间比较快,结构更清晰

五、事件高级

1.1 注册事件

  • 给元素添加事件,称为注册事件或者绑定事件
  • 注册事件有两种方式:传统方式和方法监听注册方式

1.2 传统注册方式

  • 利用on开头的事件 比如onclick
  • 特点是注册事件的唯一性,同一个元素同一个事件只能设置一个处理函数,最后注册的处理函数将会覆盖前面注册的处理函数

1.3 方法监听注册方式

  • 是w3c标准推荐的方式
  • addEventListener()它是一个方法
  • ie9之前不支持此方法,可使用attachEvent()代替
  • 特点是同一个元素同一个事件可以注册多个监听器,按注册顺序依次执行
  • eventTarget.(addEventListener(type,listener[,useCapture])例如btns[1].addEventListener('click',function(){alert(22);})
  • eventTarget.addEventListener()方法是将指定的监视器注册到eventTarget(目标对象)上,当该对象触发指定的事件时,就会执行事件处理函数
  • 该方法接受三个函数,type、listener、useCapture
  • type:事件类型字符串,比如click、mouseover、注意这里不要带on,要加引号
  • listener:事件处理函数,事件发生时,会调用该监听函数
  • useCapture:可选参数,是一个布尔值,默认是false

1.4 attachEvent 事件监听方式

  • 该特性是非标准的,请尽量不要在生产环境中使用它,这是ie9之前的浏览器专有的替代性标准

2.1 删除事件

  • 传统方式删除事件eventTarget.onclick = null
  • 方法监听删除事件eventTarget.removeEventListener(type,listener[,useCapture])例如
divs[1].addEventListener('click',fn)
function fn(){
    alert(22)
    divs[1].removeEventListener('click',fn)
}

六、dom事件流

  • 事件流描述的是从页面中接收事件的顺序,事件发生时会在元素节点之间按照特定的顺序传播,这个传播过程即dom事件流
  • dom事件流分为3个阶段,分别是捕获阶段、当前目标阶段、冒泡阶段
  • 事件捕获:网景最早提出,由dom最顶层节点开始,然后逐级向下传播到最具体的元素接收的过程
  • 事件冒泡:由ie最早提出,事件开始时由最具体的元素接收,然后逐级向上传播到dom最顶层的节点的过程
  • 注意:js代码中只能执行捕获或者冒泡其中一个阶段
  • 注意:onclick和attachEvent只能得到冒泡阶段
  • 注意:addEventListener(type,listener[,useCapture])第三个参数如果是true,表示在事件捕获阶段调用事件处理程序,如果是false(不写默认是false)表示在事件冒泡阶段调用事件处理程序
  • 实际开发中我们很少使用事件捕获,我们更关注事件冒泡
  • 注意:有些事件是没有事件冒泡的,比如onclick、onfocus、onmouseenter、onmouseleave

七、事件对象

  • 事件对象写到侦听函数的小括号里,是形参
  • 事件对象只有有了事件才会存在,是系统自动创建的,不需要我们传递参数
  • 事件对象代表事件的状态。事件对象是我们事件的一系列相关数据的集合,跟事件相关,比如鼠标点击事件包含了鼠标的相关信息,鼠标坐标等,键盘事件包含了键盘的信息,比如判断用户按下了哪个键盘
  • 这个事件对象我们可以自己命名,通常命名为event、evt、e
  • 事件对象也有兼容性问题,ie678只能通过window.event,兼容性写法为e = e || window.event

事件对象的常见属性和方法

  • e.target返回触发事件的对象(注意:与this不同,this返回的是绑定事件的对象)(了解:currentTarget是一个与this非常相似的属性)
  • e.srcElement返回触发事件的对象 非标准 ie678使用
  • e.type返回事件的类型,比如click、mouseover、不带on
  • e.cancelBubble = true该属性阻止冒泡。非标准 ie678使用
  • e.returnValue该属性阻止默认事件(默认行为)非标准 ie678使用 比如不让链接跳转
  • e.preventDefault()该方法阻止默认事件,标准,比如不让链接跳转,让提交按钮不提交
  • e.stopPropagation()阻止冒泡 标准

事件委托

  • 事件委托的原理:不是每个子节点单独设置事件监听器,而是事件监听器设置在其父节点上,然后利用冒泡原理影响设置每个子节点
  • 比如给ul注册点击事件,然后利用事件对象的target来找到当前点击的li,因为点击li,事件会冒泡到ul上,ul有注册事件,就会触发事件监听器
  • 事件委托的作用:只操作了一次dom,提高了程序的性能