CSS display属性的转换

1932

我目前正在设计一个CSS“超级下拉”菜单-基本上是一个常规的仅使用CSS的下拉菜单,但其中包含不同类型的内容。

目前看来, CSS 3转换不适用于“display”属性,也就是说,您无法从“display: none”到“display: block”(或任何组合)进行任何过渡。

是否有一种方法可以使上面示例中的二级菜单在某人悬停在顶级菜单项上时“淡入”?

我知道您可以在“visibility:”属性上使用过渡,但我无法想出有效使用它的方法。

我还尝试使用高度,但那样只会失败。

我知道使用JavaScript轻松实现这一点,但我想挑战自己仅使用CSS,并且我认为我做得不够好。


32
position: absolute; visibility: hidden;与display: none;相同。 - Jawad
13
只有当你添加类似于 z-index:0 的内容时才行。 - DanMan
31
建议永远不要使用 visibility: hidden,除非你希望屏幕阅读器读取它(而典型的浏览器不会)。它仅定义元素的可见性(就像说 opacity: 0 一样),它仍然是可选、可点击和以前的任何状态;它只是不可见。 - Forest Katsch
2
IE 8、9、10不支持pointer-events,因此并不总是可行。 - Steven Pribilinskiy
5
你需要使用“display: none”,否则你可能会意外地显示隐藏的对象,而这些对象在触发器外面,我只是提醒一下 :) - Samuel Ramzan
显示剩余7条评论
40个回答

1720
你可以将两个或多个转换串联起来,这时visibility很有用。

div {
  border: 1px solid #eee;
}
div > ul {
  visibility: hidden;
  opacity: 0;
  transition: visibility 0s, opacity 0.5s linear;
}
div:hover > ul {
  visibility: visible;
  opacity: 1;
}
<div>
  <ul>
    <li>Item 1</li>
    <li>Item 2</li>
    <li>Item 3</li>
  </ul>
</div>

(不要忘记给transition属性添加厂商前缀。)
更多细节请参考这篇文章

38
这个方法的问题是,即使看不到,它后面的任何东西都会重叠。我发现使用height:0是一个更好的解决方案。 - Josh Bedo
990
这很好,但问题在于“visibility hidden”元素仍然占据空间,而“display none”则不会。 - Rui Marques
56
我可能漏掉了一些东西,但是为什么你要同时改变可见性和不透明度呢?将不透明度设置为0不会隐藏元素吗?为什么还需要将可见性设置为隐藏? - GMA
29
如果你只设置了透明度,元素实际上仍然在页面上渲染(例如,你不能单击它)。 - adriendenat
43
这个不应被标记为正确答案。它没有涉及到显示属性,正如Rui所说,该元素仍然占据空间,因此在许多情况下都不实用。 - Ian Steffy
显示剩余8条评论

982

为了使这个效果起作用,你需要通过其他方式隐藏元素。

我通过绝对定位两个

元素并将隐藏的一个设置为opacity: 0来实现效果。

如果你甚至从“none”切换display属性到“block”,则其他元素的过渡不会发生。

为了解决这个问题,始终允许元素显示为"display: block",但可以通过以下任何方式之一来隐藏元素:

  1. 将高度设置为0。
  2. 将透明度设置为0。
  3. 将元素定位在具有overflow: hidden的另一个元素框架之外。

可能还有更多的解决方案,但是如果你将元素切换为“display: none”,则无法执行过渡。例如,你可能尝试像这样的操作:

div {
    display: none;
    transition: opacity 1s ease-out;
    opacity: 0;
}
div.active {
    opacity: 1;
    display: block;
}

但那样不会起作用。从我的经验来看,我发现这样做什么也没做成。

因此,你始终需要保持元素的display: block属性 - 但是你可以通过像这样做某些事情来规避它:

div {
    transition: opacity 1s ease-out;
    opacity: 0;
    height: 0;
    overflow: hidden;
}
div.active {
    opacity: 1;
    height: auto;
}

39
感谢Jim提供详细的答案。你说得没错,如果display属性有任何变化,所有的转换效果都将无法工作。这很遗憾——我想知道背后的原因是什么。顺便说一句,在我最初的问题中发布的链接中,你可以看到我目前的进展。唯一(小)的问题是在Chrome [5.0.375.125]上,当页面加载时,你可以看到菜单在元素加载到页面上时迅速消失。Firefox 4.0b2和Safari 5.0都没有问题……这是个bug还是我漏掉了什么? - RichardTape
2
我实现了这个,但它没有起作用,后来我意识到你所说的是这个不会起作用。这个答案假设我们不只是在快速阅读重要部分... :) - Liam
这不起作用,因为你不能从“auto”转换高度。它必须是一个实际的数字。 - Jez
需要过渡的不是高度,而是不透明度。@Jez - xenooooo

