尝试使用 JavaScript 触发 CSS 转换
Posted
技术标签:
【中文标题】尝试使用 JavaScript 触发 CSS 转换【英文标题】:Trying to trigger a CSS transition using JavaScript 【发布时间】: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://***.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 模板字符串。
【参考方案1】:
要触发转换,您实际上并不需要一个类来切换。这很重要,因为您可能无法动态设置类以预先切换。换句话说,您可能只需要更改一些 CSS 属性来触发转换。但是是的……您需要满足以下条件;
-
为了使您的
<div id="blue"></div>
元素具有动画效果,它必须带有定义transition
的std
类。所以首先我们应该像blue.classList.add("std");
您需要异步执行任务。异步刷新 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 属性时动画。我也在答案中添加了它。以上是关于尝试使用 JavaScript 触发 CSS 转换的主要内容,如果未能解决你的问题,请参考以下文章
普通 javascript 触发 css 淡出,ajax 加载内容,使用 CSS 淡入
如何使用 Javascript 键盘事件触发 CSS Sprite 动画
从 HTML、CSS、JS 转换为 JSX、CSS、JS 时,如何在 react 中链接我的 javascript 文件?