我目前正在设计一个CSS“巨型下拉菜单”——基本上是一个常规的仅限CSS的下拉菜单,但包含不同类型的内容。

目前,CSS 3转换似乎不适用于“display”属性,也就是说,您不能进行从display:none到display:block(或任何组合)的任何转换。

当有人悬停在一个顶级菜单项上时,上面示例中的第二层菜单是否有办法“淡入”?

我知道你可以在visibility:属性上使用转换,但我想不出一种有效的方法。

我也尝试过使用高度,但那只是失败得很惨。

我也意识到,使用JavaScript实现这一点很简单,但我想挑战一下自己,只使用CSS,我觉得我做得有点短。


当前回答

我找到了更好的方法来解决这个问题,你可以使用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)
     }
}

其他回答

不需要JavaScript,也不需要惊人的最大高度。相反,在文本元素上设置最大高度,并使用字体相对单位,例如rem或em。这样,您可以设置比容器更大的最大高度,同时避免菜单关闭时出现延迟或“弹出”:

HTML

<nav>
  <input type="checkbox" />
  <ul>
    <li>Link 1</li>
    <li>Link 1</li>
    <li>Link 1</li>
    <li>Link 1</li>
  </ul>
</nav>

CSS

nav input + ul li { // Notice I set max-height on li, not ul
   max-height: 0;
}

nav input:checked + ul li {
   max-height: 3rem; // A little bigger to allow for text-wrapping - but not outrageous
}

请参阅此处的示例:http://codepen.io/mindfullsilence/pen/DtzjE

我怀疑,如果显示发生变化,转换被禁用的原因是因为显示实际的功能。它不会改变任何可以想象的流畅动画。

显示:无;可见性:隐藏;是两件完全不同的事情。两者都具有使元素不可见的效果,但具有可见性:隐藏;它仍然呈现在布局中,但只是不明显。隐藏元素仍然占用空间,并且仍然以内联或块、块内联或表的形式呈现,或者以显示元素告诉的方式呈现,并相应地占用空间。其他元素不会自动移动以占用该空间。隐藏元素只是不将其实际像素渲染到输出。

display:另一方面,没有一个实际上阻止元素完全渲染。它不占用任何布局空间。其他原本占据该元素占用的部分或全部空间的元素现在调整为占据该空间,就好像该元素根本不存在一样。

显示不仅仅是另一种视觉属性。它建立了元素的整个呈现模式,例如它是块、内联、内联块、表、表行、表单元格、列表项还是其他任何元素!每一个都有非常不同的布局影响,并且没有合理的方法来动画化或平滑地转换它们(例如,尝试想象从块到内联的平滑转换,反之亦然!)。

这就是为什么如果显示更改(即使更改为none或从none更改为none-none不仅仅是不可见的,它本身的元素渲染模式意味着根本没有渲染!),则会禁用转换。

显示不是转换工作的财产之一。

有关可以应用转换的CSS财产的列表,请参阅动画CSS财产。请参阅CSS值和单位模块第4级,组合值:插值、相加和累加,了解如何插值。

9.1中列出了CSS 3。来自CSS的财产(只需关闭警告弹出窗口)

我也尝试过使用高度,但那只是失败得很惨。

上次我不得不这样做时,我使用了max height,这是一个可设置动画的属性(虽然它有点黑客,但确实有效),但要注意,对于复杂的页面或使用低端移动设备的用户来说,它可能非常刺耳。

我怀疑任何刚开始CSS转换的人都会很快发现,如果你同时修改显示属性(block/none),它们就不起作用了。一个尚未提及的解决方法是,您可以继续使用display:block/none来隐藏/显示元素,但将其不透明度设置为0,这样即使在显示为display:block时,它仍然是不可见的。

然后添加另一个CSS类,例如“on”,将不透明度设置为1,并定义不透明度的转换。正如您可能想象的那样,您必须使用JavaScript将“on”类添加到元素中,但至少您仍在使用CSS进行实际转换。

P.S.如果您发现自己需要同时执行display:block和添加class“on”,请使用setTimeout延迟后者。否则,浏览器只会同时看到这两种情况,并禁用转换。

你可以让它以你所期望的自然方式工作-使用显示-但你必须限制浏览器才能让它工作,要么使用Javascript,要么像其他人建议的那样,使用一个标签放在另一个标签内的花式技巧。我不关心内部标记,因为它会使CSS和维度更加复杂,所以这里是Javascript解决方案:

https://jsfiddle.net/b9chris/hweyecu4/17/

以如下框开头:

<div id="box" class="hidden">Lorem</div>

一个隐藏的盒子。

div.hidden {
    display: none;
}
#box {
    transition: opacity 1s;
}
    

我们将使用相关问答中的技巧,检查offsetHeight以立即限制浏览器:

https://stackoverflow.com/a/16575811/176877

首先,库将上述技巧正规化:

$.fn.noTrnsn = function () {
    return this.each(function (i, tag) {
        tag.style.transition = 'none';
    });
};
$.fn.resumeTrnsn = function () {
    return this.each(function (i, tag) {
        tag.offsetHeight;    
        tag.style.transition = null;
    });
};

