当生成内容中的图像被加载时触发事件

时间:2022-06-23 09:09:13

I've got a problem with some Javascript-generated content :

我遇到了一些javascript生成内容的问题:

I use the onload() event of my html page to load HTML code from a server and inject it in the page. Since it already is html I use the innerHtml property of the nodes I want to fill with content.

我使用html页面的onload()事件来从服务器加载html代码并将其注入页面。由于它已经是html,所以我使用要填充内容的节点的innerHtml属性。

Once I've injected the HTML I call a handmade adjustHeights() function that normalizes the height of the elements I created this way to make them all match the height of the tallest element.

一旦我注入了HTML,我就调用一个手工的adjustHeights()函数,它将我创建的元素的高度规范化,使它们都与最高的元素的高度匹配。

All of this works perfectly unless the code inside innerHtml contains some sort of image or other media that takes time to load (particularly videos) because adjustHeights() is called between the injection of the code and the end of the loading time of the image/video/etc.

所有这些都可以很好地工作,除非innerHtml中的代码包含某种图片或其他需要花费时间加载(特别是视频)的媒体,因为adjustHeights()在代码注入和图像/视频/等加载时间结束之间调用。

Is there any way to wait for every element to be loaded before calling adjustHeights() ? I've already tried the document.onreadystatechange property but the state is already on 'completed' when I start to inject code.

是否有方法在调用adjustHeights()之前等待每个元素被加载?我已经试过了。onreadystatechange属性,但当我开始注入代码时,状态已经“完成”了。

If it's possible I would rather not use time-based calls on adjustHeights().

如果可能的话,我宁愿不对adjustHeights()使用基于时间的调用。

To make it clearer here's an example :

为了让它更清晰,这里有一个例子:

var mylist = document.getElementById('mycontent');
var li;
for (var i = 0; i < res.length; i++)
{
    li = document.create('li');
    li.innerHTML=res[i];
    mylist.appendChild(li);
}
adjustHeights();

2 个解决方案

#1


1  

For image and other sort of media, you should use "onload" event so that you can call adjust height after that. Example:

对于图像和其他类型的媒体,您应该使用“onload”事件,以便在此之后可以调用调整高度。例子:

<img src="someimage.png" onload="loadImage()" >

<script>
   function loadImage() {
     // code to adjust heights
    }
</script>

Here is the standard version:

以下是标准版本:

var img = document.querySelector('img')

function loaded() {
  alert('loaded');
  // do something to adjust height
}

if (img.complete) {
  loaded()
} else {
  img.addEventListener('load', loaded)
  img.addEventListener('error', function() {
      alert('error')
  })
}

#2


9  

To do this without adding an inline onload attribute to all the images/videos/etc you will have to observe the DOM for changes. Then on every change, you have to fetch all the new media and add the onload event to them from the callback. To prevent checking each element every time, once they've been loaded you could mark them as such by adding a data-loaded="true" property for instance.

要做到这一点,无需向所有图像/视频等添加内联onload属性,您必须观察DOM以了解更改。然后,在每次更改时,都必须获取所有新媒体,并从回调中将onload事件添加到它们中。为了防止每次都检查每个元素,一旦加载了它们,您可以通过添加数据加载的="true"属性来标记它们。


