getElementsByClassName返回[]而不是异步添加的节点

4

我在第一个问题表述糟糕后再次提问:

我面临以下问题:

<div class="testA" id="test1"></div>

上述元素是预定义的。现在,我通过XMLHttpRequest & Co.加载一个XML树,返回以下响应:

<response>
    <div class="colorSelector" id="0-0">
        <div class="gbSelector" id="1-0">
            <table style="none" id="2-0"></table>
        </div>
    </div>
</response>

我现在使用以下代码添加第一个 div
request.responseXML.getElementsByTagName("response")[0]
                       .getElementsByTagName("div")[0]

将内容放入预定义的 div 中。
<div class="testA" id="test1">

最终的文档看起来像这样(使用开发工具进行检查):
<div class="testA" id="test1">
    <div class="colorSelector" id="0-0">
        <div class="gbSelector" id="1-0">
            <table style="none" id="2-0"></table>
        </div>
    </div>
</div>

当我尝试使用getElementById("0-0")获取元素<div class="colorSelector" id="0-0">时,得到了预期的结果。
但是,使用getElementsByClassName("colorSelector")返回了[]
我错过了什么吗?这可能是节点类型为Element而不是HTMLElement的遗留问题吗?

这些节点实际上被注释了吗?如果没有,您从哪个上下文调用了getElementsByClassName - user113716
1
真正的问题是,为什么 GetElementById 会获取到被注释掉的内容?你确定它返回了什么吗? - ThatMatthew
1
@ThatMatthew:OP已经验证.getElementsByClassName返回[],这可能意味着控制台正在被用于验证。我猜测要么代码注释实际上不存在,要么页面上有另一个具有相同ID的元素。 - user113716
是的,那也是我的两个猜测。 - ThatMatthew
这个主题为什么被标记为 XML?我猜你需要提供一个演示,目前只有两个选项:两种方法都返回结果或者都不返回。 - Dr.Molle
显示剩余5条评论
6个回答

3

colorSelector被注释掉了。JavaScript只能在DOM内工作,而被注释掉的部分不在DOM中。


使用 getElementById("0-0") 我得到了预期的结果。 - user113716
注释节点肯定在 DOM 中。但是,它们被视为 CDATA。 - user1385191
1
对我来说看起来并没有被注释掉。 - Lightness Races in Orbit

1

既然你说你的getElementById("0-0")成功了,那么显然你实际上没有将节点注释掉。

我猜你正在做:

document.getElementById("0-0").getElementsByClassName('colorSelector');

...这样是行不通的,因为根据ID选中的元素没有任何具有该类的后代元素。


由于您在标记中显示了HTML注释,我还想知道页面上是否有某个具有ID "0-0" 的不同元素。请查看一下。


如果你的节点实际上是被注释掉的,你需要先选择注释部分,并将其替换为其中包含的标记。
var container = document.getElementById('test1'),
    comment = container.firstChild;

while( comment && comment.nodeType !== 8 ) {
    comment = comment.nextSibling;
}

if( comment ) {
    container.innerHTML = comment.nodeValue;
}

...导致:

<div class="testA" id="test1">
    <div class="colorSelector" id="0-0">
        <div class="gbSelector" id="1-0">
            <table style="none" id="2-0"></table>
        </div>
    </div>
</div>

...但是再说一遍,这似乎不太可能,因为你的getElementsById确实起作用。


我确定没有任何元素使用这个ID。请注意我的注释(抱歉)。 - Leo Selig

0
<!--<div class="colorSelector" id="0-0">
        <div class="gbSelector" id="1-0">
            <table style="none" id="2-0"></table>
        </div>
    </div>-->

上面的代码是灰色的,这是有原因的:它是一条注释。注释不会被浏览器解析,对页面没有任何影响。
你需要解析HTML,读取注释,并使用注释内容创建一个新的DOM对象。

使用 getElementById("0-0") 可以得到预期的结果。 - user113716

0

以下是针对 Firefox、Opera、Chrome和 Safari 的实现方式。基本上,您只需执行 div.innerHTML = div.innerHTML 以重新解释其内容为 HTML,这将使 XML 文件中的 class 属性被视为 HTML 类名。

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title></title>
        <script>
            window.addEventListener("DOMContentLoaded", function() {
                var div = document.getElementsByTagName("div")[0];
                var req = new XMLHttpRequest();
                req.onreadystatechange = function() {
                    if (this.readyState === 4 && this.status === 200) {
                        var doc = this.responseXML;
                        div.appendChild(document.importNode(doc.getElementsByTagName("response")[0].getElementsByTagName("div")[0], true));
                        div.innerHTML = div.innerHTML;
                        alert(document.getElementsByClassName("colorSelector").length);
                    }
                };
                req.open("GET", "div.xml");
                req.send();
            }, false);
        </script>
    </head>
    <body>
        <div class="testA"></div>
    </body>
</html>

如果您在支持本地xhr的浏览器中进行本地测试,则可以删除this.status === 200。

