js之DOM

415 阅读14分钟

    文档对象模型(DOM,Document Object Model)它是HTML和XML文档的编程接口。一般来说,一个网页页面由HTML搭建结构,通过CSS定义样式,通过js完成与页面的交互。而DOM是web页面与js连接的通道,它是js操作网页的接口,将web页面与编程语言连接起来了。说白了,它就是一套针对标记语言的API。当网页被加载时,浏览器会创建页面的文档对象模型,通过DOM可以动态的改变文档内容。

    浏览器会根据DOM模型将HTML文档解析成一系列的节点,再由这些节点组成一个树状结构。DOM的最小单位叫做节点(node)。DOM树由这些节点组成。节点分为很多类型,每种类型对应文档中不同的信息和标记也有着自己不同的特性和方法。通过下文html代码解析成的DOM节点层级图,我们更加清晰地看到各节点之间关系。其中document节点表示文档的根节点,它唯一的子元素是<html>文档元素(每个文档只能有一个文档元素)。整个文档是一个文档节点,每个HTML元素是元素节点,HTML元素内的文本是文本节点,每个HTML属性是属性节点,注释是注释节点。DOM中总共有12种节点类型。下面分别对重要类型进行说明。

1.png

Node类型

1、 nodeType

    在JavaScript中,所有的节点类型都继承Node类型,所以所有类型都共享相同的基本属性和方法。一般来说节点都有nodeType、nodeValue和nodeName属性。一些常见节点的NodeType如下表所示:

节点nodeType值
Node.ELEMENT_NODE1
Node.ATTRIBUTE_NODE2
Node.TEXT_NODE3
Node.COMMENT_NODE8
Node.DOCUMENT_NODE9

如果要确定一个节点的类型,可以通过与这些常量比较来确定:如:

if(someNode.nodeType == 1){
    alert('node is an element')
}

    nodeValue和nodeName属性它们保存相关节点的信息,完全取决于节点类型。对于元素而言,nodeName始终等于元素的标签名,nodeValue始终为null。

<body>
    <div id="box">box</div>
    <script>
        var box = document.getElementById('box');
        console.log(box.nodeType); //1
        console.log(box.nodeName); //DIV
        console.log(box.nodeValue); //null
        //访问元素的标签名可以使用nodeName和tagName这两个属性,它们会返回相同的值。
        console.log(box.nodeName == box.tagName); //true
    </script>
</body>

2、节点关系

    文档中的所有节点都与其它节点有关系,这些关系可以形容成家庭关系。我们可以用父、子和同胞等术语来描述这些关系。在节点树中,顶端节点被称为根(root),每个节点都有父节点(根除外),一个节点可以有任意数量的子节点,同胞节点是拥有相同父节点的节点。

    每个节点都有一个childNodes属性,其中包含一个NodeList实例。它保存着这个节点的第一层子节点。NodeList实例它存储可以按位置存取的有序节点,是一个对DOM结构的实时查询,我们可以通过length属性或者是中括号来访问它的值。

    每个节点都有一个parentNode属性,指向其DOM树中的父元素。childNodes中所有的节点都有同一个父元素。它们的praentNode相同。并且childNodes列表中的每个节点都是同一列表中其它节点的同胞节点,可以使用previousSibling或者是nextSibling来进行查找。如果chidNodes中只有一个节点,它的previousSibling和nextSibling都为null。

    firstChild和lastChild分别指向childNodes中的第一个和最后一个子节点。如果只有一个子节点那么这二者指向同一个节点。

    所以我们可以通过以下方法来获取到节点:

  • document.documentElement 获取html元素
  • document.head 获取头部元素
  • document.body 获取body元素
  • node.parentNode 获取父节点
  • element.childNodes 获取已知节点的所有子节点,包括文本节点,注释节点
  • element.children 获取已经元素的所有子节点,只返回元素节点
  • node.firstChild/node.lastChild 获取第一个子节点/最后一个子节点
  • node.firstElementChild/node.lastElementChild 获取第一个元素节点/最后一个元素节点
  • node.nextSibling /node.previousSibling 获取已知节点后一个节点/前一个节点
  • node.nextElementSibling/node.previousElementSibling 类似nextSibling和previousSibling,但只代表兄弟element.