372

在本文发布时,所有主流浏览器都会在尝试更改display属性时禁用CSS过渡效果,但CSS动画仍然可以正常工作,因此我们可以将其作为解决方法。

示例代码(您可以将其应用于菜单)演示

将以下CSS添加到样式表中:

@-webkit-keyframes fadeIn {
    from { opacity: 0; }
      to { opacity: 1; }
}
@keyframes fadeIn {
    from { opacity: 0; }
      to { opacity: 1; }
}

然后在父级悬停时将 fadeIn 动画应用于子元素(当然要设置 display: block):

.parent:hover .child {
    display: block;
    -webkit-animation: fadeIn 1s;
    animation: fadeIn 1s;
}

2019年更新 - 支持淡出效果的方法:

(需要一些JavaScript代码)

// We need to keep track of faded in elements so we can apply fade out later in CSS
document.addEventListener('animationstart', function (e) {
  if (e.animationName === 'fade-in') {
      e.target.classList.add('did-fade-in');
  }
});

document.addEventListener('animationend', function (e) {
  if (e.animationName === 'fade-out') {
      e.target.classList.remove('did-fade-in');
   }
});
div {
    border: 5px solid;
    padding: 10px;
}

div:hover {
    border-color: red;
}

.parent .child {
  display: none;
}

.parent:hover .child {
  display: block;
  animation: fade-in 1s;
}

.parent:not(:hover) .child.did-fade-in {
  display: block;
  animation: fade-out 1s;
}

@keyframes fade-in {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

@keyframes fade-out {
  from {
    opacity: 1;
  }
  to {
    opacity: 0;
  }
}
<div class="parent">
    Parent
    <div class="child">
        Child
    </div>
</div>


3
谢谢您。上面提到的height: 0技巧(用于过渡效果)似乎不起作用,因为在淡出转换时高度被设置为0,但是这个技巧似乎完全有效。 - Elliot Winkler
48
谢谢,非常有用。但是如何淡出它? - Illiou
2
这个答案的第一段并不太合理。浏览器不会在你使用display属性时立即完全禁用所有过渡 - 实际上没有理由这样做。即使它们这样做了,为什么动画还能工作呢?在CSS动画中也不能使用display属性。 - BoltClock
1
是的,“change” - 我不确定为什么我在那里说“use”。我的意思是,您无法转换或动画显示,但只要您不转换到“none”,就不会阻止所有其他属性进行动画处理。 - BoltClock
1
这对于鼠标悬停下拉菜单非常完美,高度不是问题。我的下拉菜单在父级导航项上悬停时淡入,鼠标移出时淡出。 - Andrew West
显示剩余5条评论

157

不要使用CSS中不存在的回调函数,而是可以使用transition-delay属性。

#selector {
    overflow: hidden;  /* Hide the element content, while height = 0 */
    height: 0;
    opacity: 0;
    transition: height 0ms 400ms, opacity 400ms 0ms;
}
#selector.visible {
    height: auto; opacity: 1;
    transition: height 0ms 0ms, opacity 600ms 0ms;
}

那么,这里发生了什么?

  1. 当添加visible类时,heightopacity都会立即启动动画(0ms延迟),尽管height需要0ms完成动画(相当于display:block),而opacity需要600ms。

  2. 当移除visible类时,opacity开始动画(0ms延迟,400ms持续时间),height等待400ms,然后立即(0ms)恢复初始值(在动画回调中相当于display:none)。

请注意,这种方法比使用visibility更好。在这种情况下,元素仍将占据页面上的空间,这并不总是适合的。

有关更多示例,请参阅本文


10
只有在使用height:100%的情况下才能起作用,但这可能会破坏某些布局。如果这不是问题,那么这是非常好的解决方案,也是少数可双向工作的解决方案之一。 - Fabian von Ellerts
5
你需要设置一个高度 - 设置height: auto会使其立即消失。 - El Mac
1
是的,我不知道为什么这个帖子有这么多赞,因为 height 无法过渡,所以它完全有问题。 - Jez
1
高度无法过渡到自动,但最大高度可以过渡到荒谬的值。 - senecaTheMeek

119

我怀疑如果更改display的值,过渡效果被禁用的原因是因为display实际上所做的事情。它不会改变任何可以被平滑动画化的东西。