importNode()函数似乎在IE(例如9)中无法正常工作。我收到一个模糊的“接口不受支持”的错误。

您也可以这样做:

var doc = this.responseXML;
var markup = (new XMLSerializer()).serializeToString(doc.getElementsByTagName("response")[0].getElementsByTagName("div")[0]);
div.innerHTML = markup;

只要标记对于空元素的结束标签是HTML友好的,就可以了。

我尝试了这个,但令人困惑的是,当使用document.getElementsByClassName("colorSelector")时,我仍然只得到[]返回值,而使用document.getElementById("0-0")却可以正常工作。 - Leo Selig
@Leo Selig,我不小心写成了this.responseXML.document而不是只有this.responseXML。在Firefox、Opera、Chrome和Safari中可以工作,但在IE9中似乎无法工作。可能与importNode有关。 - Shadow2531
@Leo Selig,我添加了另一种方法来完成它。 - Shadow2531

0
请描述您对返回结果的操作。nodeList和node之间存在显着差异,nodeList是实时的。
因此,如果您将由getElementsByClassName()(或类似方法)返回的nodeList分配给变量,则当您从DOM中删除nodeList中的节点时,该变量将发生更改。

我有一个像这样的函数:handleTmpl = function(tmplNode) { element = parentEl.appendChild($t("div", 0, tmplNode)); var bgPixels = element.getElementsByClassName("gbSelector")[0]; }其中tmplNode是通过处理响应接收到的节点 现在,element.getElementsByClassName("gbSelector")[0]返回undefined,尽管开发工具清楚地显示了代码中的完整树 - Leo Selig
现在有点混淆了,你想要访问哪个节点,.gbSelector 还是 .colorSelector?然而,再看一遍 Ӫ_._Ӫ 的答案。在你的函数内部,element 将是 #0-0(appendChild 返回已添加的元素),所以 element.getElementsByClassName("colorSelector") 不会返回任何东西,因为 .colorSelector 不是 element 的子节点,而是 element 本身。 - Dr.Molle

0

我现在附加第一个div

你怎么做到的?在responseXML中,你拥有的是XML元素,而不是HTML元素。

  • 你不能将它们appendChild到非XHTML HTML文档中;

  • 实际上,你不能将它们appendChild到另一个文档中,你应该使用importNode从一个文档获取元素到另一个文档中,否则你会得到WRONG_DOCUMENT_ERR

  • 即使你由于浏览器的宽松性而设法将它们插入到HTML中,它们仍然是XML元素,不是语义上的HTML元素。因此,class属性并没有什么特别之处;只是拥有这个名称并不意味着该属性实际上代表一个类。 getElementsByClassName不会返回具有名称class的属性的元素;它们必须是其语言定义将属性与类概念相关联的元素(通常是HTML、XHTML或SVG)。

对于id属性也是如此;仅仅拥有一个名为id的属性并不意味着它在概念上就是一个ID。因此,getElementById不应该起作用。有一种方法可以将任意的XML属性与ID关联起来,而使用类则无法实现,这可以通过在文档类型中使用<!ATTLIST声明来实现。虽然通常不值得费心去做。另外,在支持XML ID的实现中,xml:id是一个特殊情况。

如果您正在使用本地XHTML页面,并在内容上放置适当的xmlns属性以使其成为实际的XHTML而不仅仅是任意的XML,然后使用importNode,您可能会使其工作。但总的来说,这并不值得,更简单的方法是返回HTML标记字符串(通常为JSON)或原始XML数据,客户端脚本可以根据这些数据构建HTML元素。


非常感谢,我原以为混淆XML和HTML节点会导致这种情况,但我不确定具体原因。 - Leo Selig
另一个问题是:您认为我应该如何从字符串(我更喜欢没有innerHTML的解决方案)或XML数据构建HTML元素?还是您认为需要完全“重新创建”XML节点作为HTML元素? - Leo Selig
importNode 应该能够将 XML 节点重新创建为 HTML 节点,但是在浏览器中存在困难(特别是 IE 中,你甚至无法获取该方法)。我认为我不会尝试将 HTML 元素放置在 XML 中作为元素;这两种语言实在是太不同了,除非你在整个应用程序中使用 XHTML(在这种情况下,你会遇到 IE<9 的问题)。因此,你需要使用字符串(在 XML 中编码为 <response>&lt;div class=... 或者在 JSON 响应中)。 - bobince
如果您不喜欢使用 innerHTML,那么答案不是在服务器端生成标记,而是返回简单数据(例如 XML 中的 <response><id>1</id><color>red</color></response> 或 JSON 以节省空间),然后使用 DOM 方法创建必要的 div 和表格。 - bobince
好的,非常感谢!看起来innerHTML没有真正的替代方案,所以我想我会使用它。 @bobince在处理AJAX请求时,我也使用了类似的方法,但在这种情况下,思路是通过AJAX后载入模板文件,所以这不起作用,但还是非常感谢! - Leo Selig

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