【问题标题】:JavaScript - overwriting .onload prototype of HTMLImageElement(s)JavaScript - 覆盖 HTMLImageElement(s) 的 .onload 原型
【发布时间】:2016-12-28 05:16:51
【问题描述】:

是否可以将 onload 事件绑定到每个图像,声明一次?我试过了,但无法让它工作......(抛出这个错误:Uncaught TypeError: Illegal invocation

HTMLImageElement.prototype.onload = function()
{
        console.log(this, "loaded");
};

P.S:我也尝试返回 this,但似乎不是这里的问题...关于为什么我当前的代码不起作用的任何建议/解释?

【问题讨论】:

  • 没有。不要乱用内置原型。
  • @Bergi 这是一个空的内置原型。它是一个事件监听器,它是为自定义函数的自定义边界而设计的。
  • @mplungjan 很有意思,谢谢分享!
  • @Bilal075_ 是否存在onloadHTMLImageElement.prototype 无关。你不应该通过修改原生对象的原型来添加自定义功能,并且应该避免为你不拥有的对象(例如外国库)这样做,除非它们给你一个明确的为什么你应该怎么做。

标签: javascript image prototype onload overwrite


【解决方案1】:

你不能在原型上设置处理程序,不。

事实上,如果您没有将load 挂在特定 图像元素上,我不知道有任何方法可以获取图像加载的主动通知,因为load 没有t冒泡。

我只有两个人知道两种方法来实现通用的“某处已加载某些图像”机制:

  1. 使用计时器循环,这在多个级别上显然不能令人满意。但它确实起作用。实际查询 (document.getElementsByTagName("img")) 并没有那么糟糕,因为它返回了对 img 元素的不断更新(实时)HTMLCollection 的引用,而不是像 querySelectorAll 这样创建快照做。然后你可以在上面使用Array.prototype 方法(如果你愿意,可以直接使用,以避免创建中间数组)。

  2. 1234563不是真的。 (您必须小心那里的竞争条件;即使您的 JavaScript 代码正在运行,浏览器也可以更改该属性,因为您的 JavaScript 代码在单个 UI 线程上运行,但 浏览器 是多线程。)

【讨论】:

  • 感谢您的回答。我尝试设置一个突变观察者,但似乎没有拾取“静态”元素(写入文档本身的元素)。我已经阅读了有关突变观察者的信息,实际上是为了检测当前 DOM 中的变化而设计的。
  • @Bilal075_:HTML 中的标记成为 DOM 中的元素(这就是它的目的)。如果您使用突变观察者方法,则需要首先检查已经存在的元素,然后使用观察者来观察新的img 元素或img 元素的src 发生变化。跨度>
  • 但是我应该如何将 onload 侦听器 (以编程方式) 添加到从一开始就在 HTML 的 'markup' 中的这些元素中?
  • @Bilal075_: document.getElementsByTagName("img") 给你一个HTMLCollection。从 body 末尾的 script 标记中包含的代码调用它,就在关闭 </body> 标记之前,并在尚未完成的那些上设置处理程序(其中一些已经完成),允许我上面提到的比赛条件。或者,如果您想在解析器添加它们时捕获它们,请在处理任何 <img> 标记之前,在 bodytopscript 标记中设置突变观察器.
  • 抱歉没有提到你的名字,stackoverflow 根本不会让我。但回到我们的讨论。我的脚本已经设置在 标记中,并且在文档具有 readystate interactive 时立即调用。这基本上意味着只要文档准备好与客户交互,他就可以/将。一旦这个就绪状态命中,这个函数就会被调用,并选择(正如你提到的)所有带有 IMG 标签的元素。但是在这个集合上设置一个事件监听器是行不通的。我不知道我做错了什么。图片已经加载了吗?
【解决方案2】:

您收到该错误是因为onloadHTMLElement.prototype 中定义的访问器属性。

您应该只在 HTML 元素上调用访问器,但您在 HTMLImageElement.prototype 上调用 setter,这不是 HTML 元素。

如果要定义该函数,请改用defineProperty

Object.defineProperty(HTMLImageElement.prototype, 'onload', {
  configurable: true,
  enumerable: true,
  value: function () {
    console.log(this, "loaded");
  }
});
var img = new Image();
img.onload();

警告:使用内置原型是不好的做法。

但是,这仅定义了一个函数。加载图像时不会神奇地调用该函数,即使该函数被命名为onload

那是因为即使是听众也是内在的东西。并不是说,当加载图像时,浏览器会调用onload 方法。相反,当您设置 onload 方法时,该函数在内部存储为事件侦听器,当加载图像时,浏览器运行 load 事件侦听器。

相反,正确的方法是使用 Web 组件来创建自定义元素:

var proto = Object.create(HTMLElement.prototype);
proto.createdCallback = function() {
  var img = document.createElement('img');
  img.src = this.getAttribute('src');
  img.addEventListener('load', function() {
    console.log('loaded');
  });
  this.appendChild(img);  
};
document.registerElement('my-img', {prototype: proto});
<my-img src="/favicon.ico"></my-img>

不过,目前还没有太多浏览器支持。

【讨论】:

  • 正如 T.J 提到的那样,也行不通。我正在尝试为文档中的每个图像设置默认的加载行为。那么有什么解决方法吗?
  • @Oriol 在您的场景中,确实如此。但我也在尝试将此事件绑定到静态图像元素(所以这些是写在 HTML 文档本身中的,但没有附加 onload 事件)。关于如何让您的 sn-p 工作让我满意的任何想法?
  • @Oriol:您直接致电 onload。如果 OP 知道何时这样做,他们就不需要这样做。尝试在img 上设置一个src,看看你是否被调用(你不会)。
  • @T.J.Crowder 也许我解释得不好。我只是在修复 OP 的代码,这会引发错误。但是当然设置一个函数不会在加载资源时神奇地调用它,即使你将它命名为onload
  • @Oriol 感谢您的回答。不幸的是,目前我没有创建自定义元素的选择......但是..你介意给我解释一下为什么设置 onload (statically) 有效,但是将其定义为“默认”行为,不是吗?
【解决方案3】:

这为任何图像加载提供了通知,至少在 Opera (Presto) 和 Firefox 中(尚未尝试任何其他浏览器)。脚本标签放置在HEAD 元素中,因此在加载任何正文内容之前执行它并安装事件侦听器。

document.addEventListener('load', function(e) {
    if ((!e.target.tagName) || (e.target.tagName.toLowerCase() != 'img')) return;
    // do stuff here
}, true);

当然,通过更改tagName 上的过滤,它还将用于响应任何其他触发加载事件的元素的加载,例如脚本标签。

【讨论】:

    【解决方案4】:

    我前段时间写过类似的东西来检查是否加载了图像,如果没有,则显示默认图像。您可以使用相同的方法。

    $(document).ready(function() {
        // loop every image in the page
        $("img").each(function() {
            // naturalWidth is the actual width of the image
            // if 0, img is not loaded
            // or the loaded img's width is 0. if so, do further check
            if (this.naturalWidth === 0) { // not loaded
                this.dataset.src = this.src; // keep the original src
                this.src = "image404.jpg";
            } else {
                // loaded
            }
        });
    });
    

    【讨论】:

      猜你喜欢
      • 2021-01-26
      • 1970-01-01
      • 2013-04-06
      • 1970-01-01
      • 2011-07-08
      • 1970-01-01
      • 2013-01-11
      相关资源
      最近更新 更多