Jump to content
Search Community

TypeError: c.removeAttribute is not a function at S.CSSPropTween.Ua [as setRatio]

RolandSoos test
Moderator Tag

Warning: Please note

This thread was started before GSAP 3 was released. Some information, especially the syntax, may be out of date for GSAP 3. Please see the GSAP 3 migration guide and release notes for more information about how to update the code to GSAP 3's syntax. 

Recommended Posts

Hi!

 

After I updated my application to the latest GASP version I run into strange errors. I'm pretty sure that GSAP is fine and the issue is with my application, but I'm unable to find the cause. Maybe the following error message will ring a bell and you will be able to tell me where to look.

 

Error message: 

TypeError: c.removeAttribute is not a function at S.CSSPropTween.Ua [as setRatio]

 

 

And here are a screenshot of the clues from the call stack: https://i.imgur.com/FI6SDHg.png

 

I tried to recreate the issue on CodePen, but it does not happen there with the same parameter to the timeline's fromTo method. Here you can the error in real environment: https://smartslider3.com/bugs/gsap/1/index.html

Link to comment
Share on other sites

Hm, sorry to hear about the trouble. It's super difficult for us to troubleshoot on a live site with thousands of lines of minified code. It looks like maybe that's the part of the code that's specific to old version of IE for handling "opacity" (IE8 didn't support "opacity", so a Microsoft-specific filter had to be used). 

 

I really wish I could do more to help without a reduced test case, but I'm at a loss. There's no way to reproduce this in Codepen or a simpler test that doesn't use minified code? 

  • Like 1
Link to comment
Share on other sites

13 hours ago, GreenSock said:

Hm, sorry to hear about the trouble. It's super difficult for us to troubleshoot on a live site with thousands of lines of minified code. It looks like maybe that's the part of the code that's specific to old version of IE for handling "opacity" (IE8 didn't support "opacity", so a Microsoft-specific filter had to be used). 

 

I really wish I could do more to help without a reduced test case, but I'm at a loss. There's no way to reproduce this in Codepen or a simpler test that doesn't use minified code? 

 

 

Thank you, you gave me an idea :)

 

I create a custom scope for GSAP to prevent conflicts. 

var myScope = {};
(function(){
    // Here comes the source code of GSAP
}).call(myScope);

 

Then GSAP creates _gsScope with the following definition:

var _gsScope = (typeof(module) !== "undefined" && module.exports && typeof(global) !== "undefined") ? global : this || window;

 

So the _gsScope variable will be equal with myScope.

 

Then GSAP tries to find the navigator in the _gsScope which is not there as it assumes it is a Window object:

_agent = (_gsScope.navigator || {}).userAgent || "",

 

As _agent is empty string, GSAP is unable to find which browser it is and it assumes that opacity not supported, then it tries to use filter as opacity not supported :)

 

Solution:

 

Define navigator in the custom scope:
 

myScope.navigator = window.navigator;

 

 

Jack:

Do you know any other variable what GSAP presume in _gsScope?

Link to comment
Share on other sites

11 hours ago, OSUblake said:

 

What conflicts? You're creating problems, not solving any. If you want your GSAP stuff to be part of a certain scope, create an object for GreenSockGlobals, like here.

 

 

I do not see why do you think that playing with temporary variables is a good solution instead of creating a new scope:

21 hours ago, RolandSoos said:

var myScope = {};

(function(){

    // Here comes the source code of GSAP

}).call(myScope);

 

 

I went through the source code and the following needed to make GSAP to work as expected in custom scope:

_gsScope.document
_gsScope.navigator
_gsScope.console
 
Link to comment
Share on other sites

29 minutes ago, RolandSoos said:

I do not see why do you think that playing with temporary variables is a good solution instead of creating a new scope:

 

Didn't you already break your app once doing that? Doesn't sound like a good way to future-proof it. 

 

I don't know what you're trying to accomplish, and you didn't specify what you meant by preventing conflicts, so it's hard to advice. What I linked to shows how you can handle running multiple instances of GSAP. Those variables aren't temporary. Here's an old demo running 3 different versions of GSAP.

 

