【问题标题】:How to apply CSS changes in order?如何按顺序应用 CSS 更改?
【发布时间】:2016-12-15 21:39:24
【问题描述】:

我有时会遇到一些情况,我想立即对 CSS 应用多项更改,确保每一项都由渲染器注册。

Here's a simplified example

元素的高度是自动的,所以它不能被转换。所以我想将元素高度设置为当前计算的高度,然后立即更改类以开始转换。如果这种情况发生在下一行代码中,css-renderer 就没有时间对第一次更改做出反应,就好像只有类被更改了 -> 没有过渡。

var foo = $(".foo");
foo[0].addEventListener("click", function(ev){
  foo.css({"height":foo.height()+"px"});
  foo.addClass("active");
});  //this doesn't work, foo.css…' is ignored.

我们可以使用window.requestAnimationFrame() 延迟到最小的动画时间步长,但是,由于浏览器的差异,这些已经需要两个嵌套调用来支持 Firefox。

var dan = $(".dan");
dan[0].addEventListener("click", function(ev){
  window.requestAnimationFrame(function(){
    dan.css({"height":dan.height()+"px"});
    window.requestAnimationFrame(function(){
      dan.addClass("active");
    });
  });
});  //this does work (afai can tell), but feels overdone with all that nesting.

从技术上讲,此代码有效。我只是想知道这是否真的是像这样链接 css 更改的最佳方法,或者是否还有其他方法。

【问题讨论】:

  • 这是一个很好的问题。查看给出的示例,代码中不存在 webkit 转换。虽然我认为这不能解决你的问题,但值得把它们放进去看看它的反应。
  • 过渡在所有当前浏览器上都是无前缀的,所以虽然它们在生产中仍然是必需的,但它不会影响这个问题。无论如何更新了代码,并没有表现出任何行为差异。
  • 我认为应该有一个简单的答案,但我想不是。我认为可能有一种方法可以使用 jQuery .queue 来做到这一点,但我无法在几分钟的摆弄中让它工作,所以我会把它扔在这里以便其他人可能能够弄清楚出去。我相信旧方法是使用 setInterval 或 setTimeout,但这会使您的转换在执行之前可能会暂停,并且在代码中不会更漂亮。
  • 您需要时,Application.DoEvents() 在哪里?
  • 请考虑更新此问题以删除“最佳”指示,或指明“最佳”和“更好”的确切含义。例如,您的意思可能是“更少的代码行”或“更快的性能”,但我们不知道。

标签: javascript css css-transitions


【解决方案1】:

您可以在此处使用setTimeout 以确保类更改总是在计算divheight 之后发生。

示例:

var foo = document.getElementsByClassName('foo')[0];
var bar = document.getElementsByClassName('bar')[0];
var dan = document.getElementsByClassName('dan')[0];

function fooFunction(element) {
    element.style.height = element.clientHeight + 'px';

    setTimeout(function(){
        element.classList.add('active');
        element.removeAttribute('style');
    },10);

}

foo.addEventListener('click',function(){fooFunction(foo);},false);
bar.addEventListener('click',function(){fooFunction(bar);},false);
dan.addEventListener('click',function(){fooFunction(dan);},false);
.foo, .bar, .dan{
width:20%;
display: inline-block;
overflow: hidden;
vertical-align:top;
transition: height 1s ease-out;
}

.active {
height:50px;
}

.foo {
background:rgb(150, 100, 100);
}

.bar {
background:rgb(150, 150, 100);
}

.dan {
background:rgb(100, 150, 100);
}
<div class="foo">
<p>Works.</p>
<p>Another paragraph.</p>
<p>Third paragraph. This one is even longer. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quibusdam, fugiat facere? Animi, odit et tempore debitis modi quae eaque, libero, dolores magni, voluptas tenetur tempora quidem alias ut praesentium sed.</p>
</div>

<div class="bar">
<p>Works.</p>
<p>Another paragraph.</p>
<p>Third paragraph. This one is even longer. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quibusdam, fugiat facere? Animi, odit et tempore debitis modi quae eaque, libero, dolores magni, voluptas tenetur tempora quidem alias ut praesentium sed.</p>
<p>Fourth paragraph. This one is even longer. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quibusdam, fugiat facere? Animi, odit et tempore debitis modi quae eaque, libero, dolores magni, voluptas tenetur tempora quidem alias ut praesentium sed.</p>
</div>

