复杂,沉重的web应用在现在是常态,想jquery这样的易于使用的库,跨浏览器的兼容性,各种各样的功能,在操作HTML上非常有帮助。所以难怪很多开发者选择使用这样的库,而不是过去有很多问题原生的DOM API。虽然浏览器的差异仍然是一个问题,今天的DOM是比5年或6年前jQuery刚开始流行时更好。
在这篇文章中,我将讨论和展示一些不同的DOM的功能,可以用来操纵HTML,主要聚焦在父元素,子元素和同级节点的关系上。
在最后还是介绍到浏览器的支持兼容情况。但是有一点需要注意的是像jquery这样的库仍然还是一个不错的选择。因为在使用原生的特性的时候会有很多的bug和冲突。
计算子节点数
我们用下面的HTML结构,我也会不断以各种形式在这篇文章中使用:
- <body>
- <ul id="myList">
- <li>Example one</li>
- <li>Example two</li>
- <li>Example three</li>
- <li>Example four</li>
- <li>Example five</li>
- <li>Example Six</li>
- </ul>
- </body>
如果我想计算在上边的结构里边<ul>元素的数量,有两种方法我可以做到。
- var myList = document.getElementById(‘myList‘);
- console.log(myList.children.length);//6
- console.log(myList.childElementCount);//6
最后两行代码在控制台显示同样的结果,但是是不同的技术,在第一种情况下,我用的是children属性,这个只读属性返回被查询的HTML元素的下一级元素,和我使用的长度属性来找出子元素的数量。
在第二个实例中,我用的是childElementCount,我认识这种方法是处理这种事件最简洁,高效的一种形式(换句话说,这段代码看起来非常明了)。
同样的,我还可以使用childNodes.length 方法替代children.length,但是结果会有所不同:
- var myList = document.getElementById(‘myList‘);
- console.log(myList.childNodes.length); // 13
因为childNodes返回所有的节点包括空白节点,所以返回值是13。在你使用时,时刻注意所有子元素节点和所有元素节点的区别。
检查子元素节点的存在
同样我也可以使用hasChildNodes()方法来检查给定的元素是否有子元素节点。这种方法返回一个布尔值来判断是否存在子节点。
- var myList = document.getElementById(‘myList‘);
- console.log(myList.hasChildNodes()); // true
我知道我的列表有子节点,但是我可以改变HTML结构通过删除子节点的元素。删除之后变成:
- <body>
- <ul id="myList">
- </ul>
- </body>
再次使用hasChildNodes()方法运行一下结果是:
- console.log(myList.hasChildNodes()); // true
发现仍然显示true。尽管这个列表不包含任何元素,但是他包含空白节点,也是众多节点的一种。这种特殊的方法只适合处理一般的节点,不适用元素节点。为确保haschildnodes()返回false,HTML应该这样写:
- <body>
- <ul id="myList"></ul>
- </body>
现在得到了自己期望的结果:
- console.log(myList.hasChildNodes()); // false
当然,如果我知道我处理的有潜在的空白,我会先检查子节点是否存在,然后用nodeType属性判断他们是不是元素节点。
增加和删除子元素
我们可以使用的从DOM中增加或者删除节点的技术有很多。最常见的,经常是createElement()和appendChild()方法结合使用。
- var myEl = document.createElement(‘div‘);
- document.body.appendChild(myEl);
在这个案例中,我用creatElement()创建了一个<div>,然后添加到body中,非常简单,很有可能是你以前使用过的技术。
appendChild()除了可以新创建一个元素外,还可以使用它实现一个简单的移动已经存在的元素。看下边的例子:
- <div id="c">
- <ul id="myList">
- <li>Example one</li>
- <li>Example two</li>
- <li>Example three</li>
- <li>Example four</li>
- <li>Example five</li>
- <li>Example Six</li>
- </ul>
- <p>Example text</p>
- </div>
用下边的代码可以改变列表的位置:
- var myList = document.getElementById(‘myList‘),
- container = document.getElementById(‘c‘);
- container.appendChild(myList);
运行后结果是这样:
- <div id="c">
- <p>Example text</p>
- <ul id="myList">
- <li>Example one</li>
- <li>Example two</li>
- <li>Example three</li>
- <li>Example four</li>
- <li>Example five</li>
- <li>Example Six</li>
- </ul>
- </div>
注意到整个列表被移动到p元素的下边。尽管经常看到appendChild()被用来和createElement()所创建的元素来增加元素,他同样是移动页面中已经存在的元素的一种简单的方法。
我也可以通过removeChild()方法来实现从DOM中移除元素,给出从上边的html代码中删除的实例。
- var myList = document.getElementById(‘myList‘),
- container = document.getElementById(‘c‘);
- container.removeChild(myList);
现在元素已经被移除了,这个removeChild()方法可以返回被删除的元素,因此可以将它保存,以后使用的时候再调用。
- var myOldChild = document.body.removeChild(myList);
- document.body.appendChild(myOldChild);
同样也有ChildNode.remove()方法,一个比较新的特性。
- var myList = document.getElementById(‘myList‘);
- myList.remove();
这种方法不会返回已经被删除的对象,并且IE浏览器不支持(除了Edge)。需要注意的是两种移除元素的技术都会包含文本节点和元素节点。
替换子元素
我可以用一个新的子元素替换一个已经存在的元素。不管是新的子元素是否已经存在,或者我可以创建一个新的。下边是html代码:
- <p id="par">Example Text</p>
javascript代码:
- var myPar = document.getElementById(‘par‘),
- myDiv = document.createElement(‘div‘);
- myDiv.className = ‘example‘;
- myDiv.appendChild(document.createTextNode(‘New element text‘));
- document.body.replaceChild(myDiv, myPar);
这是我创建一个元素,用createTextNode()添加一个文本节点,然后插入到段落元素中,之前的段落元素就会被替换成下边的html:
- <div class="example">New element text</div>
正如你所见,这个replaceChild()方法传进去两个参数:旧元素和新元素被取代。
用这种方法同样可以实现让一个存在的元素从他原来的位置到新的位置,看下边的html代码:
- <p id="par1">Example text 1</p>
- <p id="par2">Example text 2</p>
- <p id="par3">Example text 3</p>
用下边的代码实现第一段替换第三段:
- var myPar1 = document.getElementById(‘par1‘),
- myPar3 = document.getElementById(‘par3‘);
- document.body.replaceChild(myPar1, myPar3);
改变过后的DOM结构:
- <p id="par2">Example text 2</p>
- <p id="par1">Example text 1</p>
获取特定的子元素
有一些不同的方法来获取特定的元素,之前的例子中,可以用children或者childNodes属性来获取,我们来看一下其他的一些方法,
firstElementChild 和 lastElementChild属性 顾名思义是获取第一个子元素和最后一个子元素。看一下html代码:
- <body>
- <ul id="myList">
- <li>Example one</li>
- <li>Example two</li>
- <li>Example three</li>
- <li>Example four</li>
- <li>Example five</li>
- <li>Example Six</li>
- </ul>
- </body>
用以下代码获取第一个或者最后一个元素:
- var myList = document.getElementById(‘myList‘);
- console.log(myList.firstElementChild.innerHTML); // "Example one"
- console.log(myList.lastElementChild.innerHTML); // "Example six"
使用previousElementSibling 和 nextElementSibling属性 来获取非第一个或者最后元素的子元素。这些属性可以结合firstElementChild 和 lastElementChild使用:
- var myList = document.getElementById(‘myList‘);
- console.log(myList.firstElementChild.nextElementSibling.innerHTML); // "Example two"
- console.log(myList.lastElementChild.previousElementSibling.innerHTML); // "Example five"
还有一些相似的属性,例如:firstChild, lastChild, previousSibling, 和 nextSibling。但是这些属性可以处理所有节点的类型。不仅仅是元素节点类型。在很案例中,获取特定元素的属性比较实用,因为他们仅获取元素节点。
给DOM添加内容
我们已经知道在DOM中添加元素,我们看一个相似的概念,和一些插入内容控制的特性。
首先,最直接的方法是insertBefore(),他跟replaceChild()类似,有两个参数,并且和一个新创建的元素一起使用,或者和一个已存在的元素。html代码:
- <div id="c">
- <ul id="myList">
- <li>Example one</li>
- <li>Example two</li>
- <li>Example three</li>
- <li>Example four</li>
- <li>Example five</li>
- <li>Example Six</li>
- </ul>
- <p id="par">Example Paragraph</p>
- </div>
注意段落元素,我想要移除这个段落,然后放在列表前边。下边的例子:
- var myList = document.getElementById(‘myList‘),
- container = document.getElementBy(‘c‘),
- myPar = document.getElementById(‘par‘);
- container.insertBefore(myPar, myList);
这个html结果显示段落元素在列表元素前边。另一种方法从已有的元素节点移动到页面的其他位置。
- <div id="c">
- <p id="par">Example Paragraph</p>
- <ul id="myList">
- <li>Example one</li>
- <li>Example two</li>
- <li>Example three</li>
- <li>Example four</li>
- <li>Example five</li>
- <li>Example Six</li>
- </ul>
- </div>
replaceChild(), insertBefore() 都有两个参数:前者用新节点替换某个子节点,insertBefore() 方法在您指定的已有子节点之前插入新的子节点。
该方法是相当简单的。现在让我们用一个更强大的方法来获得插入:
这个insertAdjacentHTML()方法。
- var myEl = document.getElementById(‘el‘);
- myEl.insertAdjacentHTML(‘beforebegin‘, ‘<p>New element</p>‘);
上面的代码将在目标元素(该案例中,在列表之前)插入指定的内容。第二个参数是你要插入的html代码字符串(也可以是纯文本)第一个参数,也表示为一个字符串,必须是以下四个值之一:
- beforeBegin:在该元素前插入
- afterBegin:在该元素第一个子元素前插入
- beforeEnd:在该元素最后一个子元素后面插入
- afterEnd:在该元素后插入
为了让每个值所代表的含义更容易理解,看下边的注释。假设# EL div是有针对性的元素,每个评论显示在HTML会与第一个参数指定的值:
- <!-- beforebegin -->
- <div id="el">
- <!-- afterbegin -->
- <p>Some example text</p>
- <!-- beforeend -->
- </div>
- <!-- afterend -->
这应该很清楚地知道,每一个可能的值与字符串的内容是什么样的。
浏览器支持情况:
当讨论DOM的时候,浏览器始终是一个很大的因素。一边是bug,下面是一个演示如何以及这些功能的支持。
所有浏览器都支持以下功能,包括旧版本:
- childNodes
- hasChildNodes()
- appendChild()
- removeChild()
- replaceChild()
- createTextNode()
- previousSibling
- nextSibling
- insertBefore()
- insertAdjacentHTML()
IE9+和其他浏览器都支持的特性:
- children
- childElementCount
- firstElementChild
- lastElementChild
- previousElementSibling
- nextElementSibling
remove()方法,其中,如上所述,在IE浏览器不支持,但已被添加到新的微软的Edge浏览器。
总的来说,对DOM的操作特性,浏览器支持还是比较好的。当然,也会有一些冲突和bug ,所以在你使用某些特性的时候,做好做一下测试。
结论
理想情况下,JavaScript语言和本地的DOM API应该是让我们在跨浏览器的方式下更容易进行开发。最终我认为我们会做到这一点,但同时,如jQuery这样的工具将继续填补这方面的需要。
可以随意的对你发现的或正在使用的相关特性发表看法,当你在试图操纵本地HTML DOM脚本的时候。
原文链接:http://www.gbtags.com/gb/share/9532.htm