为什么firstChild不返回第一个元素?

5

前几天我写了一些代码,出于某种原因,我不知道为什么会发生这种情况,但这是我编写的代码的一部分。

    var item = document.getElementByClass('object');
    var innerImageId = item.firstChild.id;

我已经反复检查了这段代码很多次。这段代码的问题是innerImageId的值未定义。item的第一个子元素是一个图像。我需要获取该图像的id,但无论我多少次检查代码都无法实现。以下是该代码的HTML文件。

    <div class="object inventory-box">
        <img src="image.png" id="sample-image">
    </div>

这看起来没问题吧?但是,我在Google Chrome中进行了一些调试,发现"object" div中有3个节点。第一个和最后一个节点的id未定义,但第二个节点是"sample-image"。 然后,我尝试使用"firstElementChild"而不是"firstChild",这个方法有效。

为了测试一下,我做了这样的操作-

    <div class="object inventory-box">



       <img src="image.png" id="sample-image">



    </div>

即使有多行不必要的空格,它仍然显示3个节点,即输入符号、div和另一个输入符号。 这个问题在Chrome、Internet Explorer和Firefox中不断发生。

请问有人能解释一下为什么会有这两个额外的随机节点吗?


在图像之前和之后有空文本节点。您可以通过删除标签之间的空格来修复HTML。--> <div><img /></div> - WhiteHat
@WhiteHat 我确实尝试过那个方法,但我忘了在问题中提到它。div 应该仍然只有一个节点,因为在浏览器中这样做太荒谬了。 - Ronit Joshi
@RonitJoshi 好的,那么让我们重新定义 DOM 已经工作了几十年的方式,这样它就不会对你显得荒谬了。空格是空格,必须被表示为节点。它被表示为一个节点。如果你对元素感兴趣而不是节点,你已经发现了一整套 API(例如 firstElementChild)-使用它们。MDN 文档关于 firstChild 很清楚地解释了这个问题。另外,请更仔细地编辑你的问题。var item = document.getElementByClass('object'); 会立即停止解释器,因为没有这样的 API。 - user663031
我投票关闭此问题,因为它询问的行为已在文档中明确描述。 - user663031
5个回答

8
浏览器会在代码中存在空格或新行时插入文本节点。您正在针对其中之一进行目标设置。
尝试:
var img = document.querySelector('.object img');
var innerImageId = img.id;

6
你说得对,他得到了一个文本节点。OP也可以将firstChild替换为firstElementChild以获得正确的结果。 - Noah Freitas
在IE和Safari上,firstElementChild不被支持,对吗? - L. Catallo
@LCatallo,当前版本的两个浏览器都支持它。看起来,在IE和Safari浏览器中,querySelector的支持版本要稍微早一些,分别是89,以及3.2和4.0。 - Noah Freitas
@NoahFreitas 你是正确的,我误读了 MDN 上的支持表。 - L. Catallo
1
HTML解析是有明确定义的,这不是“大多数浏览器”默认的行为。这是因为他们在它们之间添加了空格。 - Domenic

5

您还可以使用firstElementChild代替firstChild,这将跳过空文本节点。

编辑:感谢BAM5提醒,我在输入时注意力不集中。


3
  1. There isn't a getElementByClass method, this is why you failed. Open console and you'll see JS error. I guess what you thought were getElementsByClassName. Note it's in plural form, and it returns an array-like.

  2. Even if you get over the first issue, you may still fail due to your markup. firstChild does return the first one among children, including the text nodes. Your better choice is firstElementChild. Or you have to write like this to avoid text node children:

    <div><img></div>
    
所以,这应该给你想要的东西:
var items = document.getElementsByClassName('object');
var innerImageId = items[0].firstElementChild.id;

另一方面,其他答案指出 querySelector 确实可以完成任务。而且它还有一个兄弟 querySelectorAll。您可以在 MDN 上搜索它们的用法以及它们之间的轻微差异。


2

使用firstElementChild代替firstChild。正如l. Catallo所说,你可能得到的是一个文本节点而不是一个元素。

看起来Atheist已经超过我了,但在他的答案中错误地使用了firstChildElement。想评论,但是没有足够的声望...


谢谢。我只是在打字时没有注意到。=] - AtheistP3ace
1
哦,太好了,我可以评论自己的答案!没问题,我经常这样做 :P - BAM5
@BAM5 - 我知道firstElementChild方法,但我认为浏览器将这样的东西视为文本节点实在是非常荒谬。 - Ronit Joshi
我的意思是,你应该能够以任何你想要的风格编程,是吗? - Ronit Joshi
@RonitJoshi 如果您是正确的,那么想要获取文本节点的人应该怎么做? - Asons
1
@RonitJoshi 绝对可以用任何你喜欢的编程风格进行编程。我个人使用 K&R 风格,但那只是代码格式而已。像浏览器之间的这种差异就是为什么需要属性,例如 firstElementChild 的存在,并且不使用它而只因为你喜欢 firstChild 而导致你的代码崩溃是很愚蠢的:P 另外,我认为在 HTML 中的任何字符都会创建一个文本节点。如果你将元素放在文本中的紧挨着(例如 <div><div>),则不会有文本节点。添加换行符和空格也算作文本节点。 - BAM5

0
我在学习DOM探索时遇到了类似的问题,我发现当存在空格或下一个标签在下一行时,节点列表通常会显示文本节点。例如: 如果我像下面这样编写我的HTML代码,并尝试获取元素body的所有子节点。
HTML代码:
<body>
    <h2> Lets Study DOM!! </h2>
    <input type = 'text' id = 'topic'> </input></br>
    <button onClick = addToList()> Click Me to Add </button>

Javascript:

console.log(document.querySelector('body').childNodes); 

上述代码给了我以下输出:
[text, h2, text, input#topic, text, br, text, button, text, ul#unordered, text, script]

另一方面,如果我修改了下面所示的代码,然后尝试打印节点列表,我会得到不同的输出,如下所示:
HTML 代码:
<body><h2> Lets Study DOM!! </h2>
        <input type = 'text' id = 'topic'> </input></br>
        <button onClick = addToList()> Click Me to Add </button>

Javascript:

console.log(document.querySelector('body').childNodes);

上述代码给了我以下输出:
[h2, text, input#topic, text, br, text, button, text, ul#unordered, text, script]

很明显,浏览器将body标签后的空格视为文本节点。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接