Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why don't transitions on svg work on DOMContentLoaded without delay?

I have an svg with transitions set on it. Now when I add a class to it which has some properties varied then the transition only occur if I add delay between DOMContentLoaded event and addclass event. Here are two example, first with no delay second with an infinitesmall delay:

Without Delay:

! function() {
  window.addEventListener('DOMContentLoaded', function() {
    var logo2 = document.querySelector("svg");
    logo2.classList.add('start');
  });
}();
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 104.75 32.46">
            <defs>
                <style>
                polygon {
                    fill: red;
                    transition: opacity 3s ease-out, transform 3s ease-out;
                    opacity: 0;
                }

                .start polygon {
                	opacity: 1;
                }
				
				#A1 polygon {
					transform: translate(100px, 100px);
					transition-delay: 1s;
				}

				/*styles after animation starts*/
				.start #A1 polygon {
					transform: translate(0px, 0px);						
				}


            </style>
            </defs>
            <title>Logo</title>
            <g id="A1">
                
                <polygon  class="right" points="0.33 31.97 0.81 26.09 13.61 3.84 13.13 9.72 0.33 31.97" />
            </g>
            </svg>

With Delay:

! function() {
  window.addEventListener('DOMContentLoaded', function() {
    var logo2 = document.querySelector("svg");
    setTimeout(function(){
       logo2.classList.add('start');
    },0);
  });
}();
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 104.75 32.46">
            <defs>
                <style>
	                polygon {
	                    fill: red;
	                    transition: opacity 3s ease-out, transform 3s ease-out;
	                    opacity: 0;
	                }

	                .start polygon {
	                	opacity: 1;
	                }
					
					#A1 polygon {
						transform: translate(100px, 100px);
						transition-delay: 1s;
					}

					/*styles after animation starts*/
					.start #A1 polygon {
						transform: translate(0px, 0px);						
					}


                </style>
            </defs>
            <title>Logo</title>
            <g id="A1">
                
                <polygon  class="right" points="0.33 31.97 0.81 26.09 13.61 3.84 13.13 9.72 0.33 31.97" />
            </g>
            </svg>

As you can see in second example I added a delay of 0 second but it caused the animations to work, why?

Update1: well... we all are wrong :-)

I tried the same code without DOMContentLoaded and without delay. It still doesn't add transition without a delay:

! function() {
 
    var logo2 = document.querySelector("svg");
    logo2.classList.add('start');

}();
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 104.75 32.46">
            <defs>
                <style>
                polygon {
                    fill: red;
                    transition: opacity 3s ease-out, transform 3s ease-out;
                    opacity: 0;
                }

                .start polygon {
                	opacity: 1;
                }
				
				#A1 polygon {
					transform: translate(100px, 100px);
					transition-delay: 1s;
				}

				/*styles after animation starts*/
				.start #A1 polygon {
					transform: translate(0px, 0px);						
				}


            </style>
            </defs>
            <title>Logo</title>
            <g id="A1">
                
                <polygon  class="right" points="0.33 31.97 0.81 26.09 13.61 3.84 13.13 9.72 0.33 31.97" />
            </g>
            </svg>

I also noted that jQuery doesn't cause a reflow. Here is an example of inline jquery code that still doesn't fire ready function before CSSOM is loaded. Instead of inline jquery if we had external jquery then ready event would fire after CSSOM is ready. The understanding I have reached is that CSSOM needs a few milliseconds after html dom is rendered. So till it downloads external jquery CSSOM is ready. DOMContentLoaded simply don't care if stylesheets are loaded or not, that is it doesn't care if CSSOM is ready or not.

like image 603
user31782 Avatar asked Mar 19 '17 20:03

user31782


2 Answers

Because that's what DOMContentLoaded does : it fires when the DOM has been parsed, but before the CSSOM has been (and thus before styles have been applied).

If you don't want to wait for the load event,
one way is to force the browser to paint before your script execution (synchronously), by calling offsetXXX property on any document's element (e.g <body>) :

! function() {
  window.addEventListener('DOMContentLoaded', function(){
    document.body.offsetTop; // force a CSS repaint
    var logo2 = document.querySelector("svg");
    logo2.classList.add('start');
  });
}();
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 104.75 32.46">
  <defs>
    <style>
      polygon {
        fill: red;
        transition: opacity 3s ease-out, transform 3s ease-out;
        opacity: 0;
      }
      .start polygon {
        opacity: 1;
      }
      #A1 polygon {
        transform: translate(100px, 100px);
        transition-delay: 1s;
      }
      /*styles after animation starts*/
      .start #A1 polygon {
        transform: translate(0px, 0px);
      }
    </style>
  </defs>
  <title>Logo</title>
  <g id="A1">
    <polygon class="right" points="0.33 31.97 0.81 26.09 13.61 3.84 13.13 9.72 0.33 31.97" />
  </g>
</svg>
like image 164
Kaiido Avatar answered Oct 03 '22 22:10

Kaiido


As you can see in second example I added a delay of 0 second but it caused the animations to work, why?

Since the CSS object model have not been loaded when the DOMContentLoaded event fires

The DOMContentLoaded event is fired when the initial HTML document has been completely loaded and parsed, without waiting for stylesheets, images, and subframes to finish loading. A very different event load should be used only to detect a fully-loaded page. It is an incredibly popular mistake to use load where DOMContentLoaded would be much more appropriate, so be cautious.

https://developer.mozilla.org/en/docs/Web/Events/DOMContentLoaded

So, adding the css class will not run the animation.

The setTimeOut method is a javascript event, once fired (even with 0 time), it will be added to the end of the current browser execution queue (which in your case will be added after loading the CSS model). Hence the animation will fire properly.

Update:

But jquery domready fires after CSSOM is loaded. So are all these posts on SO are technically incorrect?

domready uses DOMContentLoaded so theoratically speaking, they behave the same way.

Are deferred script tags(both inline or external) executed before CSSOM is loaded?

The script deferred by the defer attribute is executed before the DOMContentLoaded is fired. So the answer is YES.

like image 31
KAD Avatar answered Oct 03 '22 22:10

KAD