【问题标题】:Why don't transitions on svg work on DOMContentLoaded without delay?为什么 svg 上的转换不能立即在 DOMContentLoaded 上工作?
【发布时间】:2017-08-11 00:35:11
【问题描述】:

我有一个设置了转换的 svg。现在,当我向其中添加一个具有不同属性的类时,只有在 DOMContentLoaded 事件和 addclass 事件之间添加延迟时才会发生转换。这里有两个例子,第一个没有延迟,第二个有无限小的延迟:

毫不拖延:

! 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>

有延迟:

! 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>

正如您在第二个示例中看到的,我添加了 0 秒的延迟,但它导致动画正常工作,为什么?

更新 1:嗯...我们都错了 :-)

我在没有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>

我还注意到 jQuery 不会导致重排。这是一个内联 jquery 代码的示例,它在加载 CSSOM 之前仍然不会触发 ready 函数。如果我们有外部 jquery,而不是内联 jquery,那么在 CSSOM 准备好后会触发 ready 事件。我达到的理解是CSSOM在html dom渲染后需要几毫秒。因此,直到它下载外部 jquery CSSOM 已准备就绪。 DOMContentLoaded 根本不关心样式表是否加载,也就是说它不关心 CSSOM 是否准备好。

【问题讨论】:

标签: javascript html css svg css-transitions


【解决方案1】:

因为DOMContentLoaded 就是这样做的:它在 DOM 被解析时触发,但在 CSSOM 解析之前(因此在应用样式之前)。

如果您不想等待load 事件,
一种方法是强制浏览器在脚本执行之前(同步)进行绘制,方法是在任何文档的元素上调用offsetXXX 属性(例如&lt;body&gt;):

! 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>

【讨论】:

  • 为什么document.body.offsetTop; 会导致重绘?
  • 因为这个属性必须根据它的offsetParent的位置返回元素的偏移位置,要做到这一点,就得做一个reflow,让每个人都有一个位置。
  • 我一直认为DOMContentLoaded 与 jQuery dom 就绪相同。 SO上的许多帖子都暗示了这一点。加载 CSSOM 后readystatechangeevent 会触发吗?
  • 如果我们的头部有一个大的 css 文件。当浏览器到达document.body.offsetTop; // force a CSS repaint,如果样式表还没有加载,浏览器会停止进一步的javascript处理吗?它会等待样式表加载并应用,然后找到offSet,然后开始进一步的javascript处理吗?
  • @user31782,样式表会在 DOMContentLoaded 触发时加载(当然,除非您在之后动态附加它)。不会加载的是 CSSOM,这意味着如果我可以这么说的话,CSS 内容将不会被解析为与 DOM 相关。所以要回答你的问题,offsetXXX hack 不会等待任何东西加载,它只会强制回流。但是,它不会等待应该从样式表中加载的外部内容(例如背景图像、字体)实际加载。为此,请使用load 事件。
【解决方案2】:

正如您在第二个示例中看到的,我添加了 0 秒的延迟,但它 导致动画工作,为什么?

由于DOMContentLoaded事件触发时CSS对象模型尚未加载

当初始 HTML 文档具有 完全加载和解析,无需等待样式表, 图像和子帧以完成加载。非常不同的事件负载 应该只用于检测完全加载的页面。这是一个令人难以置信的 使用负载的常见错误 DOMContentLoaded 会更多 合适,所以要小心。

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

所以,添加 css 类不会运行动画。

setTimeOut 方法是一个 javascript 事件,一旦被触发(即使是 0 时间),它将被添加到当前浏览器执行队列的末尾(在您的情况下,它将在加载 CSS 模型后添加)。因此动画会正常触发。

更新:

但是加载 CSSOM 后 jquery domready 会触发。所有这些也是如此 SO 上的帖子在技术上不正确?

domready 使用 DOMContentLoaded,所以从理论上讲,它们的行为方式相同。

延迟脚本标记(内联或外部)是否在之前执行 CSSOM 已加载?

由 defer 属性延迟的脚本在DOMContentLoaded 被触发之前执行。所以答案是肯定的。

【讨论】:

  • 我现在有很多困惑。 1. SO上的许多帖子建议jquery domready与DOMContentLoaded相同(例如this)但是jquery domready在CSSOM加载后触发。那么所有这些关于 SO 的帖子在技术上都是不正确的吗? 2. 延迟脚本标签(内联或外接)是否在 CSSOM 加载之前执行?
  • 正如我正在与以下答案的用户讨论的那样,似乎 jquery ready 和 DOMContentLoaded 是相同的,因此其他 stackoverflow 帖子原则上是正确的,但他们应该提到,如果没有 jquery,我们需要在浏览器重绘之后DOMContentLoaded。您的最后一个链接没有提到 CSSOM。
  • domready 使用 DOMContentLoaded 所以从理论上讲,它们的行为方式相同。至于答案,在大多数浏览器中,延迟发生在页面加载之后,所以 CSSOM 应该在那个时候加载
  • 不,不,如果当时加载了 CSSOM,那么 DOMContentLoaded 必须在此之前触发。但它不能,因为 DOMContentLoaded 仅在执行延迟脚本后才会触发。
  • 是的,确实如此,由 defer 属性延迟的脚本在 (DOMContentLoaded) 触发之前执行。我更新了答案
猜你喜欢
  • 2016-02-21
  • 1970-01-01
  • 1970-01-01
  • 2020-06-18
  • 1970-01-01
  • 2014-06-28
  • 2021-05-22
  • 1970-01-01
  • 2021-03-10
相关资源
最近更新 更多