See the Pen a1325263d93f0085a4e9df87e215e58f by osublake (@osublake) on CodePen

 

Link to comment
Share on other sites

I have a custom module loader which creates a custom scope for the modules. I load GSAP into this module loader, so this is why this issue popped up. (It worked fine with with an older version of GSAP) 

 

If GSAP really needs window object for specific tasks then it should use directly, I think:

_gsScope.document => window.document
_gsScope.navigator => window.navigator
_gsScope.console => window.console

 

 

Link to comment
Share on other sites

22 hours ago, OSUblake said:

Using _gsScope instead of window is faster, and sometimes the window object doesn't exists, like in a node or server environment.

 

Ok, I understand. I take the risk. Hopefully this topic will help others who encounter the same issue.

 

Another idea. If you do not mind I would like to continue the converstation, maybe we will figure out something good for GSAP :)

window.window holds the window object. Why don't you use _gsScope.window.navigator and such instead of _gsScope.navigator

 

Then we can use:

var obj = {};
(function ($, undefined) {
    this.window = window;
    // Here comes GSAP
}).call(obj);

 

If _gsScope.window is undefined, you could throw an error for the developers.

 

Link to comment
Share on other sites

I'm curious, how does your module loader work? Is it done on the server, or ahead of time, like as part of a build process?

 

On 11/20/2017 at 4:51 AM, RolandSoos said:

If _gsScope.window is undefined, you could throw an error for the developers.

 

But that's not an error. The window object might not exist in a bunch of different environments. Just yesterday, somebody was asking about using GSAP inside Node.js, which uses the global object instead of window.

 

 

What's kind of ironic about that, is that getting GSAP to run inside Node.js used to involved modifying the file kind of like what you're doing, but with dummy objects.

 

 

On 11/20/2017 at 4:51 AM, RolandSoos said:

Another idea. If you do not mind I would like to continue the converstation, maybe we will figure out something good for GSAP :)

 

 

Why don't you do something like this. Create your own GreenSockGlobals object as shown above.

 

// Store GreenSock vars to restore them later
var oldGS = window.GreenSockGlobals;
var oldGSQueue = window._gsQueue;
var oldGSDefine = window._gsDefine;

// Reset
window._gsDefine = null;
window._gsQueue = null;

// Capture and separate the new version in the GSAP variable
var myGSAP = window.GreenSockGlobals = {};

// Load your versions of GSAP

// Restore the affected variables from older version (if any)
window.GreenSockGlobals = oldGS;
window._gsQueue = oldGSQueue;
window._gsDefine = oldGSDefine;

 

Now that you have isolated your version of GSAP from the window, you can run it however you want without any conflicts from other versions of GSAP being loaded. Perhaps something like this so you don't have to worry about messing with the window object.

 

(function(gsap) {
  
  const { TweenMax, MorphSVGPlugin, Back } = gsap;
    
  TweenMax.to("#foo", 1, {
    morphSVG: "#bar",
    ease: Back.easeOut,
    repeat: -1,
    yoyo: true
  });
  
})(myGSAP); // call it some scope if you like

 

 

Link to comment
Share on other sites

My module loader is nothing special, it just does what I told, creates a new scope for my stuff and loads there the required things. Custom jQuery, GSAP etc...

 

But, my enviroment is special. We are creating WordPress plugins and we have to make sure that our thing runs in every different environment. Async, not async, other plugin combines every JS files put them into the head with/without async, put to the end of the body, loads it with AJAX and just eval the JS codes.

 

I'm sure that I can use your solution with the variable exchange.You won't be able to convince me that this is the BEST solution, but you convinced me that this is the official solution.

 

 

I know that people want to use GSAP even in CLI environment where window is not available only global (like my scope, but I have window too). So you can even look for window that it is optional and you should prepare the code in that way. What I suggest could work and you would be able to know that you are in a non-window-ed context.