A cross-browser solution to observe DOM changes can be found in this answer: Detect changes in the DOM. I will not repeat it here (but it's included in the demo below).

可以在这个答案中找到观察DOM更改的跨浏览器解决方案:检测DOM中的更改。我不会在这里重复(但它包含在下面的演示中)。


Note: I use images as an example, but this should also work for videos and other media.

注意:我以图片为例,但这也适用于视频和其他媒体。

On every DOM change first you check for images without the data-loaded attribute that are already loaded anyway (this could happen when an image was still in the browser's cache) by checking element.complete. If so, fire the callback function and add the attribute to it.

在每次DOM更改时,首先要检查没有数据加载属性的图像(这可能发生在图像仍然在浏览器的缓存中),检查element.complete。如果是,启动回调函数并向其添加属性。

If .complete is not the case, add an onload event to them that also fires the callback once it is loaded.

如果.complete不是这种情况,那么向它们添加一个onload事件,该事件在加载回调之后也会触发回调。

// Observe the DOM for changes
observeDOM(document.body, function(){ 
    checkNewMedia();
});

// Loop through all new media, add the event
var checkNewMedia = function() {

    // extend this by using document.querySelectorAll("img:not([data-loaded), video:not([data-loaded])") etc.
    var media = document.querySelectorAll('img:not([data-loaded]');
    for(var i = 0; i < media.length; i++) {
        addMediaLoadedEvent(media[i]);   
    }
}

// Fire the callback if complete, otherwise bind to onload
var addMediaLoadedEvent = function(element) {
    if (element.complete) {
        onMediaLoaded(element);
    } else {
        element.addEventListener('load', function(event) {
            onMediaLoaded(event.target);   
        });
    }
}

// The callback that is fired once an element is loaded
var onMediaLoaded = function(element) {
    element.setAttribute('data-loaded', 'true');

    adjustHeights(); // <-- your function
}

DEMO: fire event on each image load


If you only want to fire the event once all images are loaded, you could add an extra check that counts how many images are still not loaded:

如果您只想在所有图片加载后触发事件,您可以添加一个额外的检查,以计算有多少图片仍然没有加载:

// The callback that is fired once an image is loaded
var onMediaLoaded = function(element) {
    element.setAttribute('data-loaded', 'true');

    // only fire when there are no media elements without the 'data-loaded' attribute left
    // again, can be extended to use video, etc.
    if(document.querySelectorAll('img:not([data-loaded])').length === 0) {
        adjustHeights(); // <-- your function
    }
}

DEMO: fire event on all images loaded

#1


1  

For image and other sort of media, you should use "onload" event so that you can call adjust height after that. Example:

对于图像和其他类型的媒体,您应该使用“onload”事件,以便在此之后可以调用调整高度。例子:

<img src="someimage.png" onload="loadImage()" >

<script>
   function loadImage() {
     // code to adjust heights
    }
</script>

Here is the standard version:

以下是标准版本:

var img = document.querySelector('img')

function loaded() {
  alert('loaded');
  // do something to adjust height
}

if (img.complete) {
  loaded()
} else {
  img.addEventListener('load', loaded)
  img.addEventListener('error', function() {
      alert('error')
  })
}

#2


9  

To do this without adding an inline onload attribute to all the images/videos/etc you will have to observe the DOM for changes. Then on every change, you have to fetch all the new media and add the onload event to them from the callback. To prevent checking each element every time, once they've been loaded you could mark them as such by adding a data-loaded="true" property for instance.

要做到这一点,无需向所有图像/视频等添加内联onload属性,您必须观察DOM以了解更改。然后,在每次更改时,都必须获取所有新媒体,并从回调中将onload事件添加到它们中。为了防止每次都检查每个元素,一旦加载了它们,您可以通过添加数据加载的="true"属性来标记它们。


A cross-browser solution to observe DOM changes can be found in this answer: Detect changes in the DOM. I will not repeat it here (but it's included in the demo below).

可以在这个答案中找到观察DOM更改的跨浏览器解决方案:检测DOM中的更改。我不会在这里重复(但它包含在下面的演示中)。


Note: I use images as an example, but this should also work for videos and other media.

注意:我以图片为例,但这也适用于视频和其他媒体。

On every DOM change first you check for images without the data-loaded attribute that are already loaded anyway (this could happen when an image was still in the browser's cache) by checking element.complete. If so, fire the callback function and add the attribute to it.

在每次DOM更改时,首先要检查没有数据加载属性的图像(这可能发生在图像仍然在浏览器的缓存中),检查element.complete。如果是,启动回调函数并向其添加属性。

If .complete is not the case, add an onload event to them that also fires the callback once it is loaded.

如果.complete不是这种情况,那么向它们添加一个onload事件,该事件在加载回调之后也会触发回调。

// Observe the DOM for changes
observeDOM(document.body, function(){ 
    checkNewMedia();
});

// Loop through all new media, add the event
var checkNewMedia = function() {

    // extend this by using document.querySelectorAll("img:not([data-loaded), video:not([data-loaded])") etc.
    var media = document.querySelectorAll('img:not([data-loaded]');
    for(var i = 0; i < media.length; i++) {
        addMediaLoadedEvent(media[i]);   
    }
}

// Fire the callback if complete, otherwise bind to onload
var addMediaLoadedEvent = function(element) {
    if (element.complete) {
        onMediaLoaded(element);
    } else {
        element.addEventListener('load', function(event) {
            onMediaLoaded(event.target);   
        });
    }
}

// The callback that is fired once an element is loaded
var onMediaLoaded = function(element) {
    element.setAttribute('data-loaded', 'true');

    adjustHeights(); // <-- your function
}

DEMO: fire event on each image load


If you only want to fire the event once all images are loaded, you could add an extra check that counts how many images are still not loaded:

如果您只想在所有图片加载后触发事件,您可以添加一个额外的检查,以计算有多少图片仍然没有加载:

// The callback that is fired once an image is loaded
var onMediaLoaded = function(element) {
    element.setAttribute('data-loaded', 'true');

    // only fire when there are no media elements without the 'data-loaded' attribute left
    // again, can be extended to use video, etc.
    if(document.querySelectorAll('img:not([data-loaded])').length === 0) {
        adjustHeights(); // <-- your function
    }
}

DEMO: fire event on all images loaded