你的浏览器不支持canvas

Love You Ten Thousand Years

原生JavaScript的DOM操作

Date: Author: M/J

本文章采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可。转载请注明来自小可嗒嗒的博客

DOM中的D指的是documentO指的是Object,在JavaScript语言中,有三种对象类型,分别是user-defined objectnative objecthost object,对于host object,指的是浏览器提供的对象,最基础的就是window本身了。其中的M,指的就是模型。

那整个模型怎么解释呢?

下面是一个基本的网页。

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset='utf-8'/>
        <title>Shopping list</title>
    </head>

    <body>
        <h1>What to buy</h1>
        <p title='a gentle reminder'>Don't forget to buy this stuff.</p>
        <ul id='purchases'>
            <li>A tin of beans</li>
            <li class='sale'>Cheese</li>
            <li class='sale important'>Milk</li>
        </ul>
    </body>
</html>

这份文档可以用下面这个模型来表示:

DOM-model

这都是一些元素,有些有父元素、有些有子元素、有些有兄弟元素

同时每个元素标签又以节点(node)作为一个整体。比如,在<p></p>标签所包裹的区域。

node

并不是所有元素都包裹文本节点属性节点

总结以下:

  • 一份文档就是一颗节点树
  • 节点分为不同的类型:元素节点、属性节点和文本节点等
  • 每个节点都是一个对象

以上这些就是基本概念了,那么我们怎么去操纵这些元素呢?第一步,我们需要获取元素。


获取元素

有三种DOM方法可以获取元素节点。

getElementById() – 通过元素ID

方法返回一个与那个有着给定id属性值的元素节点对应的对象。它是document对象特有的函数,使用方法是:

document.getElementById(id)

getElementByTagName() – 通过标签名字

方法返回一个对象的数组。使用方法是:

document.getElementByTagName(tag)

getElementByClassName() – 通过类名来获取

方法返回一个具有相同类名的元素的数组。

document.getElementByClassName(class)

获取和设置属性

得到需要的元素以后,我们就可以设法获取它的各个属性。

getAttribute() – 获取你打算查询的属性名字

getAttribute()方法不属于document对象,所以不能通过document对象调用,它只能通过元素节点对象调用。

object.getAttribute(attribute)

setAttribute()

setAttribute()允许我们对属性节点的值做出修改。与getAttribute()一样,setAttribute()也只能用于元元素节点。

object.setAttribute(attribute, value)

DOM属性

搭配DOM的各种属性,我们可以进行更多的操作。

childNodes属性

在一棵节点树上,childNodes属性可以用来获取任何一个元素的所有子元素,它是一个包含这个元素全部子元素的数组。

element.childNodes

nodeType属性

每个节点都有一个nodeType属性,通过这个属性我们知道我们在与哪种节点打交道。

node.nodeType

nodeType属性总共有12种可取值,但其中仅有3种具有实用价值。

  • 元素节点的nodeType属性值是1
  • 属性节点的nodeType属性值是2
  • 文本节点的nodeType属性值是3

nodeValue属性

如果想改变一个文本节点的值,那就使用DOM提供的nodeValue属性,它用来得到(或设置)一个节点的值。

node.nodeValue

firstChild和lastChild属性

有时候我们只需要访问childNodes数组的第一个元素childNodes[0],可以使用firstChild属性。

node.firstChild

这与node.childNodes[0]完全等价。

同理,node.lastChild代表childNodes数组的最后一个元素。


事件处理函数

事件处理函数的作用是,在特定的事件发生时调用特定的JavaScript代码。

  • 如果想在鼠标指针悬停在某个元素上时触发一个动作,就使用onmouseover事件处理函数
  • 如果想在鼠标指针离开某个元素时触发一个动作,可以使用onmouseout事件处理函数
  • 如果想点击某个链接时触发一个动作,可以使用onclick事件处理函数

添加事件处理函数的语法如下:

event = "JavaScript statement(s)"

JavaScript代码包含在一对引号之间。

onclick事件处理函数,我们可以这样使用。

onclick = "xxx(this);return false;"

xxx是调用的JavaScript函数,使用this关键字指代这个对象return false;是为了防止默认行为。

这种在HTML中指定事件处理程序有缺点。

  • 时差问题。如果页面在解析JavaScript就调用了事件处理函数,就会引发错误
  • HTMLJavaScript代码紧密耦合

所以很多开发员转而使用JavaScript指定事件处理程序。有三种方式。

DOM0级事件处理函数

var btn=document.getElementById("mybtn"); // 取得该按钮的引用
btn.onclick=function(){
alert('clicked');
alert(this.id); // mybtn
}

//删除指定的事件处理函数
btn.onclick=null; // 删除事件处理程序

以这种方式添加的事件处理程序会在事件流的冒泡阶段被处理。

DOM2级事件处理函数

DOM2级事件定义了两个方法,用于处理指定和删除事件处理程序的操作:addEventListener()removeEventListener()。所有DOM节点中都包含这两个方法,并且它们都接受3个参数:要处理的事件名,做为事件处理程序的函数和一个布尔值。最后这个参数如果是true,表示在捕获阶段调用事件处理程序;如果是fasle,表示在冒泡阶段调用事件处理程序。

var btn=document.getElementById("mybtn");
btn.addEventListener("click",function(){
alert(this.id);
},false);
btn.addEventListener("click",function(){alert("hello world!");},false);

使用DOM2级事件处理程序的主要好处是可以添加多个事件处理程序。

通过addEventListener()添加的时间处理程序只能使用removeEventListener()来移除,移除时传入的参数与添加时使用的参数相同。这也意味着通过addEventListener()添加的匿名函数将无法移除。