接下来,我们将使用它来显示一个框,并将其淡入:

$('#button').on('click', function() {
    var tag = $('#box');
    if (tag.hasClass('hidden'))
        tag.noTrnsn().removeClass('hidden')
        .css({ opacity: 0 })
        .resumeTrnsn().css({ opacity: 1 });
    else
        tag.css({ opacity: 0 });
});

这会使盒子变暗。因此,.noTrnsn()关闭了转换,然后我们删除了隐藏类,该类将显示从“无”转换为默认的“块”。然后,我们将不透明度设置为0,为淡入做好准备。现在,我们已经设置了阶段,我们使用.resumeTrnsn()重新打开过渡。最后,通过将不透明度设为1开始过渡。

如果没有该库,对显示的更改和对不透明度的更改都会导致不理想的结果。如果我们简单地删除了库调用,我们根本不会得到转换。

请注意,上述操作不会在渐变动画结束时再次将显示设置为无。不过我们可以变得更漂亮。让我们用一个从0开始逐渐变高的渐变。

想要

https://jsfiddle.net/b9chris/hweyecu4/22/

#box {
    transition: height 1s, opacity 1s;
}

我们现在正在转换高度和不透明度。注意,我们没有设置高度,这意味着它是默认值,自动。传统上,这是无法转换的——从自动转换为像素值(如0)不会导致转换。我们将使用库和另一个库方法来解决这个问题:

$.fn.wait = function (time, fn) {
    if (time)
        this.delay(time);
    if (!fn)
        return this;

    var _this = this;
    return this.queue(function (n) {
        fn.call(_this);
        n();
    });
};

这是一种方便的方法,可以让我们参与jQuery现有的fx/animation队列,而不需要jQuery3.x中现在排除的任何动画框架。

让我们设置向下滑动效果的动画。

$('#button').on('click', function() {
    var tag = $('#box');
    if (tag.hasClass('hidden')) {
        // Open it
        // Measure it
        tag.stop().noTrnsn().removeClass('hidden').css({
            opacity: 0, height: 'auto'
        });
        var h = tag.height();
        tag.css({ height: 0 }).resumeTrnsn()
        // Animate it
        .css({ opacity: 1, height: h })
        .wait(1000, function() {
            tag.css({ height: 'auto' });
        });
    } else {
        // Close it
        // Measure it
        var h = tag.noTrnsn().height();
        tag.stop().css({ height: h })
        .resumeTrnsn()
        // Animate it
        .css({ opacity: 0, height: 0 })
        .wait(1000, function() {
            tag.addClass('hidden');
        });
    }
});

这段代码首先检查#box以及它当前是否隐藏,然后检查它的类。但是,它使用wait()库调用实现了更多的功能,在滑动/淡出动画的末尾添加隐藏类,如果它实际上是隐藏的,您可以找到隐藏类-这是上面的简单示例无法做到的。这也会启用反复显示/隐藏元素,这在前面的示例中是一个错误,因为隐藏类从未恢复。

您还可以看到在.noTrnsn()之后调用CSS和类更改,以通常设置动画的阶段,包括进行测量,例如在调用.resumeTrnsn()之前测量#box的最终高度,而不向用户显示,并将其从完全设置的阶段动画化为其目标CSS值。

旧答案

https://jsfiddle.net/b9chris/hweyecu4/1/

您可以通过以下方式在单击时进行转换:

function toggleTransition() {
  var el = $("div.box1");

  if (el.length) {
    el[0].className = "box";
    el.stop().css({maxWidth: 10000}).animate({maxWidth: 10001}, 2000, function() {
        el[0].className = "box hidden";
    });
  } else {
    el = $("div.box");
    el[0].className = "box";
    el.stop().css({maxWidth: 10001}).animate({maxWidth: 10000}, 50, function() {
        el[0].className = "box box1";
    });
  }

  return el;
}

someTag.click(toggleTransition);

CSS就是你猜到的:

.hidden {
    display: none;
}
.box {
    width: 100px;
    height: 100px;
    background-color: blue;
    color: yellow;
    font-size: 18px;
    left: 20px;
    top: 20px;
    position: absolute;
    -webkit-transform-origin: 0 50%;
    transform-origin: 0 50%;
    -webkit-transform: scale(.2);
    transform: scale(.2);
    -webkit-transition: transform 2s;
    transition: transform 2s;
}
.box1{
    -webkit-transform: scale(1);
    transform: scale(1);
}

关键是限制显示属性。通过删除隐藏的类,然后等待50毫秒,然后通过添加的类开始转换,我们可以让它出现,然后像我们想要的那样展开,而不是只在屏幕上闪烁而没有任何动画。类似的情况会以相反的方式发生,只是我们要等到动画结束后再应用隐藏。

注意:我在这里滥用.animate(maxWidth)来避免setTimeout竞争条件。setTimeout很快就会在你或其他人不知道的情况下拾取代码时引入隐藏的错误。animate()可以很容易地用.stop()杀死。我只是用它在标准fx队列上设置50毫秒或2000毫秒的延迟,在这里,其他基于此构建的程序员很容易发现/解决。