【问题标题】:Trying to trigger a CSS transition using JavaScript尝试使用 JavaScript 触发 CSS 转换
【发布时间】:2016-06-18 17:16:13
【问题描述】:

在玩 JavaScript 和 CSS 过渡时,我尝试删除一个 CSS 类 在使用 JavaScript 和 innerHTML 动态插入 div 之后。

我很惊讶与蓝色 div 的不透明度相关的 CSS 过渡没有按照我想要的方式触发(在 Safari 下工作,在 Chrome 下随机工作,在 Firefox 开发版下不工作)。有人能解释一下这种现象吗?

我不确定为什么它的工作方式与红色 div 不同。也许我不知道浏览器如何处理 innerHTML ?

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Having fun with JS</title>
    <style>
        .std {
            width:100px;
            height:100px;
            opacity: 1;
            transition: opacity 5s;
        }

        .std--hidden {
            opacity: 0;
        }

        .std--red {
            background-color: red;
        }

        .std--blue {
            background-color: blue;
        }
    </style>
</head>
<body>
<button>click here</button>
<div class='std std--red std--hidden'></div>
<div class='insert-here'>
</div>
<script>
    // let a chance to the browser to trigger a rendering event before the class is toggled
    // see http://stackoverflow.com/a/4575011
    setTimeout(function() {
        // everything works fine for the red div
        document.querySelector('.std--red').classList.toggle('std--hidden');
    }, 0);


    document.querySelector('button').addEventListener('click', function(e) {

        var template = `<div class='std std--blue std--hidden'></div>`;
        document.querySelector('.insert-here').innerHTML = template;

        setTimeout(function() {
            // Why does the CSS transition seems to be triggered randomly ?
            // well more exactly
            // - it works under my Safari
            // - it does not work under my FirefoxDeveloperEdition
            // - it works randomly under my Google Chrome
            document.querySelector('.std--blue').classList.toggle('std--hidden');
        }, 0);

    });
</script>
</body>
</html>

编辑

所以我刚刚阅读了CSS transitions specs 并找到了这个

对一组同时进行的样式更改的处理称为 样式更改事件。 (实现通常会改变风格 事件以对应于他们所需的屏幕刷新率,以及何时 脚本需要最新的计算样式或布局信息 依赖它的 API。)

这可能是某种解释吗? setTimeout 0 在某些浏览器上是否太快以至于他们没有时间计算样式差异,因此不会触发样式更改事件?事实上,如果使用更长的 setTimeout(例如,~16.6666,猜测 1/60 的刷新率......)它似乎在任何地方都有效。有人可以确认吗?

