Jump to content
GreenSock

Search In
  • More options...
Find results that contain...
Find results in...
Faction Ryan

Animate element whose class changes dynamically

Recommended Posts

I'm using TweenMax in conjunction with the jQuery Isotope plugin. Isotope lets you filter by clicking an anchor element. Elements that are filtered out get a class of "isotope-hidden". This class does not exist when the page is rendered, and is added after the fact, when a user clicks a filter.

 

I currently have this code:

jQuery( '.isotope' ).not( '.isotope-hidden' ).each( function() {
    timeline.add(
        TweenMax.staggerFrom(
            ....
        )
    );
} );

I'm using TweenMax in conjunction with the Super Scrollorama plugin, so that when users scroll up and down the page, the animations are played out and reversed, respectively. I've discovered that if a user scrolls down the page, filters out some isotopes, scrolls back up, then scrolls back down to the isotope section, all of the isotopes are animated in, including the ones that should be hidden. I've determined that this is because TweenMax is acting as expected: it does not know that I have elements with the class "isotope-hidden", so it doesn't ignore these.

 

It would be ideal if there were a "live" method, as in jQuery, that would act on elements that are modified after the DOM is loaded. I didn't see anything like this in the docs.

 

In short, is there a way to get TweenMax to "recognize" these elements, which are given the class "isotope-hidden" dynamically?

 

Thanks!

 

 

Link to post
Share on other sites

hello,

 

what are using as your click handler?

 

have you tried to have the each argument pass to staggerFrom?

jQuery( '.isotope' ).not( '.isotope-hidden' ).each( function(index,element) {
    
    var $this = $(element);  // this basically equals 'this' element
    
    timeline.add(
        TweenMax.staggerFrom( $this, 3,{ css:{autoAlpha:1} })
    );

} );

just as an example.. do you notice how i used the $this variable which references the element parameter, which equals the current element in the loop.. also you can try checking if each element has the .isotope-hidden class:

jQuery( '.isotope' ).each( function(index,element) {
       
       var $this = $(element); // this basically equals 'this' element
       
       // check if element does not have .isotope-hidden class
       if( !$this.hasClass('.isotope-hidden') ){
 
             timeline.add(
                   TweenMax.staggerFrom( $this, 3,{ css:{autoAlpha:1} })
             );

       }

 } );

also if your just iterating through one collection of elements with the same class using the the jQuery each() method, you can try just using the GSAP .from() method instead, unless they are staggering in time

 

Also i cant see all your code.. but if you have your own click event bound to a button which you are using as a trigger.. you can also use the jQuery .on() method which keeps listening for what selector you bind to it.

$('.isotope:not(.isotope-hidden)').on('click', function(){
    
   // TweenMax code goes here
    
});

so even if the class is added to the DOM after this event runs, it will still listen for the above selector when clicked. Also you could give the handler context if your element is dynamically added to the DOM after the fact..

 

i hope this helps if im understanding your question.. or if you can provide an example :)

  • Like 1
Link to post
Share on other sites

Hi,

 

What you could try is to create an empty variable to contain the timeline that animates the isotope elements. Then when the user filters, with the filtered elements you add the tweens to the timeline. Then if the user filters again you kill the entire timeline and add the tweens again but with the new set of filtered elements. This can be done with a simple function and, as Jonathan proposed, in that function you iterate through the elements that have the particular class.

 

As I see in the isotope docs the filtering event has a callback, that's even better you can call the function to add the tweens to the timeline using isotope's native callback (which works very similar to images loaded, another great piece of code by David DeSandro):

$('#container').isotope({ filter: '.my-selector' }, function( $items )
{
    populateTimeline($items);
});

Since items is a JQuery array, you can use that in the function:

