【问题标题】:HTML frame's onload is called before the content is loaded在加载内容之前调用 HTML 框架的 onload
【发布时间】:2014-08-23 08:28:29
【问题描述】:

我正在为 Java SE API 参考文档 [http://docs.oracle.com/javase/8/docs/api/] 编写一个简单的 Greasemonkey 脚本。它是这样的:

// ==UserScript==
// @id          Test
// @grant       none
// @include     http://docs.oracle.com/javase/8/docs/api/*
// @version     1
// @run-at      docuitment-end
// ==UserScript==


var classNamesFrame = null;
var classNamesDoc = null;
var classNamesATags = null;
var classNames = null;

function printClassNames()
{
  classNamesATags = classNamesDoc.getElementsByTagName('a');
  classNames = new Array();
  var i;
  for (i = 0; i < classNamesATags.length; i++) {
    classNames.push(classNamesATags[i].textContent);
  }
  console.log(classNames);
  console.log(classNamesDoc.URL);
  console.log('Total number of classes: ' + classNamesATags.length);
  alert("printClassNames called");
}


classNamesFrame = document.getElementsByName('packageFrame') [0];
classNamesDoc = classNamesFrame.contentDocument;
classNamesFrame.onload = printClassNames;
//classNamesFrame.addEventListener("DOMContentLoaded", printClassNames, false);

函数printClassNames() 打印它在列出所有类的框架中找到的所有类的名称。当框架完成加载时,我应该这样做。但是 onload 甚至在加载框架的 HTML 文档之前被调用。

我尝试过使用DOMContentLoaded 事件,但它甚至没有被调用。

我该怎么做才能在框架完全加载完成后调用printClassNames

【问题讨论】:

  • 框架的内容是如何填充的?服务器端还是通过 AJAX?
  • @ChrisHardie 它通过服务器(如果我理解正确的话),如:&lt;frame src="allclasses-frame.html" name="packageFrame" title="All classes and interfaces (except non-static nested types)"&gt;
  • 所有类是如何加载到框架中的?
  • @ChrisHardie Frame 有一个关联的 HTML 源代码,浏览器会加载它们。

标签: javascript dom greasemonkey dom-events html-frames


【解决方案1】:

该脚本有各种错误(如下所列),但主要是它抓取了错误的contentDocument

这是因为 Firefox 为框架内容返回 &lt;about:blank&gt;,直到它被实际页面内容替换。 classNamesDoc 变量仍然指向空白值。 (请注意,Chrome 处理帧的方式略有不同,并会为您更新 classNamesDoc。)

所以,将classNamesDoc = classNamesFrame.contentDocument; 移动到printClassNames() 内部,最明显的问题就解决了。

其他问题,“最坏优先”:

  • 元数据块格式错误。// ==/UserScript== 行不正确。这会导致脚本为您浏览的每个页面和 (i) 框架运行(并崩溃)!

  • 脚本静默崩溃(在 Firefox 上,Chrome 会报告这些错误)每个页面/框架,但只有一小部分。使用前需要检查classNamesFrame 的值。

    例如,在docs.oracle.com/javase/8/docs/api/ 页面上,此脚本运行了 4 次,并且在其中的三个页面上静默崩溃。

  • 缺少@name 指令。这可能会导致范围界定、更新和维护问题——以及妨碍上传和共享脚本。

  • 语法错误。应该是:@run-at document-end,但在这种情况下您可能根本不需要这个指令。

将它们放在一起,您的脚本将是:

// ==UserScript==
// @name        Test
// @grant       none
// @include     http://docs.oracle.com/javase/8/docs/api/*
// @version     1
// ==/UserScript==

var classNamesFrame = null;
var classNamesDoc = null;
var classNamesATags = null;
var classNames = null;

function printClassNames()
{
  classNamesDoc = classNamesFrame.contentDocument;
  classNamesATags = classNamesDoc.getElementsByTagName('a');
  classNames = new Array();
  var i;
  for (i = 0; i < classNamesATags.length; i++) {
    classNames.push(classNamesATags[i].textContent);
  }
  console.log(classNames);
  console.log(classNamesDoc.URL);
  console.log('Total number of classes: ' + classNamesATags.length);
  console.log("printClassNames called");
}

classNamesFrame = document.getElementsByName('packageFrame') [0];
if (classNamesFrame) {
    classNamesFrame.onload = printClassNames;
}

-- 适用于 Firefox 和 Chrome(可能还有其他)。

【讨论】:

  • 谢谢 :) 是否有关于浏览器如何以及何时呈现或填充 DOM 的行为的文档?
  • 不是我所知道的这个级别。
【解决方案2】:
// ==UserScript==
// @name        Test
// @version     1
// @include     http://docs.oracle.com/javase/8/docs/api/allclasses-frame.html
// @grant       none
// ==/UserScript==

var classNamesATags = document.getElementsByTagName('a');
var classNames = [].map.call(classNamesATags, function(e) {
    return e.textContent;
});
console.log(classNames);
console.log(document.URL);
console.log('Total number of classes: ' + classNamesATags.length);
alert('printClassNames called');

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-30
    • 1970-01-01
    相关资源
    最近更新 更多