3、操纵节点

3.1 增加插入节点

    DOM中最常用操作节点的方法是parentNode.appendChild() 或者是parentNode.insertBefore() 。使用 Document对象的createElement() 方法创建新的 Element节点,然后再使用appendChild()或insertBefore() 将其插入到文档中。在这里我们需要注意的是:

  • 使用document.createElement(tagName)方法时,它的参数是元素的标签名;
  • appendChild(newNode)方法是在新节点的父节点上调用,用于在childrenNodes列表末尾添加节点。
  • 如果想把节点放到childrenNodes中的特定位置而不是末尾,则可以使用insertBefore()方法,这个方法接收两个参数,要插入的节点和参照节点,要插入的节点将插入到参照节点前面并被返回。如果参照节点为null,则insertBefore()与appendChild()效果相同。
  • 调用appendChild()或insertBefore()将一个已经存在于文档中的节点再次插入,那个节点将自动从它当前位置删除并在新位置重新插入。
    <ul class="list" id="list">
        <li>1</li>
        <li>2</li>
    </ul>
    <script>
        var ul = document.getElementById('list');
        var liNode = document.createElement('li');
        liNode.style.background = 'pink';
        ul.insertBefore(liNode, null);//插入节点
        // ul.appendChild(liNode);
        var num = -1;
        var max = ul.children.length;
        function fn() {
            num++;
            ul.insertBefore(liNode, ul.getElementsByTagName('li')[num]);
            if (num == max) {
                num = -1;
            }
            if (num == 0) {
                num = 1;
            }
            setTimeout(fn, 1000);
        }
        setTimeout(fn, 1000);
    </script>

3.2 删除节点

  要移除节点可以使用parentNode.removeChild(childNode) 方法用于从父级中移除指定节点。参数是那个要被移除的节点,被移除的节点会被返回。当然我们也可以直接使用remove() 方法来删除节点,这个方法不调用父节点,直接在当前节点使用remove()方法就可无返回值。

    <button id="btn">删除节点</button>
    <ul>
        <li>1</li>
        <li>2</li>
        <li>3</li>
    </ul>
    <script>
        var ul = document.querySelector('ul');
        var btn = document.getElementById('btn');
        function fn() {
            //获取li个数
            var len = ul.getElementsByTagName('li').length;
            //如果长度不为0
            if (len) {
                //删除最后一个子元素
                ul.removeChild(ul.getElementsByTagName('li')[len - 1]);
                //再次调用计时器
                setTimeout(fn, 1000);
            }
        }
        btn.onclick = function () {
            setTimeout(fn, 1000)
        }
    </script>

3.3 克隆复制节点

    我们还可以通过parentNode.cloneNode(deep) 方法来要克隆指定节点。参数为true时克隆元素和元素所包含的子孙节点,当参数为false时只克隆当前节点默认为false。cloneNode()方法返回与调用它的节点一模一样的节点。复制的节点虽然属于文档,但它是一个孤儿节点,还需将其添加到文档中。需要注意的是这个方法只能克隆节点身上的html和css,节点身上所包含的javascript属性是不会克隆,如果克隆的元素本身有id属性,那克隆后的元素也会有id属性,两个一样,这样不符合标准,我们需要手动修改id属性。

    <ul id="list">
        <li>1</li>
    </ul>
    <script>
        var ul = document.getElementById('list');
        ul.index = 0;
        var deepList = ul.cloneNode(true);
        //成功复制了子节点
        console.log(deepList.children.length);//1
        //但并没有复制属性
        console.log(deepList.index);//undefined
        var shallowList = ul.cloneNode();
        //浅复制不复制子节点
        console.log(shallowList.children.length);//0
    </script>

