Jump to content


Setting a non-numeric DOM attribute/property

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

I was hoping to change the src attribute of an image simply using .set instead of a manual .call (which won't reset the attribute if I bring the playhead to 0) but it doesn't seem to work at all.


See the Pen ?editors=001 by bfred-it (@bfred-it) on CodePen

 (open console and element inspector on the image)


AttrPlugin doesn't seem to make any change.  It works

See the Pen ?editors=101 by bfred-it (@bfred-it) on CodePen

, but not with an image's attributes. Strangely enough, if I try to set the src as a property (by disabling autoCSS), GSAP sets it to "NaN"


Am I doing something wrong?

Link to comment
Share on other sites

GSAP works primarily with numerical values, and plugins are required to enable plaintext 'tweening' (e.g. CSSPlugin allows toggling of display to 'none','block', etc). AttrPlugin is not meant for changing the src attribute, and the AttrPlugin API states "Tweens any numeric attribute of a DOM element".


I just changed your loop to

var logos = ['https://raw.githubusercontent.com/alrra/browser-logos/master/chrome/chrome_256x256.png','https://raw.githubusercontent.com/alrra/browser-logos/master/firefox/firefox_256x256.png','https://raw.githubusercontent.com/alrra/browser-logos/master/opera/opera_256x256.png','https://raw.githubusercontent.com/alrra/browser-logos/master/safari/safari_256x256.png'];

for (var i = 0; i < logos.length; i++) {
  tl.call(function (i) {
    img.src = logos[i];
  }, [i], null, i);
  tl.set(img, {
    opacity: Math.random()*0.5+0.5//just for visual
  }, i);
  tl.call(function () {
and it seems to be working as I think you intended. I added the chrome logo to the start of the array so that it's also included in the timeline.


You may also encounter a difference between seek() and time(). seek()'s second parameter suppressEvents defaults to true, while time() defaults to false. If suppressEvents is false, your call()s will still occur whenever you jump past them in the timeline.


Another possible solution (you can determine if this is too convoluted for your use) would be to use a custom object with a getter/setter to handle the img src changes.

window.onload = function() {
  var logo = new (function(){
    var img = document.getElementById('logo'),
        src = ['img1.png','img2.png','img3.png'],
        currentI = 0,
        lastI = 0,
        maxI = src.length - 1;
    this.setI = function(i) {
      currentI = Math.floor(i);
      if (lastI !== currentI) img.src = src[(lastI = currentI)];
    this.getI = function() { return currentI; };
    this.tl = new TimelineLite({paused:true}).to(this, maxI, { setI:maxI, ease:Linear.easeNone });
    return this;
  • Like 3
Link to comment
Share on other sites

As I specified in my post, if I use .call, TL won't keep track of src's previous value, so it won't restore it. By using suppressEvents:false, src would be reset to its first change, not to its original value.


I guess I could store its original value and manually set it at time 0, so it would be remembered as the first change. Like this:

var originalValue = img.src;
tl.call(function () {
  img.src = originalValue;
for (var i = 0; i < logos.length; i++) {
   tl.call(function (i) {
     img.src = logos[i];
   }, [i], null, i);


Your second example is interesting, I didn't know that TL would call a function using { fn:arg }, it could come in handy.


Anyway, I take it that .set can't be used to set non-numerical values at all (unless I code a plugin for it). 

Link to comment
Share on other sites

I did cover that original value in my suggestion where I said I added the chrome logo to the start of the array, but yes you could also pull it from the original src like you've said. Don't forget you are using i as insert time, and starting from i = 0, so your loop would need to change to inserting at i + 1;


Here's another modification that might suit you

var logos = ['https://raw.githubusercontent.com/alrra/browser-logos/master/firefox/firefox_256x256.png','https://raw.githubusercontent.com/alrra/browser-logos/master/opera/opera_256x256.png','https://raw.githubusercontent.com/alrra/browser-logos/master/safari/safari_256x256.png'];

for (var i = 0; i < logos.length; i++) {
  if (i !== 0) tl.call(changeSrc, [i-1], null, i); // swap to previous image
  tl.call(changeSrc, [i], null, i); // swap to next image
tl.set({}, {}, "+=1"); // add some padding for the final logo

function changeSrc(i) {
  img.src = logos[i];

This adds the original image in to the mix, and behaves in the same manner as the getter/setter version (where each image has its own 'section' of the timeline, rather than just a trigger point at which it gets enabled). This improves the behaviour when going backwards in time.


GSAP can pick up custom getters/setters if your target has them in the form target.getProp / target.setProp. It's a better alternative than using an onUpdate if you want to modify the values being set by the tween.


.set() is just a shortcut method that creates a .to() tween with a duration of 0 for you (with smart handling of immediateRender if used on a timeline). It doesn't possess any different capabilities for tweening non-numeric values.

TweenLite.set(foo, { bar:1 });
// is identical to
TweenLite.to(foo, 0, { bar:1 });
  • 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.