IE事件处理函数

IE实现了与DOM中类似的两个方法:attachEvent()detachEvent()。这两个方法接受相同的两个参数:事件处理程序名称和事件处理程序函数。由于IE只支持时间冒泡,所有通过attachEvent()添加的事件处理程序都会被添加包冒泡阶段。


动态创建标记

前面我们是查找并更改已存在的元素,但是,JavaScript也可以用来改变网页的结构和内容。

传统方法

  • document.write: document对象的write()方法可以方便快捷地把字符串插入到文档中
  • innerHTML: innerHTML属性可以用来读、写某给定元素里的HTML内容

DOM方法

首先,我们需要明白的一点是,在浏览器看来,DOM节点树才是文档。所以,在动态创建标记的时候,我们并不是在创建标记,而是在改变DOM节点树。

createElement()方法

语法是:

document.createElement(nodeName)

比如:

//创建一个p元素
document.createElement("p");

appendChild()方法

把新创建的节点插入某个文档的节点树的最简单的方法,就是让他称为这个文档某个现有节点的一个子节点。

parent.appendChild(child)

createTextNode()方法

createElement()方法只能创建元素节点,如果你需要创建一个文本节点,你可以用createTextNode()方法来实现它。

语法是:

document.createTextNode(text)

需要了解的是,JavaScript脚本只应该用来充实文档的内容,避免使用DOM技术来创建核心的内容。


CSS-DOM

显而易见,CSS-DOM显然是操纵表现层CSS的。

style属性

文档的每个元素节点都是一个对象,有着多种类型属性,比如:

  • 元素在节点树的位置信息: parentNode/nextSibling/previousSlibling/childNodes/firstChild/lastChild这些属性
  • 包含元素本身的属性:nodeType/nodeName这些属性
  • 除此之外,还有一个style属性,包含了元素的样式,这个属性返回一个对象而不是一个简单的字符串。

样式都存在style属性的返回对象中:

element.style.property

获取样式

element.style.property

//比如
element.style.color
element.style.fontFamily

注意:当你需要引用一个中间带-号的CSS属性时,DOM要求你用驼峰命名法。即font-family变成fontFamily

设置样式

使用style返回的对象的各个属性都是可读写的。我们还可以通过它去更新样式。解决方法就是获取样式之后,进行赋值。

className属性

要知道,用行为层(JavaScript)去改变表现层(CSS)的这种做法通常来说是不可取的。

我们可以使用className属性去获取或更改样式。

语法是:

element.className

通过CSS-DOM去改变样式有一个最大的障碍就是:无法改变外部引用的CSS


总结

常用的DOM方法和属性集合

暂略。

混淆概念

DOM-Core vs HTML-DOM

DOM是由W3C制定的一套访问和操作XML文档的标准,即APIDOM与特定的平台、浏览器、语言无关,很多种语言都实现了DOM,比如因为JavaScriptPHP都实现了DOM

但为什么DOM可以用来访问和操作HTML呢?

Web语义化的一个发展方向是将HTML逐渐演变为更有语义、能将数据内容与现实分离的XML,但HTML不可能立即演变为XML,所以出现了一个过渡产物–XHTMLHTMLXHTML网页形成的节点树在结构上与XML节点树一样,可以看做是一个符合DOMXML文档,因此可以使用实现了DOM的程序语言(如JavaScriptPHP等)来访问和操作HTML文档,即访问和操作那些节点。

由于HTMLXML的相似性及差异,JavaScript不仅实现了标准的DOM方法和属性(即由W3C制定的),而且还实现了HTML特有的DOM方法和属性,前者称为DOM-Core,并不专属于JavaScript,后者称为HTML-DOM

比如:innerHTML就是特有的HTML-DOM方法

来源

DOM vs BOM

DOMBOM的结构关系示意图:

DOM-BOM

stackoverflow上对这个问题的解释是:

BOM包含了window对象的所有子对象navigator/history/screen/location/document,在document中,节点就是DOM,表示页面的所有内容。

window是一个全局对象,这意味着在网页中定义的任何对象,变量和函数,都以window作为其global对象。

  • document对象:实际上是windows对象的属性,document === window.documenttrue,是唯一一个既属于BOM又属于DOM的对象。
document.anchors[0]或document.anchors["anchName"] //访问页面中所有的锚  
document.forms[0]或document.forms["formName"]  //访问页面中所有的表单  
document.images[0]或document.images["imgName"]  // 访问页面中所有的图像  
document.links [0]或document.links["linkName"]  //访问页面中所有的链接 
document.applets [0]或document.applets["appletName"]  //访问页面中所有的Applet  
document.embeds [0]或document.embeds["embedName"]  //访问页面中所有的嵌入式对象  
  • location对象: 表示载入窗口的URL,也可用window.location引用它

  • navigator对象: 含大量有关Web浏览器的信息,在检测浏览器及操作系统上非常有用,也可用window.navigator引用它

  • screen对象: 用于获取某些关于用户屏幕的信息,也可用window.screen引用它

冒泡型事件 vs 捕获型事件

DOM同时两种事件模型:冒泡型事件和捕获型事件

  • 冒泡型事件: 事件按照从最特定的事件目标到最不特定的事件目标的顺序触发
 <body onclick="handleClick()">  
    <div onclick="handleClick()">Click Me</div>  
</body> 

触发的顺序是:divbodyhtml(IE 6.0和Mozilla 1.0)、documentwindow(Mozilla 1.0)

  • 捕获型事件: 与冒泡事件相反的过程

上面例子触发的顺序是:documentdiv


对于本文内容有问题或建议的小伙伴,欢迎在文章底部留言交流讨论。