3.4 替换节点

    parentNode.replaceChild(newNode,oldNode) 方法它接收两个参数,要插入的newNode和要替换的oldNode。newNode可以是文档中原本有的节点也可以是新创建的节点。要替换的节点会被返回并从文档树中完全移除,要插入的节点会取而代之。

    <input type="button" id="btn" value="替换">
    <ul>
        <li>a</li>
        <li>b</li>
        <li>c</li>
    </ul>
    <script>
        window.onload = function () {
            var btn = document.getElementById('btn');
            var lis = document.querySelectorAll('li');
            btn.onclick = function () {
                ul.replaceChild(lis[2], lis[0]);
            }
        }
    </script>

Document类型

1、基础知识

     Document类型是js中表示文档节点的类型,在浏览器中文档对象document是HTMLDocument的实例,表示整个html页面。document是window对象的属性,因此是一个全局对象。虽然Document类型还可以表示XML文档,但最常用还是通过HTMLDocument的实例取得document对象。document对象可以用于获取关于页面的信息以及操纵其外观和底层结构。 Document类型的节点有以下特点:

  • nodeType为9
  • nodeName为'#document'
  • nodeValue值为null
  • parentNode值为null
  • document.documentElement 指向html元素
  • document.body 指向body元素      document还可以向网页输出流中写入的内容。有:write()、writeIn()、open()、close()4个方法。还可以使用innerHTML属性设置或读取元素的html内容,或者是使用innerText属性,设置或读取元素的纯文本内容。
    <input type="button" id='btn' value="关闭"">
    <script>
        var btn = document.getElementById('btn');
        btn.onclick = function () {
            document.write('world');
        } 
    </script>

2、定位元素

描述方法返回值
通过id属性获取元素document.getElementById(id)选择一个基于唯一ID的元素
通过name属性获取元素document.getElementsByName(name)得到一个NodeList类数组对象
通过标签名称获取元素document.getElementsByTagName(tagName)得到一个HTMLCollection类数组对象
通过class属性获取元素document.getElementsByClassName(className)得到一个类数组对象
通过css选择器获取元素querySelector(selector)/querySelectorAll(selector)得到一个元素/得到一个类数组对象

Element类型

1、基础知识

     除了Document类型,Element类型是web开发中最常用的类型。Element类型的节点有以下特征:

  • nodeType为1
  • nodeName值为元素的标签名
  • nodeValue值为null
  • parentNode的值为Document或Element      我们可以通过nodeName或者是tagName属性来获取元素的标签名,这两个属性返回同样的值。
//在html中元素标签名始终以大写开头,但在xml中标签名和代码中的大小写一致,最好转换一下便于比较
if(element.tagName.toLowerCase() == 'div'){
    // do something 
}

2、方法

     每一个元素都有零个或者是多个属性,通常用于为元素或其内容附加更多信息。与属性相关的DOM方法主要有3个:getAttribute()、setAttribute()和removeAttribute() 这三个方法主要用于操纵属性,包括在HTMLElement类型上定义的属性。一般来说我们是要先用document.createElement() 对某个节点创建属性然后再对属性进行一系列的操作。

  • document.createAttribute()
    <div></div>
    <script>
        var attr = document.createAttribute('id');
        attr.value = "box";
        document.getElementsByTagName('div')[0].setAttributeNode(attr);
    </script>
  • element.getAttribute(attributename)通过属性名称获取某个节点属性的值,如果参数是一个src或者是href那它取到的结果就是引号里面的值(相对地址)。它取不到js的自定义属性,但是它可以取到html标签的自定义属性。

  • element.setAttribute(attributeName,attributeValue)设置属性。这里面的参数必须同时出现,如果属性已经存在,则setAttribute()会以指定的值替换原来的值,如果属性不存在则setAttribute()会指定的值创建该属性。可以设置自定义属性,也可以设置系统中的属性。

  • element.removeAttribute(属性名)删除属性。 我们还可以通过DOM修改html元素的样式。

  • 标签对象.style.css属性名称 = 值

  • 设置元素的class属性,能同时改变多个css属性

    <div id="box" n="myName" class="color" data-v="myName">
        <img id="pic" src="./img/1.jpg" />
    </div>
    <script>
        var box = document.getElementById('box');
        var pic = document.getElementById('pic');
        console.log(pic.getAttribute('src')) //./img/1.jpg
        //它取不到js自定义的属性
        box.index = 1;
        console.log(box.getAttribute('index')); //null
        //它可以取到html标签自定义属性
        console.log(box.getAttribute('n')); //myName
        console.log(box.dataset.v);//myName h5新增的,注意要以dat-开始
        box.setAttribute('id', 'box2');
        box.removeAttribute('style'); 
    </script>
