DOM对象

DOM是针对HTML和XML文档的一个API(应用程序编程接口)。

它具有跨平台和语言中立的特点。

DOM将HTML和XML描述成一个多层节点构成的结构。

每个节点都有不同的特点和表现特征。

不同的节点之间具有一定的层级关系(父子、祖先和兄弟)。

文档节点是每个DOM的根节点,被称为文档元素,而每个DOM只能有一个文档元素。

Node类型

每个文档节点都可以看作一个Node,JavaScript中通过Node类实现了DOM中的文档节点。
Node一共有12种类型,DOM中的任何节点必定属于这些类型中的某一种。

  • Node.ELEMENT_NODE(1);
  • Node.ATTRIBUTE_NODE(2);
  • Node.TEXT_NODE(3);
  • Node.CDATA_SECTION_NODE(4);
  • Node.ENTITY_REFERENCE_NODE(5);
  • Node.ENTITY_NODE(6);
  • Node.PROCESSING_INSTRUCTION_NODE(7);
  • Node.COMMENT_NODE(8);
  • Node.DOCUMENT_NODE(9);
  • Node.DOCUMENT_TYPE_NODE(10);
  • Node.DOCUMENT_FRAGMENT_NODE(11);
  • Node.NOTATION_NODE(12);

我们常用的类型一般是 1(元素节点) 和 3(文本节点)。除了这两种外,还可以再了解一下类型9(文档节点)。在JavaScript中,
document.nodeType === 9

跟节点关系相关的一些属性
  • childNodes:表示该节点下的子节点列表,它是一个伪数组,并且动态获取的。关于动态获取的意思,举例说明。
<html> 
    <head> 
        <title>Sample Page</title> 
    </head> 
    <body> 
        <p>Hello World!</p> 
    </body> 
</html>

body节点的childNodes是节点p,假如有如下代码:

var sons = document.body.childNodes;
console.log(sons.length); // 1

// 如果我们动态给body添加一个子节点
document.body.appendChild(document.createElement('div'));
// 那么sons.length = ?
console.log(sons.length);
// sons.length === 2;
  • nextElementSibling / previousElementSibling:两个属性分别表示当前节点的下一个兄弟节点和上一个兄弟节点
  • parentNode:每个节点都有一个parentNode属性,指向其父节点;
  • lastChild / firstChild: 分别指向childNodes中的最后一个和第一个元素。
  • hasChildNodes:这个方法在节点包含一或多个子节点的情况下返回 true
  • ownerDocument:该属性指向表示整个文档的文档节点
操作节点
  • appendChild:向childNodes中追加一个新的节点;
  • insertBefore: 向childNodes中的某个特定位置追加一个新的节点;
  • replaceChild:替换childNodes中的某个节点
  • removeChild:删除childNodes中的某个节点
  • cloneNode:复制当前节点
  • normalize: 可以理解为将当前节点下的文本节点排版进行规范性质的优化,例如将相邻的文本节点合并为一个文本节点
Document类型

Document的子节点可以是DocumentType、Element、ProcessingIn-struction或Comment。
- DocumentType: 文档类型,指HTML文件最开始那一行文档版本说明;<!DOCTYPE html>是一种
- Element: 文档元素,HTML中的html, body, div等之类
- ProcessingIn-Struction: 处理指令,给页面解析器提供额外的信息
- Comment: 文档中的注释; <!--comment info -->之类

Document中的一些常见属性
  • documentElement : 该属性始终指向HTML页面中的<html>元素
  • doctype: 表示文档类型,该值有可能为空,在页面没有定义文档类型的情况下;不同的浏览器对doctype支持不太一致
  • 注释类型: 理论上讲,在html元素外面的注释也算是Document的子节点,but,浏览器实际实现情况却不太一致。不过也可以理解,毕竟是注释,可能应用场景的确太少;
Document 的一些文档信息
  • title: 文档标题; 这个属性的值跟<title>元素中文本一样,是显示在浏览器标题栏或标签页的信息;
  • URL: 页面完整的URL
  • domain: 只包含当前页面的域名
  • referrer: 链接到当前页面的那个页面的URL

注意domain属性,由于跨域安全问题的设置,并不是可以给domain属性设置任何值。

元素查找

getElementByIdgetElementsByTagName
分别通过元素id 和元素名称进行元素查找,这俩方法被所有的浏览器支持,但需要注意的是,低版本的IE(IE7-)对具体细节可能有第二种解释。

一些特殊的集合
  • document.anchors: 返回文档中所有的带name特性的<a>元素;
  • document.applets: 返回文档中所有的<applet>元素,注意applet元素不再 推荐使用;
  • document.forms: 返回文档所有的<form>元素;
  • document.images: 返回文档中所有的<img>元素;
  • document.links: 返回所有带href特性的<a> 元素;
文档写入功能

四个方法:write, writeln, open, close

write()writeln(), 都会将文本原样地写入文档当中,不同的是writeln会在文本末尾添加一个换行符。但是这在页面展示上似乎并没有什么明显差异(因为HTML中 除了会导致换行的元素外,<br> 才会产生换行,普通文本换行只是在文档中换行,但是解析后结果一样)。