<div class="dan">
<p>Works.</p>
<p>Another paragraph.</p>
<p>Third paragraph. This one is even longer. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quibusdam, fugiat facere? Animi, odit et tempore debitis modi quae eaque, libero, dolores magni, voluptas tenetur tempora quidem alias ut praesentium sed.</p>
<p>Fourth paragraph. This one is even longer. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quibusdam, fugiat facere? Animi, odit et tempore debitis modi quae eaque, libero, dolores magni, voluptas tenetur tempora quidem alias ut praesentium sed.</p>
<p>Fifth paragraph. This one is even longer. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quibusdam, fugiat facere? Animi, odit et tempore debitis modi quae eaque, libero, dolores magni, voluptas tenetur tempora quidem alias ut praesentium sed.</p>
</div>

【讨论】:

  • 这确实有效(正如它在 div 中所说的那样)并且它确实摆脱了其中一个嵌套级别。 10ms 是否只是“足够长让渲染器注意到,足够短以至于人类不会注意到”,还是这个数字有什么特别之处?除了更扁平的代码之外,使用这种方法还有其他好处吗?
  • 10ms 延迟没有什么特别之处。正如您正确推测的那样,它的目的是要足够长以确保确定高度已经被计算出来,但又足够短,以至于在动画开始之前人类不会看到明显的差异。
  • 您可以通过向foobardan 添加一个公共类,抓住NodeList,然后添加NodeList中每个节点的事件监听器,使用for loop
  • 另一个优点(我可以立即想到的)是它是 javascript,而不是 jQuery。有人会说这并不真正构成优势。各有千秋。
  • re:nesting:我不明白你会如何保存两个嵌套级别。它仍然是 nodeList[i]->clickHandler->timeoutCallback 而不是 elem->clickHandler->animationFrameCallback->animationFrameCallback,只有一层更平坦。 (之前我什至没有注意到您在 addEventListener 中使用匿名函数的额外级别)
【解决方案2】:

如果您在点击事件之前不知道目标元素的高度,只需在 DOM 准备好后检查它,并将每个目标元素设置为它自己的高度。 这样,当您单击目标元素时: *动画将起作用,因为元素的高度已经设置。 *点击事件会少执行一个命令。

如果你已经在使用 jQuery,你可以一直使用它:

$(document).ready(function(){ // use document ready so we can get the target elements height after they finished to load. 
    var $tran = $('.transition'); // get "transition" class. instead of using different class name for each element, i use unique class for the animation part, the change needs to be on the HTML and CSS too of course, you will see it in the example. 
    $tran.each(function(){ // run each loop so we can get each of the elements that have the class "transition", this is important when the elements height different from each other.
        var height = $(this).height(); // get the height of the current element in the loop. 
        $(this).height(height); // set the height of the current element in the loop. 
    });
    $tran.click(function(){ // set jQuery click event
        $(this).addClass('active'); // add the "active" class
    });
});

香草:

document.addEventListener('DOMContentLoaded', function(){ // use DOMContentLoaded so we can get the target elements height after they finished to load.
  var tran = document.getElementsByClassName('transition'); // get "transition" class.
  for ( let i = 0; i <= tran.length-1; i++ ){ // run for loop so we can get each of the elements that have the class "transition", this is important when the elements height different from each other.
    let height = tran[i].clientHeight; // get the height of the current element in the loop. 
    tran[i].style.height = height + 'px'; // set the height of the current element in the loop. 
    tran[i].addEventListener("click", function(ev){ // set click event
      this.className = this.className + " active"; // add the "active" class 
    });
  }
});

每个目标元素都有过渡类:

<div class="foo transition">
...
<div class="bar transition">
...  
<div class="dan transition">
... 

过渡部分移动到它自己的类中,因此如果您决定删除它或将其用于其他元素,您可以稍后从 HTML 中删除该类。 它不再在“活动”类上,因此它在没有与点击事件连接的情况下影响元素,通过这种方式,您可以使用例如toggleClass() 而不是addClass(),您将在点击时获得“打开”动画再次在元素上:

.foo, .bar, .dan {
  width: 20%;
  display: inline-block;
  overflow: hidden;
  vertical-align: top;
}
.transition {
  -webkit-transition: height 1000ms;
  transition: height 1000ms;
}
.active {
  height: 50px !important;
}

实时示例: https://jsfiddle.net/84joouov/ *在示例中,我通过将“use_jquery”变量设置为 true 或 false,让您从使用纯 JS 到 jQuery 中进行选择。

【讨论】:

  • 一旦元素的高度因任何原因发生变化,它就会崩溃。这就是示例使用 %-width 的原因,如果在单击之前调整浏览器大小,您可以在自己的演示中看到它。
  • 好吧,我不明白你在寻找响应的问题,我认为这是唯一的情况......你可以使用 resize() 函数,例如,如果你想获取窗口宽度变化的新高度...
  • onResize 增加了更多的负载,并没有抓住所有调整大小的原因。例如,如果 div 包含一个可以改变高度的孩子怎么办?这就是为什么我询问实施的这个非常具体的部分。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-22
相关资源
最近更新 更多