image - JavaScript - overwriting .onload prototype of HTMLImageElement(s) - Stack Overflow

Is it possible to bind an onload event to each image, declaring it once? I tried, but can't manage

Is it possible to bind an onload event to each image, declaring it once? I tried, but can't manage to get it working... (this error is thrown: Uncaught TypeError: Illegal invocation)

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

P.S: I also tried returning this, but doesn't seem to be the issue here... any suggestions / explanations on why my current code isn't working?

Is it possible to bind an onload event to each image, declaring it once? I tried, but can't manage to get it working... (this error is thrown: Uncaught TypeError: Illegal invocation)

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

P.S: I also tried returning this, but doesn't seem to be the issue here... any suggestions / explanations on why my current code isn't working?

Share Improve this question asked Aug 21, 2016 at 16:12 Bilal075_Bilal075_ 1537 bronze badges 5
  • 5 No. Don't mess with builtin prototypes. – Bergi Commented Aug 21, 2016 at 16:14
  • ncyoung./?p=194 – mplungjan Commented Aug 21, 2016 at 16:15
  • @Bergi It's an empty built-in prototype. It's an event listener, it's made for custom boundations of custom functions. – Bilal075_ Commented Aug 21, 2016 at 16:17
  • @mplungjan That's interesting, thanks for sharing! – Bilal075_ Commented Aug 21, 2016 at 16:19
  • @Bilal075_ is does not matter if onload exists for HTMLImageElement.prototype or not. You should never add custom functionality by modifying the prototype of native objects, and should avoid to do so for objects that you don't own (e.g. of foreign libraries), except if they give you a defined why how you should do it. – t.niese Commented Aug 21, 2016 at 16:23
Add a ment  | 

4 Answers 4

Reset to default 3

You can't set a handler on the prototype, no.

In fact, I'm not aware of any way to get a proactive notification for image load if you haven't hooked load on the specific image element, since load doesn't bubble.

I only two know two ways to implement a general "some image somewhere has loaded" mechanism:

  1. Use a timer loop, which is obviously unsatisfying on multiple levels. But it does function. The actual query (document.getElementsByTagName("img")) isn't that bad as it returns a reference to the continually updated (live) HTMLCollection of img elements, rather than creating a snapshot like querySelectorAll does. Then you can use Array.prototype methods on it (directly, to avoid creating an intermediary array, if you like).

  2. Use a mutation observer to watch for new img elements being added or the src attribute on existing img elements changing, then hook up a load handler if their plete property isn't true. (You have to be careful with race conditions there; the property can be changed by the browser even while your JavaScript code is running, because your JavaScript code is running on a single UI thread, but the browser is multi-threaded.)

You get that error because onload is an accessor property defined in HTMLElement.prototype.

You are supposed to call the accessor only on HTML elements, but you are calling the setter on HTMLImageElement.prototype, which is not an HTML element.

If you want to define that function, use defineProperty instead.

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

Warning: Messing with builtin prototypes is bad practice.

However, that only defines a function. The function won't be magically called when the image is loaded, even if the function is named onload.

That's because even listeners are internal things. It's not that, when an image is loaded, the browser calls the onload method. Instead, when you set the onload method, that function is internally stored as an event listener, and when the image is loaded the browser runs the load event listeners.

Instead, the proper way would be using Web Components to create a custom element:

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>

There is not much browser support yet, though.

This provides a notification for any image loading, at least in Opera (Presto) and Firefox (haven't tried any other browser). The script tag is placed in the HEAD element so it is executed and the event listener installed before any of the body content is loaded.

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

Of course, by changing the filtering on tagName it will also serve to respond to the loading of any other element that fires a load event, such as a script tag.

I've written something similar some time ago to check if an image is loaded or not, and if not, show a default image. You can use the same approach.

$(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
        }
    });
});

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744959373a4603395.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信