发生了啥?一天没问题,第二天“未定义”?

Posted

技术标签:

【中文标题】发生了啥?一天没问题,第二天“未定义”?【英文标题】:Whats happening? One day its OK, the next day its 'undefined'?发生了什么?一天没问题,第二天“未定义”? 【发布时间】:2011-08-21 00:43:10 【问题描述】:

我正在编写一个greasemonkey 脚本。最近我两次遇到同样的问题,我不知道为什么会这样。

function colli()
.....
var oPriorityMass = bynID('massadderPriority');//my own document.getElementById() function
var aPriorities = [];
if (oPriorityMass) 
    for (var cEntry=0; cEntry < oPriorityMass.childNodes.length; cEntry++) 
        var sCollNumber = oPriorityMass.childNodes[cEntry].getAttribute('coll');
        if (bynID('adder' + sCollNumber + '_check').checked)
            aPriorities.push(parseInt(sCollNumber));
    

.....

所以这其中的奥秘在于,有一天我将oPriorityMass 命名为oPririoty。它工作正常,但整个功能尚未完成,我开始为我的脚本开发另一个功能。这些功能之间没有任何联系。

几天后,我决定回到上面示例中的函数并完成它。我在没有修改任何内容的情况下对其进行了测试,并在 Firefox 的 (4) javascript 错误控制台中出现错误,提示 oPriority.chilNodes[cEntry] is undefined。注意,几天前我以完全相同的方式对其进行了测试,完全没有这样的问题。

好的,所以,我决定将 oPriority 重命名为 oPriorityMass。神奇的是,问题解决了。

起初我想,也许有 2 个对象有一些冲突,在不同的函数中使用了相同的名称,即使在函数范围之外,它们也以某种方式继续存在。我的脚本目前超过 6000 行,但我进行了搜索,发现 oPriority 没有在其他任何地方提及,而是在这个确切的函数中。

有人可以告诉我,这是如何发生的以及为什么会发生这种情况?我提到同样的事情现在发生了两次,它们发生在不同的功能中,但同样的问题 node.childNodes[c] is undefinednode 不是 null 并且 node.childNodes.length 显示正确的子数。 到底是怎么回事?如何避免此类问题?

谢谢

编辑:错误控制台给出的错误是 Error: uncaught exception: TypeError: oPriorityMass.childNodes[cEntry] is undefined

回应布洛克斯的评论: GM_log(oPriorityMass.childNodes[cEntry]) 返回 undefined 作为消息。所以node.childNodes[c] 通常是未定义的东西。

我的脚本创建了一个 div 窗口。后来上面的函数用到了这个div里面的元素。元素确实有唯一的 ID,我 100% 确定原始站点不知道它们。 我的脚本有一个开始/停止按钮,可以在需要时运行一个或另一个功能。 我现在一直在刷新页面并运行我的脚本功能。我注意到有时(但并非总是)脚本在第一次运行时会因描述的错误而失败,但是,如果我再次运行它(不刷新页面)它就会开始工作。

该页面有一个可以修改它的 javascript。它改变了它的一些元素宽度,所以当浏览器调整大小时它会改变。但我知道它对我的 div 没有影响,因为当我调整浏览器大小时它保持不变。

EDIT2

function bynID(sID) 
    return top.document.getElementById(ns(sID));

function ns(sText) 
    return g_sScriptName + '_' + sText;

ns 函数只是在 ID 前面添加脚本名称。我在创建 html 元素时使用它,因此我的元素永远不会与网页具有相同的 id。所以 bynID() 是一个简单的函数,当我需要通过 ID 获取元素时,它可以节省一些打字时间。

我已经修改了我的colli() 函数以包含检查

if (oPriorityMass) 
    if (!oPriorityMass.childNodes[0]) 
        GM_log('Retrying');
        setTimeout(loadPage,2000);
        return;
    
    for (var cEntry=0; cEntry < oPriorityMass.childNodes.length; cEntry++) 
        var sCollNumber = oPriorityMass.childNodes[cEntry].getAttribute('coll');
        if (bynID('adder' + sCollNumber + '_check').checked)
            aPriorities.push(parseInt(sCollNumber));
    

loadPage 函数执行 1 次 AJAX 调用,然后我对其运行了几个 XPATH 查询,但实际内容从未附加/显示在页面上,只是保留在 document.createElement('div') 中,然后此函数调用 colli()。所以现在,当我修改了我的函数时,我检查了错误控制台,发现它可能需要 5 次尝试才能开始正常工作。 5 x 2 秒,也就是 10 秒。永远不会总是重试 5 次,可能会有所不同 一定是发生了其他事情吗?

