4

I apologize if this was answered previously, I couldn't find it using the search.

I have to tag 4 elements to fire off an event when they are unhidden ("display: none" is removed). I think a mutationObserver is the right way to go, but am having trouble filtering to just the event I need.

Anyone have experience doing this?

Edit: Unfortunately, we do not have access to the script actually unhiding the elements. They are owned and protected by another team. The only thing we can key on is when the element is unhidden.

I was trying to figure out how to edit this for my needs.

    // Blocker is the element that has a changing display value
var blocker  = document.querySelector( '#blocker' );
// Trigger changes the display value of blocker
var trigger  = document.querySelector( '#trigger' );
// Our mutation observer, which we attach to blocker later
var observer = new MutationObserver( function( mutations ){
    mutations.forEach( function( mutation ){
        // Was it the style attribute that changed? (Maybe a classname or other attribute change could do this too? You might want to remove the attribute condition) Is display set to 'none'?
        if( mutation.attributeName === 'style' && window.getComputedStyle( blocker ).getPropertyValue( 'display' ) !== 'none'
          ){
            alert( '#blocker\'s style just changed, and its display value is no longer \'none\'' );
        }
    } );
} );

// Attach the mutation observer to blocker, and only when attribute values change
observer.observe( blocker, { attributes: true } );

// Make trigger change the style attribute of blocker
trigger.addEventListener( 'click', function(){
    blocker.removeAttribute( 'style' );
}, false );
Nathan Poorbaugh
  • 43
  • 1
  • 1
  • 4

3 Answers3

12

Here we go

var blocker  = document.querySelector('#blocker');
var previousValue = blocker.style.display;

var observer = new MutationObserver( function(mutations){
    mutations.forEach( function(mutation) {
        if (mutation.attributeName !== 'style') return;
        var currentValue = mutation.target.style.display;
        if (currentValue !== previousValue) {
           if (previousValue === "none" && currentValue !== "none") {
             console.log("display none has just been removed!");
           }

           previousValue = mutation.target.style.display;
        }
    });
});


observer.observe(blocker, { attributes: true });

Basically we get the default value set in the style attribute (if there is any) - this is stored in previousValue; Then we leave the code, but we make some differences in the loop. Firstly we check whether the style was changed in the target. Then we get the current display value in the target's style attribute. Next step is to compare those values. If previousValue was "none" and now it's something else, this means the display: none was unset. However you must note that it listens only for this specific change. If you don't need to listen for "none" then you may omit it.

You can use it in your code - I didn't write your entire code on purpose, so feel free to add your event listener on target.

Jakub Rożek
  • 2,110
  • 11
  • 12
  • I normally use a jQuery .each selector to apply elements like these to each element with a given class. Would this answer work with multiple objects of the same class? – Nathan Poorbaugh May 11 '16 at 17:15
  • If you mean you would like to listen for multiple elements then what you have to do is to iterate the elements. You could just place that code inside of your .each function and remove the `blocker` variable and in each place where it occured paste the second argument of .each loop (the elem). That's it, also it's worth to mention that you could disconnect the observer once it's not need anymore - just call observer.disconnect() – Jakub Rożek May 11 '16 at 17:19
1

There's another way I found that works reliably to trigger events when an element is hidden by display:none or becomes visible.

This is based on the ResizeObserver API which should work in all modern browsers (https://developer.mozilla.org/en-US/docs/Web/API/Resize_Observer_API)

When an element has css rule display:none applied to it then all its dimensions will be zero. So in order to detect becoming visible we just need to watch an element which has non-zero dimensions when visible.

const block=document.querySelector("#the-block")
const resizewatcher=new ResizeObserver(entries => {
  for (const entry of entries){
    console.log("Element",entry.target, 
      (entry.contentRect.width == 0) ? 
      "is now hidden" : 
      "is now visible"
    )
  }
})
resizewatcher.observe(block)
BenVida
  • 1,796
  • 1
  • 16
  • 25
  • This is actually the only working solution when the CSS is already set via classes, but using e.g. media queries. No attribute on the element is changed, so MutationObserver is never tirggered. This works like a charm! – Qwerty Jun 15 '22 at 01:34
0

Unfortunately, I'm unsure of how you're "removing" display: none, however, if you're, by some chance, using jQuery's $.show function, then you could specify a "callback" function, which would be called upon once the show "animation" has finished.

Here's an example:

$(".myElement").show(400, function() {
    // Do something...
});

Again, I'm unsure of how you're removing display: none, so this may not be helpful at all.

Brynden
  • 35,463
  • 2
  • 15
  • 21
  • We are not unfortunately. That script is owned by another team and unavailable to us. We are working with the marketers to enrich the data being collected. – Nathan Poorbaugh May 11 '16 at 16:31
  • Understood. Please disregard my answer, as it won't be of any use, sorry! – Brynden May 11 '16 at 16:35