为什么在Greasemonkey脚本中使用document.addEventListener('load', function)无效?

83

它没有报错,我放了一个console.log('loaded userscript wifi-autologin')console.log是可行的,但是文档中addEventListener应有的效果没有出现。经过更多的调试后,让其打印被调用的addEventListener,我发现它并没有被调用。

脚本来源:

// ==UserScript==
// @name        wifi-autologin
// @namespace   lf-ns
// @description Hopefully autologins to a captive portal
// @include     *://1.1.1.1/*
// @version     1
// @run-at document-end
// ==/UserScript==

document.addEventListener('load', submitAction);

14
监听window上的load事件。 - Paul S.
2
你是指 window.addEventListener() 吗? - Yet Another User
是的,虽然两种方法都可以(请参见http://jsbin.com/aqoweb/1),但我发现使用“window”更加可靠。 - Paul S.
1
@PaulS。从您链接的pastebin中我所看到的,所有的addEventListener都被附加到了window上(即使代码中的注释和控制台消息表明其中一个是要钩入文档的)。源代码中没有document.addEventListener('load' ...。:-/ - david.binda
我同意@david.binda的观点,Paul S.的示例没有测试文档事件处理程序,只测试了窗口事件处理程序。 - Alex MM
6个回答

128

显然,document.addEventListener() 不可靠,因此出现了错误。请改用参数相同的window.addEventListener()


17
你是怎么得出document.addEventListener('load',...)不可靠的结论的? - Alex MM
2
我已经看到了这条评论,上面写着“在窗口上侦听加载事件”,而且我做的jsfiddle证实它能够工作。但问题是,为什么在document上不起作用呢?根据我在回复中撰写的文档,它应该可以工作。所以我想知道的是,你是否了解为什么它在document上不能正常工作。 - Alex MM
3
@AlexMM 我一点儿也不知道。我决定就当它是小精灵了。 - Yet Another User
1
那么,这是个坏消息。有趣的是,事件 DOMContentLoaded 确实在 document 上触发了,而且它是 load 事件之前的一个事件,所以奇怪的是为什么其中一个被触发而另一个没有。 - Alex MM
1
在 Chrome 中,如果我将 useCapture 选项设置为 true,则会触发文档的 'load' 事件。但是,它会触发两次;是否有更多的小精灵? - Barrard
显示剩余2条评论

14

为了在页面加载时获取下拉框的值,我使用以下代码

document.addEventListener('DOMContentLoaded',fnName);

希望这能帮助到一些人。

1
DOMContentLoaded不是一个加载事件。它意味着DOM已经准备就绪,但并不总是在所有资源(图片、CSS文件或JS文件)加载完成后触发。 - Gfra54
DOMContentLoaded不是一个加载事件。它意味着DOM已经准备好了,但并不总是在所有资源(图像、CSS文件或JS文件)加载完成后触发。 - Gfra54

11

第二个链接已经失效。 - Wicket

9
对于像我这样的新手,最受欢迎的答案是不正确的。`document.addEventListener()` 不是完美的,但它是可靠的。简单的答案 `document.addEventListener('load', function)` 不能起作用是因为 `load` 事件在窗口而不是文档上触发。请参阅此处的文档 - https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event
window.addEventListener('load', runsWhenReady); //✅ load fires on the window and works.

document.addEventListener('DOMContentLoaded', runsWhenReady); //✅ domcontent fires on the document and works

document.addEventListener('load', runsWhenReady); // this WILL NOT work. load doesn't fire on the document.

9
问题在于何时通过触发添加事件并且执行(可以通过检查属性列表来验证document onload属性的修改)。

相对于onload事件触发,这是何时执行和修改onload的:

document.addEventListener('load', ... );

在页面的HTML加载和/或渲染之前、期间或之后?
这个简单的scURIple(剪切并粘贴到URL中) "工作",没有像天真地预期那样触发alert

data:text/html;charset=utf-8,
    <html content editable><head>
          <script>
                document.addEventListener('load', function(){ alert(42) } );
          </script>
          </head><body>goodbye universe - hello muiltiverse</body>
    </html>

加载是否意味着脚本内容已经被执行?

稍微扩展一下...
考虑进行轻微修改:

data:text/html;charset=utf-8,
    <html content editable><head>
          <script>
              if(confirm("expand mind?"))document.addEventListener('load', function(){ alert(42) } );
          </script>
        </head><body>goodbye universe - hello muiltiverse</body>
    </html>

而HTML是否已加载还是未加载则需要判断。由于屏幕上看不到“goodbye universe - hello muiltiverse”,因此渲染肯定是待处理的,但是难道不必先加载confirm( ... )才能执行吗?因此需要使用document.addEventListener('load', ... )吗?
换句话说,当代码本身尚未加载时,您可以执行代码以检查自我加载吗?
或者,从另一个角度来看,如果代码是可执行和已执行的,则它已经作为一个完成的交易被加载,并且在尚未加载和已加载之间的过渡发生的时间点进行事后检查是
那么哪个先出现:加载和执行代码还是使用尚未加载的代码功能? onload作为window属性有效,因为它是从对象中派生的,而不是像document一样自我引用,即通过document确定加载的问题情况。

PS.:以下情况下alert(...)会失败吗?(个人经验教训):

注意:除非在同一个窗口中加载非常快...否则覆盖是当天的主旋律
因此,在使用相同命名的窗口时,实际需要的是什么:

window.open(URIstr1,"w") .
   addEventListener('load', 
      function(){ alert(42); 
         window.open(URIstr2,"w") .
            addEventListener('load', 
               function(){ alert(43); 
                  window.open(URIstr3,"w") .
                     addEventListener('load', 
                        function(){ alert(44); 
                 /*  ...  */
                        } )
               } )
      } ) 

或者,对于每个连续的window.open,请执行以下操作:
alert("在#警告显示待处理加载完成后或通过神的启示激发后按“确定”");

data:text/html;charset=utf-8,
    <html content editable><head><!-- tagging fluff --><script>

        window.open(
            "data:text/plain, has no DOM or" ,"Window"
         ) . addEventListener('load', function(){ alert(42) } )

        window.open(
            "data:text/plain, has no DOM but" ,"Window"
         ) . addEventListener('load', function(){ alert(4) } )

        window.open(
            "data:text/html,<html><body>has DOM and", "Window"
         ) . addEventListener('load', function(){ alert(2) } )

        window.open(
            "data:text/html,<html><body>has DOM and", "noWindow"
         ) . addEventListener('load', function(){ alert(1) } )

        /* etc. including where body has onload=... in each appropriate open */

    </script><!-- terminating fluff --></head></html>

这段文字涉及到编程相关内容,强调了onloaddocumentwindow属性上的差异。另一个需要注意的是保留XSS、跨站点脚本攻击和SOP、同源策略规则,可能允许加载HTML URI但不能修改其内容以进行检查。如果一个scURIple作为同一来源/站点的书签/脚本运行,则可能会成功。例如,从任意页面,此链接将进行加载但不太可能做alert('done')
    <a href="javascript:window.open('view-source:http://google.ca') . 
                   addEventListener( 'load',  function(){ alert('done') }  )"> src. vu </a>

但如果链接被加入书签,并在查看google.ca页面时点击,它会同时实现两个功能。

测试环境:

 window.navigator.userAgent = 
   Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.4) Gecko/2008102920 Firefox/3.0.4 (Splashtop-v1.2.17.0)

0

这在2017年最后一个季度再次发生了。Greasemonkey太晚触发,已经触发了domcontentloaded事件。

该怎么办:

  • 我使用了@run-at document-start而不是document-end
  • 将Firefox更新到57。

来自:https://github.com/greasemonkey/greasemonkey/issues/2769

即使作为(私人)脚本编写者,我也不明白为什么我的脚本不起作用。

最可能的问题是在运行脚本之前已经触发了“DOMContentLoaded”事件。现在,在你回来并说@run-at document-start已设置之前,该指令目前并没有得到完全支持。由于WebExtensions非常异步的性质,很难保证何时执行某些操作。当FF59推出时,我们将拥有#2663,这将有所帮助。它实际上会帮助很多事情,包括调试。


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