document.write('<p> current time is <strong>: ' + new Date() + '</strong> </p>');
document.writeln('<p> current time is <strong>: ' + new Date() + '</strong> </p>');

PS:如果在文档加载结束后(load完成)再调用 document.write(),那么输出的内容将会重写整个页面。

方法 open()close()分别用于打开和关闭网页的输出流。

下面解释下,为什么在文档加载结束后,调用write()/writeln()方法会覆盖页面内容

在页面加载完成后,调用write()/writeln(),会自动触发document.open()方法,而document.open,会擦除原有的文档内容。那么在文档加载期间调用write()/writeln()之所以不会覆盖页面内容,是因为此时文档处于open状态,不需要调起document.open()方法,因而不会复写文档内容。当调用document.open打开文档写入流后,记得调用document.close()方法关闭写入流。如果在调用document.close()方法后,再调用write()/writeln()方法,就会覆盖之前写入的内容。

document.open();
document.write('<p>The First write</p>');
document.write('<p>不会覆盖上一行代码写入的数据</p>');
document.close();
document.write('<p>会覆盖之前两行写的数据</p>');
document.close();
Element类型

文档中的Element是除Document外,用的最多的类型了。一般情况下文档中的元素都会有如下几个属性:
- nodeType: Element类型的节点,noteType都是1
- nodeName: 元素的标签名
- nodeValue: 元素的nodeValue都是null
- parentNode: 元素的上级节点,可能是Document,也有可能是另一个Element
- Element的子节点可能是Element,Text, Comment等等

HTML元素的基本特性
  • id: 元素在文档中的唯一标识符
  • title: 有关元素的附加信息说明,一般通过工具提示条显示出来
  • lang: 元素内容的语言代码,
  • dir: 语言的方向, 值为‘ltr’ (left-to-right, 从左至右)‘rtl’(right-to-left, 从右至左)
  • className: 对应元素的class属性
元素属性操作

getAttribute()setAttribute()removeAttribute()

三个方法接受的第一个参数都是特性名称(注意,特性名不区分大小写)。

var body = document.body;
body.newattr = 'test';
// 我们可以这样为元素添加自定义的新特性,但是我们无法用getAttribute()方法取到它的value
console.log(body.getAttribute('newattr')); // undefined
// 只有通过setAttribute()方法添加的自定义特性,才可以通过getAttribute()方法取到
body.setAttribute('myattr', 1);
console.log(body.getAttribute('myattr')); // '1'

注意,我们可以通过body.getAttribute('id')来获取元素特性,也可以通过body.id同样取得该值。这是元素本身定义好的。但存在一些特殊的特性,我们通过这两种方式,取到的值也许是不一样的。比如:

var body = document.body;
console.log(body.style); // 输出一个对象 (引用类型)
console.log(body.getAttribute('style')); // 输出结果是一个字符串类型
attributes 属性

记住,Element类型是使用attributes属性的唯一一个DOM节点类型。
attributes属性中包含了一系列属性节点,每个节点都对应元素的一个特性。
attributes是一个NamedNodeMap对象,有以下几个方法:
- getNamedItem(name): 获取属性节点
- removeNamedItem(name): 删除属性节点
- setNamedItem(node) : 设置属性节点
- item(index): 根据index,返回相应的节点

创建元素

createElement()

元素的子节点

先上一段html代码

<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
</ul>

那么<ul>元素有几个子节点呢?
IE浏览器认为是3个(3个<li>); 而其它浏览器认为是7个(3个<li>和4个文本节点(<li>元素前后的空白符))

console.log(ul.childNodes);  // 下面是Chrome 67下的输出结果
// NodeList(7) [text, li, text, li, text, li, text] 

如果不想要文本节点,那么只能这么写

<ul><li>1</li><li>2</li><li>3</li></ul>
文本节点

文本节点由 Text 类型表示,包含的是可以照字面解释的纯文本内容。文本可以包含转义后的HTML字符串(<,> 等)

Text 类型的一些常用特性
  • nodeType === 3;
  • nodeName === ‘#text’;
  • nodeValue === nodeData , 节点包含的纯文本字符串;
  • parentNode 是一个 Element;
  • 没有子节点
Text类型几个操作节点的方法
  • appendData(text):给文本节点追加内容text ;
  • deleteData(offset, count): 从offset开始,删除长度为count的字符串;
  • insertData(offset, text): 在offset位置插入字符串text;
  • replaceData(offset, count, text): 在offset位置将长度为count的字符串替换为text;
  • splitText(offset):在offset位置切割文本节点,并返回一个新的文本节点,原文本节点只保留offset之前的内容;
  • substringData(offset, count):从offset位置开始截取长度为count的内容,原文本节点没有变动;
var t = document.createTextNode('111');
t.nodeType; // 3
t.nodeName; // '#text'
t.nodeValue; // '111'
t.nodeData; // '111'
t.deleteData(1,1);
t.nodeValue; // '11'
t.insertData(2, '234');
t.nodeValue; // '11234'
t.replaceData(0, 1, '')
t.nodeValue; // '1234'
var ts = t.splitText(2);
ts.nodeValue; // '34'
t.nodeValue; // '12'
t.substringData(1,1); // '2'
normalize()方法