</body>

Text类型

1、基础知识

     Text节点由Text类型表示,包含按字面解释的纯文本,也可能包含转义后的html字符,但不含html代码,Text类型的节点有以下特征:

  • nodeType为3
  • nodeName值为'#text'
  • nodeValue值就回节点中包含的文本
  • parentNode值为Element对象
  • 不支持子节点

2、方法

     网页上看到的文字内容都属于这个节点。文本节点由Text类型表示,包含的是纯文本内容,但是Text节点是对象类型。Text节点中包含的文本可以通过nodeValue属性访问,也可以通过data属性访问,这两个属性包含相同的值,修改nodeValue或data也会在另一个属性反映出来。文本节点暴露了以下操作文本的方法:

  • document.createTextNode() 创建新的文本节点
  • appendData(text)向节点末尾添加文本text
  • deleteData(offset,count) 从位置offset开始删除count个字符
  • insertData(offset,text) 在位置offset插入text
  • replaceData(offset,count,text) 用text替换从位置offset到offset+count的文本
  • splitText(offset),在位置offset将当前文本拆分为两个文本节点
  • substringData(offset,count),提取从位置offset到offset+count的文本      除了这些方法还可以通过length属性获取文本节点中包含的字符数量,这个值等于nodeValue.length和data.length。

     默认情况下,包含文本内容的每个元素最多只能有一个文本节点。不过也可以让元素包含多个文本子节点。

  <script>
        let element = document.createElement('div');
        element.className = 'box';
        let textNode = document.createTextNode('hello world!');
        element.appendChild(textNode);
        let anotherTextNode = document.createTextNode('你好啊,世界!')
        element.appendChild(anotherTextNode);
        document.body.appendChild(element);
        //在将一个文本节点作为另一个文本节点的同胞插入后,两个文本节点的文本之间不会出现空格
      var text = element.firstChild;
      console.log(text.nodeValue == text.data);//true
      console.log(text.length == text.data.length ) //true
    </script>

Attr类型

     元素数据在DOM中通过Attr类型表示,Attr类型构造函数和原型在所有浏览器中都可以直接访问。在技术上讲,属性是存在于元素attributes属性中的节点。Attr节点具有以下的特征:

  • nodeType为2
  • nodeName值为属性名
  • nodeValue值为属性值
  • parentNode值为null
  • 在html中不支持子节点      属性节点尽管是节点,但是却不被认为是DOM文档树的一部分,Attr节点很少直接被引用,一般是使用getAttribute(),removeAttribute()和setAttribute()方法操作属性。

Comment类型

     DOM中注释通过Comment类型表示。Comment类型的节点有以下特点:

  • nodeType为8
  • nodeName值为'#comment'
  • nodeValue值为注释的内容
  • parentNode值为Document或Element
  • 不支持子节点

DocumentType类型

     文档类型节点DocumentType包含文档类型(doctype)信息.以以下特点:

  • nodeType为10
  • nodeName值为文档类型的名称
  • nodeValue为null
  • parentNode是Document,
  • 不支持子节点