【问题讨论】:

  • 使用setTimeout有什么意义?它什么也没做。也许尝试删除它们,看看会发生什么?
  • 是的,它很有用,在浏览器首次渲染 DOM 之后,它会将删除 css 类的代码排入队列
  • 尝试删除您在var template 中插入内部 HTML 元素的反引号。而是看到添加双引号“”
  • oooh...那个反引号没有做任何事情...您可能应该使用双引号(")作为外部和单引号(')作为class=
  • 反引号对应一个 ES6 模板字符串。

标签: javascript css listener css-transitions innerhtml


【解决方案1】:

要触发转换,您实际上并不需要一个类来切换。这很重要,因为您可能无法动态设置类以预先切换。换句话说,您可能只需要更改一些 CSS 属性来触发转换。但是是的……您需要满足以下条件;

  1. 为了使您的&lt;div id="blue"&gt;&lt;/div&gt; 元素具有动画效果,它必须带有定义transitionstd 类。所以首先我们应该像blue.classList.add("std");
  2. 您需要异步执行任务。异步刷新 DOM 最好由 requestAnimationFrame() 函数完成。

var blue = document.getElementById("blue");
blue.classList.add("std");
blue.style.backgroundColor = "blue";
blue.style.opacity         = 0;

document.querySelector('button')
        .addEventListener( 'click'
                         , _ => requestAnimationFrame(_ => blue.style.opacity = 1)
                         );
.std { width:100px;
       height:100px;
       opacity: 1;
       transition: opacity 5s;
}
.std--red { background-color: red;}
<button>click here</button>
<div class='std std--red'></div>
<div id="blue"></div>

【讨论】:

    【解决方案2】:

    我想我找到了答案,见CSS 3 transition spec

    由于本规范未定义何时发生样式更改事件 发生,因此计算值的变化被考虑 同时,作者应该意识到,改变任何 进行更改后一小段时间的转换属性 这可能会导致行为在 实施,因为这些变化可能被认为是同时在 一些实现,但不是其他的。

    我尝试添加一点延迟,让浏览器注意到样式差异并且它可以始终如一地工作。似乎某些浏览器执行 setTimeout 0 的速度确实比其他浏览器快:-)

        setTimeout(function() {
            document.querySelector('.std--blue').classList.toggle('std--hidden');
        }, 17);
    

    【讨论】:

      【解决方案3】:

      您必须为超时设置一个值,以便为元素插入 DOM 留出时间(在调用 document.querySelector 之前必须存在)

      setTimeout(function() { document.querySelector('.std--blue').classList.toggle('std--hidden')},100);
      

      你不需要第一次超时

      onload = function() {
      document.querySelector('.std--red').classList.toggle('std--hidden');
      
       document.querySelector('button').addEventListener('click', function(e) {
         var template = "<div class='std std--blue std--hidden'></div>";
         document.querySelector('.insert-here').innerHTML = template;
         setTimeout(function() { document.querySelector('.std--blue').classList.toggle('std--hidden')},100);
          });
      }
       .std {
                  width:100px;
                  height:100px;
                  opacity: 1;
                  transition: opacity 5s;
              }
      
              .std--hidden {
                  opacity: 0;
              }
      
              .std--red {
                  background-color: red;
              }
      
              .std--blue {
                  background-color: blue;
              }
      <button>click here</button>
      <div class='std std--red std--hidden'></div>
      <div class='insert-here'>
      </div>

      【讨论】:

      • 抱歉,我不能接受您的回答,因为它没有回答问题。我知道我可以改变延迟,我只是不明白为什么它在第二种情况下不起作用。
      • 如果您尝试在 jsfiddle 之外运行代码(在浏览器中打开 HTML),如果您不放置 setTimeout,您会看到第一个动画吗?我没有。
      • 是的代码在文档加载上运行,在 IE11、Firefox、Chrome 和 Chrome Canary 中测试
      • 在我的电脑上,它在 Safari 版本 9.0.3 (11601.4.4) 下运行,但在 Chrome 版本 48.0.2564.116(64 位)下随机运行,在 Firefox 开发版 46.0a2 下无法运行(2016-03-04)。 :-(
      • 尝试将您的代码封装在事件监听器中 onload = function() { //code } }
      【解决方案4】:

      看来您必须通过引用样式来触发转换。我只是添加了这一行(即使进入 console.log 也可以)

      document.querySelector('.std.std--blue').style.width = '20';
      

      它在这里工作:jsfiddle

      注意:我正在使用 FirefoxDeveloperEdition。

      我关注了这个solution

      CSS 过渡不会在您添加或删除类时动画,它只会在您更改 CSS 属性时动画。

      完整的脚本

      <script>
      
      setTimeout(function() {
          // everything works fine for the red div
          document.querySelector('.std--red').classList.toggle('std--hidden');
      }, 0);
      
      
      document.querySelector('button').addEventListener('click', function(e) {
      
          var template = `<div class='std std--blue std--hidden'></div>`;
          document.querySelector('.insert-here').innerHTML = template;
          document.querySelector('.std.std--blue').style.width = '20';
          setTimeout(function() {
      
              document.querySelector('.std.std--blue').style.width = '100';
              document.querySelector('.std--blue').classList.toggle('std--hidden');
          }, 0);
      
          });
      </script>
      

      【讨论】:

      • 在 fiddle(或类似的)之外直接在浏览器中尝试,它对我不起作用。但它可以在 fiddle、codepen 等内部工作......
      • 不是这样,第一个动画被触发了。
      • 您的代码似乎没有回答我关于为什么它适用于红色 div 而不适用于蓝色 div 的问题?
      • CSS 过渡在您添加或删除类时不会动画,它只会在您更改 CSS 属性时动画。我也在答案中添加了它。
      猜你喜欢
      • 2017-01-19
      • 2016-08-29
      • 1970-01-01
      • 2014-12-09
      • 2019-08-06
      • 2014-01-14
      • 2020-05-12
      • 1970-01-01
      • 2012-07-12
      相关资源
      最近更新 更多