2

I've got a 3rd-party script that loads a photo gallery on my page with the images coming from off-site.

My page starts as empty:

 <div class="container-fluid" id="cincopa"> </div> 

The 3rd-party script then adds other stuff (like the frame of the photo gallery):

 <div class="container-fluid" id="cincopa"> <div id="cp_widget_38cc1320-f4a4-407a-a80e-1747bd339b64"> </div> </div> 

Then finally the images load:

 <div class="container-fluid" id="cincopa"> <div id="cp_widget_38cc1320-f4a4-407a-a80e-1747bd339b64"> <div class="galleria_images"> <div class="galleria_image">SomeImage</div> <div class="galleria_image">SomeImage</div> <div class="galleria_image">SomeImage</div> </div> </div> </div> 

I want to:

  • display a loading animation

  • set a MutationObserver on $('#cincopa')

  • when it detects that $('.galleria_image') has been created, it means images have been loaded, so I can

  • remove the loading animation

Code:

var target = document.querySelector('#cincopa'); // create an observer instance var observer = new MutationObserver(function(mutations) { console.log(mutations); mutations.forEach(function(mutation) { console.log(mutation.type); }); }); // configuration of the observer: var config = { attributes: true, childList: true, characterData: true }; // start the observer, pass in the target node, as well as the observer options observer.observe(target, config); 

The problem is that the MutationObserver only console logs one mutation and the MutationRecord only has one mutation in its array. I would expect numerous mutations as the 3rd-party script creates DOM elements.

Am I misunderstanding how MutationObserver works?

Here's the solution

// This is MeteorJS creating the loading spinning thing var loadingView = Blaze.render(Template.loading, $('#cincopa')[0]); // select the target node var target = document.querySelector('#cincopa'); // create an observer instance var observer = new MutationObserver(function(mutations) { mutations.forEach(function(mutation) { if(mutation.target.className === "galleria_image"){ // a image has been loaded, so remove the loading spinner and // kill the observer Blaze.remove(loadingView); observer.disconnect(); } }); }); // configuration of the observer: var config = { attributes: true, childList: true, characterData: true, subtree: true }; // start the observer, pass in the target node, as well as the observer options observer.observe(target, config); 

Updated Solution

.forEach is dumb and doesn't have a good way to break out of the loop, which meant that I was getting multiple commands to Blaze.remove() and observer.disconnect(), even after .galleria_image had been found.

So I used underscore instead:

// create an observer instance var observer = new MutationObserver(function(mutations) { var loaded = _.find(mutations, function(mutation){ console.log("observer running"); return mutation.target.className === "galleria-image"; }); if(loaded){ Blaze.remove(loadingView); observer.disconnect(); console.log("observer stopped"); }; }); 
0

1 Answer 1

2

There's an option to allow you to do exactly what you want: observe the subtree of an element. Just add subtree: true to your config for the MutationObserver.

// ... // In this case case only these two are needed, I believe. var config = { childList: true, subtree: true }; // ...observe 

This should allow you to figure when .gallaria_images has been inserted. As a side note, you (OP) should also double check that images are loaded when that happens.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks! All good and OP updated with solution. I originally thought only childList was required.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.