dom 兄弟
Complex, markup-heavy web apps are the norm nowadays. A library like jQuery with its ease of use, cross-browser compatibility, and variety of features can help immensely when manipulating HTML on the fly. So it’s no wonder that a lot of developers choose to use such a library rather than native DOM APIs which have been historically quite problematic. Although browser differences are still an issue, the DOM is in much better shape today than it was 5 or 6 years ago when jQuery was really starting to take off.
如今,复杂,大量标记的Web应用程序已成为常态。 像jQuery这样的易用性,跨浏览器兼容性和各种功能的库在即时处理HTML时可以提供极大的帮助。 因此,难怪很多开发人员选择使用这样的库而不是使用本来就存在问题的本机DOM API。 尽管浏览器之间的差异仍然是一个问题,但是与jQuery真正开始兴起的5或6年前相比,今天的DOM的状态要好得多。
In this post I’ll discuss and demonstrate a number of different DOM features that can be used to manipulate HTML, with a specific focus on parent, child, and sibling node relationships.
在这篇文章中,我将讨论并演示许多可用于操作HTML的不同DOM功能,并特别关注父,子和同级节点关系。
I’ll cover browser support in the conclusion, but keep in mind that a library like jQuery is still generally considered a good option, due to the bugs and inconsistencies in using these kinds of native features.
在结论中,我将介绍浏览器的支持,但请记住,由于使用此类本机功能存在错误和不一致,因此像jQuery这样的库仍通常被视为一个不错的选择。
计数子节点 (Counting Child Nodes)
Let’s use the following HTML, which I’ll be revisiting in various forms throughout this article:
让我们使用以下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>
If I want to count how many elements are inside the <ul>
element, there are two ways I can do that.
如果我想计算<ul>
元素中有多少个元素,可以通过两种方法进行。
var myList = document.getElementById('myList');
console.log(myList.children.length); // 6
console.log(myList.childElementCount); // 6
See the Pen Counting Child Elements: children & childElementCount by SitePoint (@SitePoint) on CodePen.
请参阅CodePen上的Pen 计数子元素: SitePoint( @SitePoint )的children& childElementCount 。
The logs in the last two lines produce the same result, but each with a different technique. In the first case, I’m using the children
property. This read-only property returns a collection of the HTML elements inside the element being queried and I’m using the length
property to find out how many are in the collection.
最后两行中的日志产生相同的结果,但是每条日志使用不同的技术。 在第一种情况下,我正在使用children
属性。 此只读属性返回要查询的元素内HTML元素的集合,而我正在使用length
属性来查找集合中有多少个HTML元素。
In the second example I’m using childElementCount
, which I think is a cleaner and potentially more maintainable way of doing this (in other words, revisiting this code later, you would likely not have any trouble figuring out what childElementCount
is doing).
在第二个示例中,我正在使用childElementCount
,我认为这是一种更清洁且可能更可维护的方式(换句话说,稍后再访问此代码,您可能会很容易确定出childElementCount
在做什么)。
Interestingly, I can also use childNodes.length
(instead of children.length
), but notice the result:
有趣的是,我也可以使用childNodes.length
(而不是children.length
),但是请注意结果:
var myList = document.getElementById('myList');
console.log(myList.childNodes.length); // 13
See the Pen Counting Child Nodes with childNodes.length by SitePoint (@SitePoint) on CodePen.
请参阅CodePen上的SitePoint ( @SitePoint ) 通过childNodes.length进行笔计数的子节点 。
This returns 13
because childNodes
is a collection of all nodes, including whitespace nodes – so keep that in mind if you require the ability to differentiate between all child nodes and all element nodes.
这将返回13
因为childNodes
是所有节点(包括空格节点)的集合-因此,如果您需要区分所有子节点和所有元素节点的能力,请记住这一点。
检查子节点的存在 (Checking the Existence of Child Nodes)
I can also use the hasChildNodes()
method to check if a given element has any child nodes. This method returns a Boolean to indicate whether it has child nodes or not:
我还可以使用hasChildNodes()
方法检查给定元素是否具有任何子节点。 此方法返回一个布尔值以指示它是否具有子节点:
var myList = document.getElementById('myList');
console.log(myList.hasChildNodes()); // true
See the Pen hasChildNodes() by SitePoint (@SitePoint) on CodePen.
请参阅CodePen上的SitePoint ( @SitePoint )的Pen hasChildNodes() 。
I know my list has child nodes, but I can alter the HTML to remove the elements inside the list so my HTML looks like this:
我知道我的列表有子节点,但是我可以更改HTML以删除列表中的元素,因此我HTML如下所示:
<body>
<ul id="myList">
</ul>
</body>
Here’s the result if I run hasChildNodes()
again:
如果再次运行hasChildNodes()
,则结果如下:
console.log(myList.hasChildNodes()); // true
See the Pen hasChildNodes() on empty element with white space by SitePoint (@SitePoint) on CodePen.
请参见CodePen上的SitePoint ( @SitePoint )在带有空白的空白元素上使用 Pen hasChildNodes() 。
Notice it still logs true
. Although the list doesn’t contain any elements, it does contain whitespace, which is a valid type of node. This particular method deals with nodes in general, not just element nodes. To ensure hasChildNodes()
returns false
, my HTML would have to look like this:
注意它仍然记录为true
。 尽管列表不包含任何元素,但它确实包含空格,这是节点的有效类型。 此特定方法通常处理节点,而不仅仅是元素节点。 为了确保hasChildNodes()
返回false
,我HTML必须如下所示:
<body>
<ul id="myList"></ul>
</body>
Now I get the expected result from my log:
现在,我从日志中得到了预期的结果:
console.log(myList.hasChildNodes()); // false
See the Pen hasChildNodes() on empty element with no white space by SitePoint (@SitePoint) on CodePen.
请参阅CodePen上的SitePoint ( @SitePoint )上没有空白的空白元素上的Pen hasChildNodes() 。
Of course, if I know I’m dealing with potential whitespace, I could first check for the existence of child nodes, then determine if any of them are element nodes using the nodeType property.
当然,如果我知道自己正在处理潜在的空白,则可以首先检查子节点的存在,然后使用nodeType属性确定其中是否有任何元素节点。
添加和删除子元素 (Adding and Removing Child Elements)
There are techniques we can use to add or remove nodes from the DOM. The most familiar, often used in conjunction with createElement()
, is the appendChild()
method:
我们可以使用一些技术来从DOM中添加或删除节点。 最常与createElement()
结合使用的最熟悉的方法是appendChild()
方法:
var myEl = document.createElement('div');
document.body.appendChild(myEl);
In this case I’m creating a <div>
using createElement()
, then I’m appending it to the body. Pretty straightforward, and it’s likely you’ve used that technique before.
在这种情况下,我将使用createElement()
创建<div>
,然后将其附加到主体。 非常简单,很可能您以前使用过该技术。
But instead of inserting a newly-created element, I could also use appendChild()
as a simple means of moving an existing element. Let’s say I have the following HTML:
但是,除了插入新创建的元素外,我还可以使用appendChild()
作为移动现有元素的简单方法。 假设我有以下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>Example text</p>
</div>
I can change the location of the list using the following code:
我可以使用以下代码更改列表的位置:
var myList = document.getElementById('myList'),
container = document.getElementById('c');
container.appendChild(myList);
The resulting DOM will look like this:
生成的DOM如下所示:
<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>
See the Pen Using appendChild() to change an element’s location by SitePoint (@SitePoint) on CodePen.
请参见使用appendChild()通过PenPen上的SitePoint( @SitePoint ) 更改元素位置的笔 。
Notice that the entire list was removed from its spot (above the paragraph) and then appended to the end of the body, placing it below the paragraph. So although you’ll commonly see appendChild()
used to add elements produced using the createElement()
method, it’s also an easy way to move an existing element to another location on the page.
请注意,整个列表已从其位置删除(在段落上方),然后追加到正文的末尾,并将其放在段落下方。 因此,尽管您通常会看到appendChild()
用于添加使用createElement()
方法生成的元素,但这也是将现有元素移动到页面上其他位置的一种简便方法。
I can also remove a child completely from the DOM using removeChild()
. Here’s how I can remove the same list element from the previous HTML snippet:
我还可以使用removeChild()
从DOM中完全删除一个孩子。 这是从先前HTML代码段中删除相同列表元素的方法:
var myList = document.getElementById('myList'),
container = document.getElementById('c');
container.removeChild(myList);
See the Pen Using removeChild() by SitePoint (@SitePoint) on CodePen.
请参阅CodePen上的SitePoint ( @SitePoint ) 使用removeChild()的笔。
Now the element is removed. The removeChild()
method returns the removed element, so I can store it somewhere if I need to do so for later use:
现在,该元素已删除。 removeChild()
方法返回已删除的元素,因此如果需要,可以将其存储在某个地方以备后用:
var myOldChild = document.body.removeChild(myList);
document.body.appendChild(myOldChild);
See the Pen Using removeChild() to move an element by SitePoint (@SitePoint) on CodePen.
见钢笔使用removeChild之()移动的元件由SitePoint( @SitePoint上) CodePen 。
There’s also the ChildNode.remove()
method, which is a newer feature in the spec:
还有ChildNode.remove()
方法,这是规范中的较新功能:
var myList = document.getElementById('myList');
myList.remove();
See the Pen Using childNode.remove() by SitePoint (@SitePoint) on CodePen.
请参阅CodePen上的SitePoint ( @SitePoint ) 使用childNode.remove()的笔。
This method doesn’t return the removed object and doesn’t work in Internet Explorer (only Edge). I should also note that both of these removal techniques will target text nodes as well as elements.
此方法不返回已删除的对象,并且在Internet Explorer(仅Edge)中不起作用。 我还应该注意,这两种删除技术都将针对文本节点以及元素。
替换子元素 (Replacing Child Elements)
I can replace an existing child with a new child, whether that new child exists somewhere already, or I’ve created it from scratch. Here’s the HTML:
我可以用一个新的孩子替换现有的孩子,无论这个新孩子已经存在于某个地方,还是我从头开始创建它。 这是HTML:
<p id="par">Example Text</p>
And then the JavaScript:
然后是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);
See the Pen Using replaceChild() by SitePoint (@SitePoint) on CodePen.
请参阅CodePen上的SitePoint ( @SitePoint ) 使用replaceChild()的笔。
Here I’m creating the element, appending a text node with createTextNode()
, then inserting it into the page in place of the paragraph element. The previous paragraph element is then replaced with the following HTML:
在这里,我正在创建元素,在文本节点上添加createTextNode()
,然后将其插入到页面中以代替段落元素。 然后,将上一个段落元素替换为以下HTML:
<div class="example">New element text</div>
As you can see, the replaceChild()
method takes two arguments: The new element and the old element that’s being replaced.
如您所见, replaceChild()
方法replaceChild()
两个参数:新元素和要替换的旧元素。
I can also use this method to move an existing element from its place and put it in place of a new one. Look at the following HTML:
我还可以使用这种方法将现有元素从其位置移开,然后将其替换为新元素。 查看以下HTML:
<p id="par1">Example text 1</p>
<p id="par2">Example text 2</p>
<p id="par3">Example text 3</p>
I can replace the third paragraph with the first paragraph using the following code:
我可以使用以下代码将第三段替换为第一段:
var myPar1 = document.getElementById('par1'),
myPar3 = document.getElementById('par3');
document.body.replaceChild(myPar1, myPar3);
Now the generated DOM will look like this:
现在,生成的DOM将如下所示:
<p id="par2">Example text 2</p>
<p id="par1">Example text 1</p>
See the Pen Using replaceChild() to swap one element for another by SitePoint (@SitePoint) on CodePen.
请参阅Pen在CodePen上使用SiteChild ( @SitePoint ) 使用replaceChild()将一个元素交换为另一个元素 。
定位特定的子元素 (Targeting Specific Child Elements)
There are a few different ways to target specific elements. As seen earlier, I can use the children
collection or the childNodes
property, and then work from there. But let’s see a few other options.
有几种针对特定元素的方法。 如前所述,我可以使用children
集合或childNodes
属性,然后从那里开始工作。 但是,让我们看看其他一些选择。
The firstElementChild
and lastElementChild
properties do exactly what their names’ suggest: they target the first and last child elements for a specified parent. Let’s go back to our HTML list:
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>
I can target the first or last elements in this list with the following:
我可以使用以下内容定位此列表中的第一个或最后一个元素:
var myList = document.getElementById('myList');
console.log(myList.firstElementChild.innerHTML); // "Example one"
console.log(myList.lastElementChild.innerHTML); // "Example six"
See the Pen Using firstElementChild and lastElementChild by SitePoint (@SitePoint) on CodePen.
见笔使用firstElementChild和lastElementChild由SitePoint( @SitePoint上) CodePen 。
I can also use the previousElementSibling
and nextElementSibling
properties if I want to target child elements other than the first or last. These can be used in conjunction with firstElementChild
and lastElementChild
:
如果我要定位第一个或最后一个以外的子元素,则也可以使用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"
See the Pen Using nextElementSibling and previousElementSibling by SitePoint (@SitePoint) on CodePen.
请参阅CodePen上使用 SitePoint( @SitePoint ) 使用nextElementSibling和previousElementSibling的钢笔 。
There are also the similar firstChild
, lastChild
, previousSibling
, and nextSibling
properties, but those properties deal with all types of nodes, not just element nodes. In many cases element-specific properties are going to be more useful because they only target element nodes.
还有类似的firstChild
, lastChild
, previousSibling
和nextSibling
属性,但是这些属性处理所有类型的节点,而不仅仅是元素节点。 在许多情况下,特定于元素的属性将更加有用,因为它们仅针对元素节点。
将内容注入DOM (Injecting Content into the DOM)
I’ve already looked at ways to add elements to the DOM. Let’s address a similar concept, and look at a few features that give you more fine-grained control over where you insert content.
我已经研究了将元素添加到DOM的方法。 让我们解决一个类似的概念,并查看一些功能,这些功能可让您对插入内容的位置进行更细粒度的控制。
First, there’s the straightforward insertBefore()
method. This works much like replaceChild()
, taking two arguments and it will work with a newly-created element or an existing one. Let’s use the following HTML:
首先,有一个简单的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>
Notice the paragraph element. I’m going to remove the paragraph, then insert it before the list, all in one fell swoop:
注意段落元素。 我将删除该段,然后将其插入列表之前,所有操作全部完成:
var myList = document.getElementById('myList'),
container = document.getElementBy('c'),
myPar = document.getElementById('par');
container.insertBefore(myPar, myList);
See the Pen Using insertBefore() by SitePoint (@SitePoint) on CodePen.
请参见CodePen上的SitePoint ( @SitePoint ) 使用insertBefore()的钢笔 。
The resulting HTML will have the paragraph element before the list, instead of after, which is yet another way to move an element from its existing spot to another place in the page:
生成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>
As with replaceChild()
, insertBefore()
takes two arguments: the element that’s being added and the element before which we want to add the element specified in the first argument.
与replaceChild()
, insertBefore()
接受两个参数:要添加的元素和我们要添加第一个参数中指定的元素的元素。
That method is fairly straightforward. Let’s now use a much more powerful method to get more precise with these insertions: the insertAdjacentHTML()
method.
该方法非常简单。 现在,让我们使用一种功能更强大的方法来更精确地进行这些插入: insertAdjacentHTML()
方法。
var myEl = document.getElementById('el');
myEl.insertAdjacentHTML('beforebegin', '<p>New element</p>');
The above code will insert the specified string of content before the targeted element (in this case, before the list). The second argument is the string of HTML that you want to insert (this could also just be text with no HTML tags). The first argument, also represented as a string, has to be one of the following four values:
上面的代码将在目标元素之前插入指定的内容字符串(在这种情况下,在列表之前)。 第二个参数是要插入HTML字符串(也可以是没有HTML标签的文本)。 第一个参数(也表示为字符串)必须是以下四个值之一:
- ‘beforebegin’ – Inserts the string before the specified element 'beforebegin'–在指定元素之前插入字符串
- ‘afterbegin’ – Inserts the string inside the specified element, before its first child 'afterbegin'–在指定元素的第一个子元素之前插入字符串
- ‘beforeend’ – Inserts the string inside the specified element, after its last child 'beforeend'–在指定元素的最后一个子元素之后插入字符串
- ‘afterend’ – Inserts the string after the specified element 'afterend'–在指定元素之后插入字符串
To make it easier to understand what each of these values represents, look at the HTML comments in the following snippet. Assuming the #el
div is the targeted element, each comment indicates where the HTML would go with the specified value for the first argument:
为了更容易理解这些值分别代表什么,请查看以下代码片段中HTML注释。 假设#el
div是目标元素,则每个注释都指示HTML将与第一个参数的指定值一起到达的位置:
<!-- beforebegin -->
<div id="el">
<!-- afterbegin -->
<p>Some example text</p>
<!-- beforeend -->
</div>
<!-- afterend -->
That should make it fairly clear what each of the possible values do with the string of content.
那应该很清楚地说明每个可能的值对内容字符串的作用。
See the Pen Using insertAdjacentHTML() by SitePoint (@SitePoint) on CodePen.
请参阅CodePen上的SitePoint( @SitePoint ) 使用insertAdjacentHTML()的钢笔 。
浏览器支持 (Browser Support)
When discussing anything in the DOM, browser support is always a big factor. Bugs aside, below is a rundown of how well these features are supported.
在讨论DOM中的任何内容时,浏览器支持始终是一个很大的因素。 除了错误,下面是对这些功能的支持程度的简要说明。
The following features are supported in all browsers, including old versions of IE:
所有浏览器 (包括旧版本的IE)都支持以下功能:
The following features are supported in IE9+ and all other browsers:
IE9 +和所有其他浏览器均支持以下功能:
That leaves only the Node.remove() method, which, as mentioned, has no support in Internet Explorer, but has been added to Microsoft’s new Edge browser.
剩下的只是Node.remove()方法,如上所述,该方法在Internet Explorer中不支持,但已添加到Microsoft的新Edge浏览器中。
So overall, browser support for these DOM features is excellent. Of course, there will be bugs and interoperability issues, so make sure you do thorough testing if you decide to make heavy use of any of these.
因此,总的来说,浏览器对这些DOM功能的支持非常出色。 当然,会存在错误和互操作性问题,因此,如果您决定大量使用其中的任何一个,请确保进行彻底的测试。
结论 (Conclusion)
Ideally, the JavaScript language and the native DOM APIs should be enough to allow us to build stuff easily and in a cross-browser manner. Eventually I think we will get to that point, but in the meantime, tools like jQuery will continue to fill this need.
理想情况下,JavaScript语言和本机DOM API应该足以让我们轻松地以跨浏览器的方式构建东西。 最终,我认为我们会做到这一点,但是与此同时,jQuery之类的工具将继续满足这一需求。
Feel free to comment on any related features that you’ve discovered or used when attempting to manipulate HTML on the fly with native DOM scripting.
在尝试使用本机DOM脚本动态处理HTML时,请随意评论您发现或使用的任何相关功能。
翻译自: https://www.sitepoint.com/dom-tips-techniques-parent-child-siblings/
dom 兄弟