function populateTimeline(targets)
{
    timeline.add(
        TweenMax.staggerFrom(targets,
            ....
        )
}

Also as Jonthan said if I'm completely off in this one, if you can provide a simple example of the issue (codepen or fiddle) it'll be great to identify what could be the issue.

 

Best,

Rodrigo.

  • Like 1
Link to post
Share on other sites

Thanks to you both for your answers. Unfortunately, my situation is more complicated than my small code snippet let on. I encourage you to visit factionmedia.com to see the problem in action. To replicate, follow these steps:

  1. Scroll to the "Work" section, and filter on "Media"
  2. Scroll back to the top (don't click the links on the left; just scroll to the top)
  3. Scroll back down to the work section (again, without clicking the left side links)

You'll see that all of the isotopes animate in, including the ones that were filtered out. The timeline that controls the animation is set on document ready, and not on click of the filter. I'm able to reset the isotope filter when a user clicks one of the lefthand nav items, but I'm not sure how to reset the filter (or clear out the timeline) when a user just scrolls.

 

Rodrigo, your solution made the most sense, but I was unable to get it to work properly. I used the timeline.clear() method, then added the elements with the filter callback (as you suggested), but that resulted in some really funky behavior.

 

I just wanted to let you both know I appreciate your feedback, and that I'm not sure there is a solution to this admittedly esoteric bug.

 

Ryan

Link to post
Share on other sites

i wlll debug more when i get home.. but have you tried to set on line 740 of style.css .. adding a override !important:

.isotope-item.isotope-hidden {
    pointer-events: none;
    z-index: 1;

    /* overrides inline styles */
    visibility: hidden !important; 
    opacity: 0 !important;
    filter: alpha(opacity=0) !important;
    -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)" !important;
}

also looks like Rodrigo has the right approach for a long term fix..

Link to post
Share on other sites

I did try adding those lines -- they successfully prevent the elements from being re-animated back in (step 3 of my previous post) -- but Isotope still thinks they take up space, so there are empty gaps where the hidden tiles are, instead of the filtered tiles being nicely stacked near each other.

 

I'm not looking for an answer to this anymore as I think it's mostly an Isotope issue, and a difficult to reproduce bug. Thanks anyway!

Link to post
Share on other sites

visibility doesn't alter the document flow, so if .isotope-item doesn't have position:absolute/fixed then it will still take up space even when visibility:hidden. You can set display:none to truly remove the element from document flow.

  • Like 1
Link to post
Share on other sites

ok..

 

maybe trying isotope updateSortData() method.

 

but also have you tried the reLayout method in Isotope ?

 

http://isotope.metafizzy.co/demos/relayout.html

// define isotope containing element
var $container = $('#container');

// init isotope
$container.isotope({ 
    itemSelector: '.element', 
    masonry : { 
               columnWidth : 120 
    } 
}); 

// bind an event to each isotope item
$container.on('.element', 'click', function(){ 

    // trigger relayout of isotope items
    $container.isotope('reLayout'); 

}); 
you could also trigger the isotope reLayout() method at the end of the callback of the scroll
 
i also noticed that the same animation you are using to fade in at the load of the page is being triggered each time the scroll is triggered
 
have you tried to separate the initial fade animation when the page loads, and have a separate animation fade in / out the isotope items when scrolling the items into view?
 

:)

Link to post
Share on other sites

Hi,

 

My best guess is that this issue is being caused by scrollorama.

 

When you go to the work section all elements are animated in (the staggerFrom tween). That tween records the DOM elements being animated and their initial states. When you reach that part of the site and filter the elements isotope does it's job and apply the new CSS to the elements in order to filter and reorder them. When you scroll up and then back down scrollorama works again and triggers the tween. Since the tween has recorded the initial elements and not the filtered ones it animates those, which means all the elements.

 

The way I see it there are two choices. First, you could reset isotope and undo the filter and set the "All" button as the active one; like that you'll see all the elements being animated and the filter will be consistent with what the user is seeing. Second, you can kill the tween (you said that you used clear(), but that doesn't take the DOM elements out of the tween) and create it again selecting only the filtered elements. I believe that the first choice is the easiest one, but I have no idea how you've configured your tweens/timelines in scrollorama, so the second might not be that hard either.

 

Best,

Rodrigo.

Link to post
Share on other sites

Rodrigo has the problem exactly. I've tried using Isotope's reLayout() and updateSortData() methods but, as Rodrigo states, Isotope is behaving as expected.

 

If I can remove the DOM elements and "reset" them I think that would solve the problem. I'm assuming using the kill() method would accomplish this. I'll give it a go and report back.

 

Thanks to everyone for continuing to lend a hand!

Link to post
Share on other sites

how and when are you implementing then kill() method.. do have an example ?

 

not sure if this matters but i also noticed this style in your style.css on line 347.. have you tested to see how it behaves if you comment out the overflow:visible !important line:

#our-work .offset-right, #social-stream .offset-right {
    margin-right: -10px;
    /*overflow: visible !important;*/
    perspective-origin: 50% 50%;
}

do you see what i mean? when i commented that out, i no longer so the straggler items out of place, unless you see something different after commenting out that line?

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.

×