display: none;visibility: hidden;是两个完全不同的概念。
两者都使元素不可见,但使用visibility: hidden;时,元素仍然在布局中呈现,只是不可见
隐藏元素仍占用空间,并以块、行内块或表格等display属性指定的方式呈现,并相应地占用空间。
其他元素不会自动移动以占据该空间。隐藏元素只是不向输出渲染其实际像素。

display: none;实际上完全阻止元素的呈现。
它不会占用任何布局空间。
其他本来将占据该元素空间的元素现在将调整以占据该空间,就好像元素根本不存在一样。

display不仅仅是另一个可视属性。
它确定了元素的整个呈现模式,例如它是blockinlineinline-blocktabletable-rowtable-celllist-item等等!
每个属性都有非常不同的布局影响,而且没有合理的方法可以将它们动画化或平滑地过渡(例如尝试想象从块到行内或反之间的平滑过渡!)。

这就是为什么在显示发生变化时(即使变化是从或到none),转换被禁用的原因——none不仅仅是看不见的,它是自己的元素渲染模式,意味着根本没有渲染!


这是正确的。虽然不是显而易见的,但一旦你考虑到它,你很快就会意识到转换display属性根本行不通。 - BoltClock
13
尽管上述解决方案很好,但得到一个合理的解释说明为什么转换不适用于显示属性是非常令人满意的。 - kqr
14
我不同意。这完全可以理解。如果在过渡开始时,从display:none到display:block可以立即发生,那就太好了。而且,对于向后过渡,如果在过渡结束时从display:block到display:none,那就完美了。 - Curtis Yallop
有用的功能是如果过渡支持像“display”这样的即时更改,但仍可以使用延迟属性,以便更改在一定时间后发生。 - Jez

64

我找到了更好的解决方法,你可以使用CSS动画来展示物品并制作令人惊叹的效果。

.item {
     display: none;
}

.item:hover {
     display: block;
     animation: fade_in_show 0.5s
}

@keyframes fade_in_show {
     0% {
          opacity: 0;
          transform: scale(0)
     }

     100% {
          opacity: 1;
          transform: scale(1)
     }
}

4
很遗憾,它无法实现淡出效果。 - maxime schoeni

61

display不是可以进行过渡的CSS属性之一。

请参阅可过渡的 CSS 属性以获取可以应用过渡效果的 CSS 属性列表。请参阅CSS Values and Units Module Level 4, Combining Values: Interpolation, Addition, and Accumulation了解它们如何插值。

CSS 3 将其列在9.1.CSS 中的属性中(只需关闭警告弹出窗口)

我也尝试使用高度,但结果很糟糕。

上次我这样做时,我使用了max-height,这是一个可过渡的属性(虽然有点小技巧,但确实有效),但请注意,对于复杂页面或低端移动设备用户可能会非常卡顿。


40

现在您可以向块属性添加自定义动画。

@keyframes showNav {
  from {opacity: 0;}
  to {opacity: 1;}
}
.subnav-is-opened .main-nav__secondary-nav {
  display: block;
  animation: showNav 250ms ease-in-out both;
}

演示

在这个演示中,子菜单从display:none变为display:block,仍然成功实现了淡入淡出效果。


30
除非我有所遗漏,否则“演示”链接不再显示子菜单转换。 - Realistic

38

使用CSS动画进行淡入:

.item {
     display: none;
}

.item:hover {
     display: block;
     animation: fadeIn 0.5s;
}

@keyframes fadeIn {
     from {
          opacity: 0;
     }

     to {
          opacity: 1;
     }
}

28

根据W3C 2013年11月19日工作草案display不是可动画化的属性。幸运的是,visibility是可动画化的。您可以将其转换与透明度的转换进行链接(参见JSFiddle):

  • HTML:

  • <a href="http://example.com" id="foo">Foo</a>
    <button id="hide-button">Hide</button>
    <button id="show-button">Show</button>
    
  • CSS:

    #foo {
        transition-property: visibility, opacity;
        transition-duration: 0s, 1s;
    }
    
    #foo.hidden {
        opacity: 0;
        visibility: hidden;
        transition-property: opacity, visibility;
        transition-duration: 1s, 0s;
        transition-delay: 0s, 1s;
    }
    
  • 用于测试的JavaScript:

  • var foo = document.getElementById('foo');
    
    document.getElementById('hide-button').onclick = function () {
        foo.className = 'hidden';
    };
    
    document.getElementById('show-button').onclick = function () {
        foo.className = '';
    };
    
    请注意,如果您只是将链接设置为透明,而没有设置visibility:hidden,则该链接仍然可以被点击。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接