DocumentFragment类型

     在所有节点类型中, DocumentFragment 类型是唯一一个在标记中没有对应表示的类型。 DOM 将 文档片段定义为“轻量级”文档。能够包含和操作节点,却没有完整文档那样额外的消耗 。DocumentFragment 节点具有以下特征:

  • nodeType 等于 11;
  • nodeName 值为"#document-fragment";
  • nodeValue 值为 null;
  • parentNode 值为 null;
  • 不能直接把文档片段添加到文档

DOM编程

     在html中的每个节点都是一个DOM的实例对象,每类对象都有自己的属性和方法,最常用的是table表格和各类表单元素form对象

1、表单

     表单元素对象包括输入框对象slect对象,option对象,其它表单元素等。

1.1 form对象

属性:

名称描述
elements[]得到表单里所有的元素
action提交到服务器的url
id表单的id
name名称
length表单中元素的个数
target当前表单的跳转的目标
方法:
  • reset() 把表单的所有输入元素重置为它们的默认值
  • submit() 根据表单的action和method提交页面
    <form action="" id='myForm'>
        <input type="text">
        <button id='btn' onclick="formSubmit()">提交</button>
    </form>
    <script>
        function formSubmit() {
            document.getElementById('myForm').submit();
        }
    </script>

1.2 button对象

属性

名称描述
disabled设置或得到button是否无效的属性
form返回按钮所在表单
id设置或者得到按钮的id
name设置或者得到按钮的名称
value设置或得到按钮显示的值
方法
  • click() 点击触发的事件,在按钮上模拟一次鼠标单击
  • focus() 设置按钮的焦点

1.3 checkbox or radio对象

  • 属性
    • checked 设置或返回是否应被选中
  • 方法
    • blur(): 从checkbox上移开焦点
    • click(): 模拟在checkbox中的一次鼠标点击
    • focus():为checkbox赋予焦点

1.4 select对象

  • 属性
    • options[] 返回包含下拉列表中的所有选项的一个数组
    • selectedIndex 设置或返回下拉列表中被选项目的索引
    • length 下拉框中选项的个数
    • size 下拉框中的可见行数
  • 方法
    • add() 添加一个选项
    • remove() 删除一个选项

1.5 option对象

属性

  • index 返回下拉列表中某个选项的索引位置
  • selected 设置或查看该选项是选中与否
  • text 设置或查看选项的文本值
  • value 设置或查看选项代表的值

2、表格

    表格是html中最复杂的结构之一。通过DOM编程创建table元素,通常要涉及大量标签。包括行,单元格等等。HTML DOM给<table>,<tbody>和<tr>元素添加了一些属性和方法。

元素所有表元的 HTMLCollection
属性方法
<table>元素添加了以下属性和方法(常用):
tBodies包含<tbody>元素的HTMLCollection
rows包含表示所有行的HTMLCollection
insertRow(pos)在行集合中给定位置插入一行
deleteRow(pos)删除给定位置的行
<tbody>元素添加了以下属性和方法(常用):
rows包含<tbody>元素中所有行的 HTMLCollection
deleteRow(pos)删除给定位置的行
insertRow(pos)在行集合中给定位置插入一行,返回该行的引用
<tr>元素添加了以下属性和方法(常见):
cells包含
rowIndex返回该行在表中的位置(从0开始)
insertCell(pos)在表元集合给定位置插入一个表元,返回该表元的引用
deleteCell(pos)删除给定位置的表元
tablecell对象代表一个html表格单元格:
celllndex单元格在某行单元格集合中的位置
innerHTML设置或返回单元格的开始标签和结束标签之间的HTML

    这些属性和方法极大地减少了创建表格所需的代码量。

    <table id="table"></table>
    <script>
        var ary = ['id', 'name', 'age', 'sex']
        var table = document.getElementById('table');
        var tr = table.insertRow(0);
        for (var i = 0; i < ary.length; i++) {
            tr.insertCell().innerHTML = ary[i];
        }
        console.log('第一行第二个单元格内容是' + table.rows[0].cells[1].innerHTML)//第一行第二个单元格内容是name
    </script>