我需要动态更新元素的内容,然后在加载元素及其内容后执行动画。在这种特殊情况下,元素的不透明度为0,加载内容后,我将其不透明度设置为1,并通过CSS过渡使其淡入。元素的内容中包含大量图像,在两个<img>
中标签和background-image
子元素。非常重要的是,动画只能在所有图像和背景图像都加载后才能开始。否则,淡入效果将被破坏,因为将空的占位符元素设置为动画,其图像内容将在稍后显示,如下所示。
要确定何时加载元素的内容,我正在使用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
但是,MutationObserver
本身并不是满足要求的全面解决方案。正如MDN页上所述:
MutationObserver方法observe()将MutationObserver回调配置为开始接收与给定选项匹配的DOM更改通知。
一旦修改了子树,并且加载了所有可能阻止DOM解析的资源,DOM就会触发变化。这不包括图像。因此,MutationObserver
会触发回调,而不考虑内容的图像是否已完全加载。
如果将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
无法附加到此类元素,因此在检测是否已加载背景图像时,此解决方案没有用。
如何使用MutationObserver
确定何时加载动态更新的元素的所有资源?特别是,如何使用它来确定已加载了诸如背景图像之类的隐式依赖资源?
如果用MutationObserver
无法做到这一点,那还能怎么实现呢?使用setTimeout
延迟动画不是解决方案。
答案 0 :(得分:0)
在注释中提出了一种解决方案,以检测背景图像的加载。尽管由于我不使用jQuery,确切的实现方式并不适合,但该方法具有足够的可转移性,可以与MutationObserver
一起使用。
MutationObserver
必须检测到元素及其所有子元素的更改。每个变化的孩子都必须接受调查。如果孩子是图像,则将load
事件侦听器附加到该图像,以跟踪我附加的总数。如果孩子不是图像,则检查它是否具有背景图像。如果是这样,则会创建一个新图像,并如上所述附加一个load
事件侦听器,并将图像的src
设置为子级的backgroundImage
的URL。浏览器缓存意味着一旦加载了新图像,背景图像也将加载。
一旦触发的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
事件侦听器附加到它们,或者如果我必须等待字体加载,则必须编写一个案例为他们。
如果有一个本机函数来确定何时加载所有资源,而不是将事件侦听器附加到所有内容,那将是很好的。服务人员看起来很有趣,但就目前而言,我认为这种骇人听闻的解决方案是最简单,最可靠的。