_gsScope.window = _gsScope.window || false;

Then you can use _gsScope.window when you need window and for example you can skip the runtime of CSSPlugin when _gsScope.window is false.

 

I think in theory it should work. Also I accept if you want to close the discussion on topic as this is your code and who am I to push anything :)

Link to comment
Share on other sites

I guess I'm kind of struggling to understand why the variable swap would cause any problems. It's doing pretty much the same thing as jQuery's noConflict method. 

 

39 minutes ago, RolandSoos said:

Also I accept if you want to close the discussion on topic as this is your code and who am I to push anything :)

 

Feedback is important. GSAP v2 is in the works, so maybe you can offer some suggestions on what would make working with GSAP in your environment better. 

  • Like 1
Link to comment
Share on other sites

 

21 minutes ago, OSUblake said:

I guess I'm kind of struggling to understand why the variable swap would cause any problems. It's doing pretty much the same thing as jQuery's noConflict method. 

 

I'm not telling you that it will cause problems. It will work fine. I use that method in my files too. We should also forget my specific use-case.

 

In my opinion my described method with _gsScope.window is a much more elegant way to access to the properties and services of window as GSAP's code depends on that several places. You shouldn't use _gsScope.navigator, you should use _gsScope.window.navigator as it results more understandable code. It probably be slower, I do not doubt it.

 

Then if you does this change you could suggest the following instead of the variable swapping. I think it is much more cleaner, don't you think?

var myScope = {window: window};
(function(){
  //Here comes GSAP
}).call(myScope);

 

 

Here is my loader, but I think it is not related with or discussion. It is just an example when _gsScope is not a window. 

 

The code might be messy, but it does a great job for me.

window.N2Classes is my scope

variable n2 holds jQuery and this class loader runs when jQuery is available on the page.

    window.N2Classes = {};
    (function ($, undefined) {
        "use strict";
        var a = {};

        window.N2Require = function (name, dependencies, fn) {
            var deps = [];
            if (name && a[name] == undefined) {
                a[name] = $.Deferred();
            }

            if (arguments.length == 2) {
                fn = arguments[1];
                dependencies = undefined;
            }

            if (dependencies !== undefined && dependencies) {
                for (var i = 0; i < dependencies.length; i++) {
                    if (a[dependencies[i]] == undefined) {
                        a[dependencies[i]] = $.Deferred();
                    }
                    deps.push(a[dependencies[i]]);
                }
            }
            $.when.apply($, deps).done(function () {
                var args = [$];

                if (name) {
                    if (typeof fn == 'function') {
                        N2Classes[name] = fn.apply(N2Classes, args);
                    } else {
                        N2Classes[name] = true;
                    }
                    a[name].resolve();
                } else {
                    fn.apply(N2Classes, args);
                }
            });
        };

        for (var i = 0; i < window.n2require.length; i++) {
            window.N2Require.apply(window, window.n2require[i]);
        }
    })(n2);

 

In my application JS files made in build process and I have several different JS files which holds classes for specific areas. backend.min.js frontend.min.js gsap.min.js etc...

 

Every file starts with, so if the module loader is not loaded yet, the resources added to a global array for later evaluation:

window.n2require = window.n2require || [];
window.N2Require = window.N2Require || function() {
    window.n2require.push(arguments)
};

 

And here is a class definition:

N2Require('MyClass', function ($, undefined) {
  
  function MyClass(){};
  
  return MyClass;
});

 

Class definition with dependecy:

N2Require('Hello', ['MyClass', 'MyOtherClass'], function ($, undefined) {
  
  function Hello(){};
  
  return Hello;
});

 

 

Then I can use this class as:

new N2Classes.MyClass();

//Or if there is chance that the gived class is not loaded yet. 
//For example when you start the application as inline script, you can not be sure that resources are loaded as async or not

N2Require(0,['MyClass' /* dependencies */], function(){
  // It runs when MyClass available
  new N2Classes.MyClass();
});

 

  • Like 1
Link to comment
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.
×
×
  • Create New...