【问题讨论】:

node.childNodes[c] 未定义,还是node.childNodes[c].getAttribute('coll')(等)未定义? ... 页面是否被 any JavaScript 修改? ...我们将需要一个特定的失败配方或完整脚本代码的链接,以在这方面提供更多帮助。 我已经编辑了我的帖子,我希望它现在能提供更多信息。 try-catch 块返回与没有 try-catch 块完全相同的错误。 异常是在第一次到达该行时抛出还是在多次循环后抛出?有没有可能孩子们没有从 0 开始按顺序编号? @Basiclife 第一次。即使我将 for 循环更改为 for (var cEntry in oPriorityMass.childNodes),也会引发错误。在这种情况下,cEntry 是什么,理论上它必须工作,但它第一次出错并在第二次运行时工作。 这听起来像是修改页面的竞争条件或bynID() 中的错误。 (如果没有完整的细节很难确定。)将代码发布到 bynID() 并尝试在短暂延迟(500 到 2000 毫秒)后调用 colli() 函数。 【参考方案1】:

在 Firefox 中,childNodes 可以包含#text 节点。在尝试调用它之前,您应该检查以确保 childNodes[cEntry] 具有 nodeType == 1 或具有 getAttribute 方法。例如

<div id="d0">
</div>
<div id="d1"></div>

在上面的 Firefox 和类似浏览器(即基于 Gecko 和基于 WebKit 的浏览器,如 Safari)中,d0 有一个子节点,一个文本节点,而 d1 没有子节点。

所以我会这样做:

var sCollNumber, el0, el1;

if (oPriorityMass) 
    for (var cEntry=0; cEntry < oPriorityMass.childNodes.length; cEntry++) 
        el0 = oPriorityMass.childNodes[cEntry];

        // Make sure have an HTMLElement that will
        // have a getAttribute method
        if (el0.nodeType == 1) 
          sCollNumber = el0.getAttribute('coll');
          el1 = bynID('adder' + sCollNumber + '_check');

         // Make sure el1 is not falsey before attempting to
         // access properties
         if (el1 && el1.checked)

            // Never call parseInt on strings without a radix
            // Or use some other method to convert to Number
            aPriorities.push(parseInt(sCollNumber, 10));
    

鉴于sCollNumber 似乎是一个字符串整数(只是猜测,但似乎很可能),您也可以使用:

Number(sCollNumber)

+sCollNumber

哪个适合并且更易于维护。

【讨论】:

【参考方案2】:

所以,根据您上次的编辑,它现在可以工作了,但有延迟,对吧?

但是当我建议延迟时,它并不是要在等待时执行(甚至更多?)ajax 调用!!

if (!oPriorityMass.childNodes[0]) 
    GM_log('Retrying');
    setTimeout(loadPage,2000);
    return;

更像:

setTimeout (colli, 2000);

所以 loadPage 所做的 ajax 和其他东西可以解释过度延迟。

随机行为可能由以下原因引起:

return top.document.getElementById(ns(sID));

如果存在任何框架或 iframe,并且您没有阻止对框架的操作,这将导致异常行为。 (如果您确实阻止此类操作,则 top 是多余且不必要的。) 在这种情况下,GM 无法正常运行 - 取决于脚本的作用 - 通常似乎从顶部范围“切换”到框架范围,反之亦然。

因此,最好将其更改为:

return document.getElementById (ns (sID) );

并确保您拥有:

if (window.top != window.self)  //-- Don't run on frames or iframes
    return;

作为最上面的代码行。

除此之外,由于信息不足,几乎不可能发现问题。

要么将问题归结为完整独立配方,以复制失败。

或者,发布或链接到完成未编辑脚本

【讨论】:

我无法发布我的脚本。我相信你对操纵页面内容的事情是正确的。延迟并不总是有效。这个问题的解决方案是直接列出我需要的元素,而不用像element.childNode[x] 这样的东西。我将类添加到我需要的元素中,然后通过自定义 document.getElementsByClass 将这些元素放入一个数组中,效果很好。

以上是关于发生了啥?一天没问题,第二天“未定义”?的主要内容,如果未能解决你的问题,请参考以下文章

添加特定日期和第二天的所有值

Java学习的第九天-mybatis学习的第二天:今天遇到了好两个错误,解决一天;

前端如何设置一天只能点击一次的以及如何去判断第二天0点刷新的一些问题

第二天又是充实的一天

用java猴子吃桃子问题,第一天吃掉所有桃子的一半多一个,第二天又吃掉剩下桃子的一

代练第二天