正常情况下,一个Element中永远不会存在相邻的文本节点。浏览器如果碰见相邻的文本节点,会合并成一个。
但也不排除存在两个相邻节点的情况————人工操作。

var el = document.createElement('div');
var t = document.createTextNode('hello world\n');
el.appendChild(t);
console.log(el.childNodes.length); // 1
t.splitText(5);
console.log(el.childNodes.length); // 2

// 当然,认为追加文本节点,也会产生相邻文本节点但情况
var t2 = document.createTextNode('another text');
el.appendChild(t2);
console.log(el.childNodes.length); // 3

Node类型的节点都有一个normalize()方法,用于合并相邻但文本节点。
接着上面的例子讲:

el.normalize();
console.log(el.childNodes.length); // 1
Comment节点

注释节点是通过Comment类型表示,这个类型和Text继承自相同的基类,因此它们具有相同的方法,除了splitText
创建注释节点可以通过createComment()方法。

var c = document.createComment('This is comment content~~~~');
document.body.appendChild(c);
CDATASection类型

CDATASection类型只针对与XML文档。它跟Comment类型,同样跟Text类型继承自同一基类,同样也没有splitText方法。
在浏览器中解析XML文档,CDATASection类型一般会被当成注释解析。

  • nodeType = 4
  • nodeName = ‘#cdata-section’
  • nodeValue = CDATA区域中的内容
  • parentNode是Document 或 Element
  • 没有子节点
DocumentType

表示文档类型

  • nodeType = 10;
  • nodeName :为 doctype 的名称
  • nodeValue :值为 null;
  • parentNode 是 Document;
  • 不支持(没有)子节点
DocumentFragment

表示一个文档片段,可以简单理解为,从Document中任意取下一部分即可看作为DocumentFragment。

  • nodeType = 11
  • nodeName = ‘#document-fragment’
  • nodeValue: null
  • parentNode: null
  • 子节点跟Document的子节点基本相同,除了没有DocumentType类型。

我们不可以将DocumentFragment本身直接添加到Document当中,但是可以将DocumentFragment中包含的节点添加到Document中。可以将DocumentFragment看作是一个临时仓库,可以存储DOM节点,而且它具有Node类型的所有方法,可以对自身包含的节点进行各种跟Document一样可以进行的操作。

Attr类型

Attr类型在DOM中的定位很尴尬啊~! 理论上讲,它并不是Document的节点(Node)。它只是一般用于描述Node特性的数据,所以把它看作是Node有些牵强。关于它的一些相关内容,了解下就可以了。

  • nodeType 的值为 2
  • nodeName 的值是特性的名称
  • nodeValue 的值是特性的值
  • parentNode 的值为 null;
  • 在 HTML 中不支持(没有)子节点,在 XML 中子节点可以是 Text 或 EntityReference

Attr类型的几个方法
- createAttribute, 创建属性
- setAttributeNode, 将属性添加到元素节点上
- getAttributeNode, 根据属性名称获取属性

DOM操作技术

动态脚本和动态样式

DOM家在JavaScript和CSS代码有两种方式:引入外部文件直接插入代码

写法很简单,看到这儿的朋友99.99999%都会。

下面写个兼容性比较高的方法,活动下手指。

// 动态写入JavaScript代码
function loadScriptCode(code) {

    var script = document.createElement('script');
    script.type = 'text/javascript';
    try {
        script.appendChild(document.createTextNode(code));
    } catch{
        // IE浏览器认为script是一种特殊的脚本,不允许直接访问子节点
        script.text = code;
    }
    document.body.appendChild(script);
}

function loadStyleCode(code) {
    var style = document.createElement('style');
    style.type = 'text/css';
    try {
        style.appendChild(document.createTextNode(code));
    } catch{
        // IE 浏览器将style看作特殊元素,不允许直接访问其子节点
        style.styleSheet.cssText = code;
    }
    document.head.appendChild(style);
}
表格操作

因为表格的结构比较复杂,有表头、行、列以及单元格等。如果用传统的方式,操作DOM节点,会特别麻烦冗长。
所以,HTML DOM为表格及表格里面的元素添加了一些特殊的属性和方法。

NodeList

NodeList 及其“近亲”NamedNodeMap 和 HTMLCollection,是从整体上透彻理解 DOM 的 关键所在。这三个集合都是“动态的”;换句话说,每当文档结构发生变化时,它们都会得到更新。因此,它们始终都会保存着最新、最准确的信息。从本质上说,所有 NodeList 对象都是在访问 DOM 文档时实时运行的查询。

var divs = document.getElementsByTagName('div');

for(var i = 0; i < divs.length; i++){
    document.body.appendChild(document.createElement('div'));
}

一般来说,应该尽量减少访问 NodeList 的次数。因为每次访问 NodeList,都会运行一次基于文 档的查询。所以,可以考虑将从 NodeList 中取得的值缓存起来。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值