MutationObserver 仅在所有资源加载完毕后回调

杰拉

要求

我需要动态更新元素的内容,然后在加载元素及其内容后执行动画。在这种特定的情况下该元素具有0的不透明度,并且一旦该内容被加载我设定其不透明度为1与CSS过渡褪色它英寸元素的含量具有大的数量的图像,在这两个<img>标签和作为background-image第子元素。非常重要的是,只有在所有图像和背景图像都加载后才允许动画开始。否则淡入效果会被破坏,因为空的占位符元素会被动画化,其图像内容会在稍后出现,如下所示。

页面加载

部分解决方案

为了确定元素的内容何时被加载,我使用 aMutationObserver来观察元素并在检测到更改时触发一个改变其不透明度的回调。

var page = document.getElementById( "page" );
var observer = new MutationObserver( function(){
    page.style.opacity = "1"; // begin animation on change
} );
var config = { characterData: true,
               attributes: false,
               childList: false,
               subtree: true };

observer.observe( page, config );
page.innerHTML = newContent; // update content

但是,a 本身MutationObserver并不是满足需求的综合解决方案。正如MDN页面所述:

MutationObserver 方法 observe() 配置 MutationObserver 回调以开始接收与给定选项匹配的 DOM 更改通知。

一旦子树被修改,并且当所有阻止 DOM 被解析的资源被加载时,DOM 更改就会触发。这不包括图像。因此,MutationObservers 将触发回调而不考虑内容的图像是否已完全加载。

如果我将childList参数设置true,则在回调中我可以查看每个更改的子项并将load事件侦听器附加到每个<img>,从而跟踪我附加的总数。一旦load触发了所有事件,我就会知道每个图像都已加载。

var page = document.getElementById( "page" ),
    total = 0,
    counter = 0;

var callback = function( mutationsList, observer ){
    for( var mutation of mutationsList ) {
        if ( mutation.type == 'childList' ) {
            Array.prototype.forEach.call( mutation.target.children, function ( child ) {
                if ( child.tagName === "IMG" ) {
                    total++; // keep track of total load events
                    child.addEventListener( 'load', function() {
                        counter++; // keep track of how many events have fired
                        if ( counter >= total ) {
                            page.style.opacity = "1";
                        }
                    }, false );
                }
            } );
        }
    }
}

var observer = new MutationObserver( callback );
var config = { characterData: true,
              attributes: false,
              childList: true,
              subtree: true };

observer.observe( page, config );
page.innerHTML = newContent; // update content

但是,也必须加载带有背景图像的元素。由于load无法附加到此类元素,因此该解决方案在检测背景图像是否已加载时无用。

问题陈述

如何使用 aMutationObserver来确定何时加载动态更新元素的所有资源?特别是,我如何使用它来确定隐式依赖的资源(如背景图像)已加载?

如果这不能用MutationObservers实现,那还能如何实现?使用setTimeout延迟动画不是解决方案。

杰拉

评论中提出了一个解决方案来检测背景图像加载。虽然确切的实现并不适合,因为我没有使用 jQuery,但该方法的可转移性足以与MutationObserver.

MutationObserver必须检测和更改元素及其所有子。必须调查每一个改变的孩子。如果孩子是一个图像,那么load事件侦听器附加到它,跟踪我附加的总数。如果孩子不是图像,则检查它是否有背景图像。如果是这样,则会创建一个新图像,load如上附加一个事件侦听器,并将src图像的 设置为孩子的backgroundImageurl。浏览器缓存意味着一旦新图像加载完毕,背景图像也会加载。

一旦load触发事件等于附加的事件总数,我就知道所有图像都已加载。

var page = document.getElementById( "page" ),
    total = 0,
    counter = 0;

var callback = function( mutationsList, observer ){
    for( var mutation of mutationsList ) {
        if ( mutation.type == 'childList' ) {
            Array.prototype.forEach.call( mutation.target.children, function ( child ) {
                var style = child.currentStyle || window.getComputedStyle(child, false);

                if ( child.tagName === "IMG" ) {
                    total++; // keep track of total load events
                    child.addEventListener( 'load', loaded, false );

                } else if ( style.backgroundImage != "none" ) {
                    total++; // keep track of total load events
                    var img = new Image();
                    img.addEventListener( 'load', loaded, false );
                    // extract background image url and add as img src
                    img.src = style.backgroundImage.slice(4, -1).replace(/"/g, "");
                }
            } );
        }
    }
    function loaded() {
        counter++; // keep track of how many events have fired
        if ( counter >= total ) {
            page.style.opacity = "1";
        }
    }
}

var observer = new MutationObserver( callback );
var config = { characterData: true,
            attributes: false,
            childList: true,
            subtree: true };

observer.observe( page, config );
page.innerHTML = newContent; // update content

这个解决方案似乎有点黑客。需要为每种类型的资源实施一个新案例。例如,如果我还需要等到脚本加载完毕,我就必须识别脚本并将load事件侦听器附加到它们,或者如果我不得不等待字体加载,则必须为它们编写一个案例。

如果有一个本机函数来确定所有资源何时加载,而不是将事件侦听器附加到所有内容,那就太好了。Service Workers 看起来很有趣,但就目前而言,我认为 hackish 解决方案是最简单和最可靠的。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章