代码之家  ›  专栏  ›  技术社区  ›  jla

仅当所有资源都已加载时才执行MutationObserver回调

  •  0
  • jla  · 技术社区  · 7 年前

    要求

    我需要动态更新元素的内容,然后在加载元素及其内容后执行动画。在这种特殊情况下,元素的不透明度为0,一旦加载内容,我就用CSS转换将其不透明度设置为1以淡入。元素的内容有大量的图像,在这两个 <img> 标签和as background-image

    page load

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

    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
    

    然而,就其本身而言 突变观察者 不是对需求的全面解决方案。作为 MDN

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

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

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

    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

    负载 无法附加到这些元素,此解决方案在检测是否加载了背景图像时是无用的。

    我如何使用 突变观察者 是否加载动态更新元素的资源?特别是,如何使用它来确定已加载隐式相关资源(如背景图像)?

    突变观察者 s、 否则怎么能做到这一点?使用 setTimeout

    1 回复  |  直到 7 年前
        1
  •  2
  •   jla    7 年前

    在评论中提出了一种检测背景图像的方法。虽然由于我没有使用jQuery,所以确切的实现并不合适,但是该方法可以转移到足够多的地方来处理 MutationObserver

    这个 突变观察者 必须检测对元素及其所有子元素的更改。必须调查每一个发生变化的孩子。如果孩子是一个形象,那么 load 事件侦听器连接到它,跟踪我连接的总数。如果子对象不是图像,则检查它是否有背景图像。如果是这样,则会创建一个新图像 负载 src 图像的一部分被设置为孩子的 backgroundImage

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

    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
    

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

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