向引导下拉菜单添加幻灯片效果

Posted

技术标签:

【中文标题】向引导下拉菜单添加幻灯片效果【英文标题】:Adding a slide effect to bootstrap dropdown 【发布时间】:2012-08-20 09:32:36 【问题描述】:

我正在使用bootstrap,我想将动画添加到下拉列表中。我想为它添加一个动画,离开它时向下滑动并向后滑动。 我怎么能这样做?

我尝试过的事情:

像这样更改 Js 下拉文件:

How can I make Bootstrap's navigation dropdown slide smoothly up and down?

【问题讨论】:

你是否在某处包含了 bootstrap-dropdown.js?我没有在标题中看到它……而且这些蛋糕。我现在好饿! 嗯,它在 boostrap.js 中,我确实包含了它,但它没有用,所以我删除了它,但我重新添加了它,是的,如果我删除了迷你版,它根本不起作用 编辑:哦,是的,包括在内。 嗯。默认下拉菜单不滑动;在修改后的版本上(来自链接),有一个 $el.parent().slideToggle();它调用了一个 JQuery 动画。 顺便说一句,我不明白为什么你包含引导程序 2 次。 bootstrap.min.js 是缩小版。 【参考方案1】:

对于Bootstrap 5,一个简单漂亮的动画幻灯片可以用一个简单的keframe动画来完成。

@keyframes slideIn 
    0% 
        transform: translateY(1rem);
        opacity: 0;
    
    100% 
        transform: translateY(0rem);
        opacity: 1;
    


.slideIn 
    -webkit-animation-name: slideIn;
    animation-name: slideIn;
    animation-duration: 0.4s;
    animation-fill-mode: both;
<li class="nav-item dropdown">
          <a class="nav-link dropdown-toggle m-2" href="/" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
            Dropdown
          </a>
          <ul class="dropdown-menu slideIn rounded-2 p-3" aria-labelledby="navbarDropdown">
            <li><a class="dropdown-item" href="/">Action</a></li>
            <li><a class="dropdown-item" href="/">Another action</a></li>
            <li><hr class="dropdown-divider"/></li>
            <li><a class="dropdown-item" href="/">Something else here</a></li>
          </ul>
        </li>

【讨论】:

【参考方案2】:

我建议使用 transform 而不是 max-height,它更快,而且 GPU 加速。

对于 Bootstrap 5,添加以下 CSS:

 .dropdown .dropdown-menu 
    -webkit-transition: all 0.32s;
    -moz-transition: all 0.32s;
    -ms-transition: all 0.32s;
    -o-transition: all 0.32s;
    transition: all 0.32s;

    display: block;
    overflow: hidden;
    opacity: 0;
    transform: translateX(-25%) scaleY(0);
    transform-origin: top;
  

  .dropdown-menu.show 
    opacity: 1;
    transform: translateX(-25%) scaleY(1);
  

【讨论】:

【参考方案3】:

简介

截至撰写本文时,最初的答案已经 8 岁了。我仍然觉得原始问题还没有适当的解决方案。

Bootstrap 从那时起已经走了很长一段路,现在是 4.5.2。这个答案解决了这个版本。

到目前为止所有其他解决方案的问题

所有其他答案的问题是,当他们连接到 show.bs.dropdown / hide.bs.dropdown 时,后续事件 shown.bs.dropdown / hidden.bs.dropdown 要么被触发得太早(动画仍在进行中),要么他们没有根本不开火,因为它们被压制了 (e.preventDefault())。

干净的解决方案

由于 show()hide() 在 Bootstraps Dropdown 类中的实现有一些相似之处,我在模仿原始行为时将它们组合在 toggleDropdownWithAnimation() 中,并在 showDropdownWithAnimation()hideDropdownWithAnimation().toggleDropdownWithAnimation() 创建一个 shown.bs.dropdown / hidden.bs.dropdown 事件,方法与 Bootstrap 相同。然后在动画完成后触发此事件 - 正如您所期望的那样。

/**
 * Toggle visibility of a dropdown with slideDown / slideUp animation.
 * @param JQuery $containerElement The outer dropdown container. This is the element with the .dropdown class.
 * @param boolean show Show (true) or hide (false) the dropdown menu.
 * @param number duration Duration of the animation in milliseconds
 */
function toggleDropdownWithAnimation($containerElement, show, duration = 300): void 
    // get the element that triggered the initial event
    const $toggleElement = $containerElement.find('.dropdown-toggle');
    // get the associated menu
    const $dropdownMenu = $containerElement.find('.dropdown-menu');
    // build jquery event for when the element has been completely shown
    const eventArgs = relatedTarget: $toggleElement;
    const eventType = show ? 'shown' : 'hidden';
    const eventName = `$eventType.bs.dropdown`;
    const jQueryEvent = $.Event(eventName, eventArgs);

    if (show) 
        // mimic bootstraps element manipulation
        $containerElement.addClass('show');
        $dropdownMenu.addClass('show');
        $toggleElement.attr('aria-expanded', 'true');
        // put focus on initial trigger element
        $toggleElement.trigger('focus');
        // start intended animation
        $dropdownMenu
            .stop() // stop any ongoing animation
            .hide() // hide element to fix initial state of element for slide down animation
            .slideDown(duration, () => 
            // fire 'shown' event
            $($toggleElement).trigger(jQueryEvent);
        );
    
    else 
        // mimic bootstraps element manipulation
        $containerElement.removeClass('show');
        $dropdownMenu.removeClass('show');
        $toggleElement.attr('aria-expanded', 'false');
        // start intended animation
        $dropdownMenu
            .stop() // stop any ongoing animation
            .show() // show element to fix initial state of element for slide up animation
            .slideUp(duration, () => 

            // fire 'hidden' event
            $($toggleElement).trigger(jQueryEvent);
        );
    


/**
 * Show a dropdown with slideDown animation.
 * @param JQuery $containerElement The outer dropdown container. This is the element with the .dropdown class.
 * @param number duration Duration of the animation in milliseconds
 */
function showDropdownWithAnimation($containerElement, duration = 300) 
    toggleDropdownWithAnimation($containerElement, true, duration);


/**
 * Hide a dropdown with a slideUp animation.
 * @param JQuery $containerElement The outer dropdown container. This is the element with the .dropdown class.
 * @param number duration Duration of the animation in milliseconds
 */
function hideDropdownWithAnimation($containerElement, duration = 300) 
    toggleDropdownWithAnimation($containerElement, false, duration);

绑定事件监听器

现在我们已经编写了适当的回调来显示/隐藏带有动画的下拉菜单,让我们实际将它们绑定到正确的事件。

我在其他答案中经常看到的一个常见错误是将事件侦听器直接绑定到元素。虽然这适用于注册事件侦听器时存在的 DOM 元素,但它不会绑定到稍后添加的元素。

这就是为什么你通常最好直接绑定到document

$(function () 

    /* Hook into the show event of a bootstrap dropdown */
    $(document).on('show.bs.dropdown', '.dropdown', function (e) 
        // prevent bootstrap from executing their event listener
        e.preventDefault();
        
        showDropdownWithAnimation($(this));
    );

    /* Hook into the hide event of a bootstrap dropdown */
    $(document).on('hide.bs.dropdown', '.dropdown', function (e) 
        // prevent bootstrap from executing their event listener
        e.preventDefault();
          
        hideDropdownWithAnimation($(this));
    );

);

【讨论】:

【参考方案4】:

还可以通过将这段代码添加到您的样式中来避免使用 javascript 来实现下拉效果,并使用 CSS3 过渡:

.dropdown .dropdown-menu 
    -webkit-transition: all 0.3s;
    -moz-transition: all 0.3s;
    -ms-transition: all 0.3s;
    -o-transition: all 0.3s;
    transition: all 0.3s;

    max-height: 0;
    display: block;
    overflow: hidden;
    opacity: 0;


.dropdown.open .dropdown-menu  /* For Bootstrap 4, use .dropdown.show instead of .dropdown.open */
    max-height: 300px;
    opacity: 1;

这种方式的唯一问题是您应该手动指定最大高度。如果你设置一个很大的值,你的动画会很快。

如果您知道下拉菜单的大致高度,它就像一个魅力,否则您仍然可以使用 javascript 设置精确的最大高度值。

这是一个小例子:DEMO


!此解决方案中存在填充小错误,请查看 Jacob Stamm 的解决方案评论。

【讨论】:

不错的解决方案,欣赏。 我喜欢这个,但不幸的是滑下动画只能工作一次。如果我关闭菜单并再次打开它而不刷新页面,它会立即打开。其他人有这个问题吗? 很好的解决方案。不幸的是,有一个小错误。下拉列表中的第一个项目仍然是可点击的,即使它已折叠。您可以通过将鼠标悬停在折叠的下拉列表下方来判断。在这种情况下没什么大不了的,因为您的第一个选项是纯文本。然而,当它是一个链接时,它就是一个问题。我的解决方案也是“动画”可见性,但是在其他东西完成动画后延迟发生。看看:jsfiddle.net/stamminator/kjLe1tfs/1 感谢您指出该错误,Jacob。看起来这是列表中填充的问题。如果您不介意,我会添加您的解决方案来回答。 还可以将pointer-events:none 添加到折叠版本中,然后在菜单中添加point-events: all 后添加.show【参考方案5】:

对于 Bootstrap 3,上述答案的这种变化使移动 slideUp() 动画更流畅;上面的答案有断断续续的动画,因为 Bootstrap 立即从切换的父级中删除了 .open 类,所以这段代码恢复了类,直到 slideUp() 动画完成。

// Add animations to topnav dropdowns
// based on https://***.com/a/19339162
// and https://***.com/a/52231970

$('.dropdown')
  .on('show.bs.dropdown', function() 
    $(this).find('.dropdown-menu').first().stop(true, true).slideDown(300);
  )
  .on('hide.bs.dropdown', function() 
    $(this).find('.dropdown-menu').first().stop(true, false).slideUp(300, function() 
      $(this).parent().removeClass('open');
    );
  )
  .on('hidden.bs.dropdown', function() 
    $(this).addClass('open');
  );

主要区别:

hide.bs.dropdown 事件处理程序中,我使用 .stop() 的默认值 (false) 作为其第二个参数 (jumpToEnd) hidden.bs.dropdown 事件处理程序将.open 类恢复到下拉切换的父级,并且在第一次删除该类后几乎立即执行此操作。与此同时,slideUp() 动画仍在运行,就像上面的答案一样,它的“the-animation-is-completed”回调负责最终从其父级中删除 .open 类。 方法链接在一起,因为每个事件处理程序的选择器是相同的

【讨论】:

【参考方案6】:

我不知道我是否可以解决这个问题,但我想出了一个快速修复开放类删除太快时发生的视觉错误的方法。基本上,它只是在 slideUp 事件中添加一个 OnComplete 函数并重置所有活动的类和属性。是这样的:

结果如下:Bootply example

Javascript/Jquery:

$(function()
    // ADD SLIDEDOWN ANIMATION TO DROPDOWN //
    $('.dropdown').on('show.bs.dropdown', function(e)
        $(this).find('.dropdown-menu').first().stop(true, true).slideDown();
    );

    // ADD SLIDEUP ANIMATION TO DROPDOWN //
    $('.dropdown').on('hide.bs.dropdown', function(e)
        e.preventDefault();
        $(this).find('.dropdown-menu').first().stop(true, true).slideUp(400, function()
            //On Complete, we reset all active dropdown classes and attributes
            //This fixes the visual bug associated with the open class being removed too fast
            $('.dropdown').removeClass('show');
            $('.dropdown-menu').removeClass('show');
            $('.dropdown').find('.dropdown-toggle').attr('aria-expanded','false');
        );
    );
);

【讨论】:

演示中有一个菜单在您连续单击几个后卡在打开状态。 @DOTang 是的,我想它并不完美,因此快速修复,我想稍微重构一下会更好。希望当 Bootstrap4 出来时,他们会修复它【参考方案7】:

BOOTSTRAP 3 参考

添加是因为我一直被此线程中的解决方案所吸引,并且每次都让我感到厌烦。

基本上 BS 下拉菜单会立即从父级中删除 .open 类,因此向上滑动不起作用。

使用与其他解决方案相同的位作为 slideDown();

// ADD SLIDEUP ANIMATION TO DROPDOWN //
$('.dropdown').on('hide.bs.dropdown', function(e)
  e.preventDefault();
  $(this).find('.dropdown-menu').first().stop(true, true).slideUp(300, function()
    $(this).parent().removeClass('open');
  );
);

【讨论】:

【参考方案8】:

更新 2018 Bootstrap 4

在 Boostrap 4 中,.open 类已替换为 .show。我想只使用 CSS 转换来实现这一点,而不需要额外的 JS 或 jQuery...

.show > .dropdown-menu 
  max-height: 900px;
  visibility: visible;


.dropdown-menu 
  display: block;
  max-height: 0;
  visibility: hidden;
  transition: all 0.5s ease-in-out;
  overflow: hidden;

演示:https://www.codeply.com/go/3i8LzYVfMF

注意:max-height 可以设置为足以容纳下拉内容的任何大值。

【讨论】:

这非常有效。对我来说,我什至不需要指定最大高度,因为我试图使用 margin-top 实现不同的效果。 我喜欢这个,你只需要css而不需要一堆js代码..【参考方案9】:

我正在做类似的事情,但在悬停而不是点击时。这是我正在使用的代码,你可以稍微调整一下让它在点击时工作

$('.navbar .dropdown').hover(function() 
  $(this).find('.dropdown-menu').first().stop(true, true).delay(250).slideDown();
, function() 
  $(this).find('.dropdown-menu').first().stop(true, true).delay(100).slideUp()
);

【讨论】:

这很好用!但是有没有办法在鼠标悬停而不是点击时使用它? 我上面贴的代码是鼠标悬停而不是点击。 好一个!谢谢。 不适合我。我在&lt;script&gt; 标签下添加了这个,但什么也没发生【参考方案10】:

这是一个使用jQuery 的简单解决方案,效果很好:

$('.dropdown-toggle').click(function () 
    $(this).next('.dropdown-menu').slideToggle(300);
);

$('.dropdown-toggle').focusout(function () 
    $(this).next('.dropdown-menu').slideUp(300);
)

滑动动画切换发生在点击时,它总是在失去焦点时向后滑动。

300 值更改为您想要的任何值,数字越小动画越快。

编辑:

此解决方案仅适用于桌面视图。它需要进一步修改才能在移动设备上很好地显示。

【讨论】:

这对我有用,但是当我将鼠标移到子菜单上(即远离dropdown-toggle)后,它又消失了,这意味着我无法选择子菜单项【参考方案11】:

如果您更新到 Bootstrap 3 (BS3),他们会公开许多 Javascript 事件,这些事件可以很好地将您所需的功能绑定到其中。在 BS3 中,此代码将为您的所有下拉菜单提供您正在寻找的动画效果:

  // Add slideDown animation to Bootstrap dropdown when expanding.
  $('.dropdown').on('show.bs.dropdown', function() 
    $(this).find('.dropdown-menu').first().stop(true, true).slideDown();
  );

  // Add slideUp animation to Bootstrap dropdown when collapsing.
  $('.dropdown').on('hide.bs.dropdown', function() 
    $(this).find('.dropdown-menu').first().stop(true, true).slideUp();
  );

您可以阅读 BS3 事件 here,特别是下拉事件 here。

【讨论】:

我在此部分的 slideUp 部分遇到了问题。在折叠模式 (Chrome) 下,下拉菜单的宽度会返回到与全屏查看时一样的宽度。使用 FF 时,它会向下滑动,但会隐藏而不是向上滑动。 我也看到了这个问题,我实际上创建了另一个线程专门询问它......***.com/questions/26267207/… 但我不知道如何解决它。我觉得 Dropdown 类在处理 hidden.bs.dropdown 事件之前没有等待转换完成。 这行得通,可以满足我的要求,但在移动视口上,子菜单的下拉下拉菜单消失得很快,看起来不连贯——不过感谢分享! 我无法让它工作。我不断收到以下错误:“Uncaught TypeError: $(...).find(...).first(...).stop is not a function”。我不明白 .stop() 如何不是函数,除非在触发事件时动画还没有开始?我错过了什么? 请参阅 Slipoch 的答案以进一步了解移动修复。【参考方案12】:

扩展答案,是我的第一个答案,如果之前没有足够的细节,请原谅。

对于 Bootstrap 3.x,我个人更喜欢 CSS 动画,并且我一直在使用 animate.css 以及 Bootstrap Dropdown Javascript Hooks。虽然它可能不会产生您想要的效果,但它是一种非常灵活的方法。

第 1 步:将 animate.css 添加到带有 head 标签的页面:

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.4.0/animate.min.css">

第 2 步:在触发器上使用标准 Bootstrap html

<div class="dropdown">
  <button type="button" data-toggle="dropdown">Dropdown trigger</button>

  <ul class="dropdown-menu">
    ...
  </ul>
</div>

第 3 步: 然后在 dropdrop-menu 元素中添加 2 个自定义数据属性; data-dropdown-in 用于 in 动画和 data-dropdown-out 用于 out 动画。这些可以是任何 animate.css 效果,如fadeIn 或fadeOut

<ul class="dropdown-menu" data-dropdown-in="fadeIn" data-dropdown-out="fadeOut">
......
</ul>

第 4 步:接下来添加以下 Javascript 以读取 data-dropdown-in/out 数据属性并对 Bootstrap Javascript API 挂钩/事件作出反应 (http://getbootstrap.com/javascript/#dropdowns-events):

var dropdownSelectors = $('.dropdown, .dropup');

// Custom function to read dropdown data
// =========================
function dropdownEffectData(target) 
  // @todo - page level global?
  var effectInDefault = null,
      effectOutDefault = null;
  var dropdown = $(target),
      dropdownMenu = $('.dropdown-menu', target);
  var parentUl = dropdown.parents('ul.nav'); 

  // If parent is ul.nav allow global effect settings
  if (parentUl.size() > 0) 
    effectInDefault = parentUl.data('dropdown-in') || null;
    effectOutDefault = parentUl.data('dropdown-out') || null;
  

  return 
    target:       target,
    dropdown:     dropdown,
    dropdownMenu: dropdownMenu,
    effectIn:     dropdownMenu.data('dropdown-in') || effectInDefault,
    effectOut:    dropdownMenu.data('dropdown-out') || effectOutDefault,  
  ;


// Custom function to start effect (in or out)
// =========================
function dropdownEffectStart(data, effectToStart) 
  if (effectToStart) 
    data.dropdown.addClass('dropdown-animating');
    data.dropdownMenu.addClass('animated');
    data.dropdownMenu.addClass(effectToStart);    
  


// Custom function to read when animation is over
// =========================
function dropdownEffectEnd(data, callbackFunc) 
  var animationEnd = 'webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend';
  data.dropdown.one(animationEnd, function() 
    data.dropdown.removeClass('dropdown-animating');
    data.dropdownMenu.removeClass('animated');
    data.dropdownMenu.removeClass(data.effectIn);
    data.dropdownMenu.removeClass(data.effectOut);

    // Custom callback option, used to remove open class in out effect
    if(typeof callbackFunc == 'function')
      callbackFunc();
    
  );


// Bootstrap API hooks
// =========================
dropdownSelectors.on(
  "show.bs.dropdown": function () 
    // On show, start in effect
    var dropdown = dropdownEffectData(this);
    dropdownEffectStart(dropdown, dropdown.effectIn);
  ,
  "shown.bs.dropdown": function () 
    // On shown, remove in effect once complete
    var dropdown = dropdownEffectData(this);
    if (dropdown.effectIn && dropdown.effectOut) 
      dropdownEffectEnd(dropdown, function() ); 
    
  ,  
  "hide.bs.dropdown":  function(e) 
    // On hide, start out effect
    var dropdown = dropdownEffectData(this);
    if (dropdown.effectOut) 
      e.preventDefault();   
      dropdownEffectStart(dropdown, dropdown.effectOut);   
      dropdownEffectEnd(dropdown, function() 
        dropdown.dropdown.removeClass('open');
      ); 
        
  , 
);

第 5 步(可选):如果您想加快或更改动画,可以使用 CSS 进行,如下所示:

.dropdown-menu.animated 
  /* Speed up animations */
  -webkit-animation-duration: 0.55s;
  animation-duration: 0.55s;
  -webkit-animation-timing-function: ease;
  animation-timing-function: ease;

写了一篇更详细的文章,如果有人感兴趣,可以下载: 文章:http://bootbites.com/tutorials/bootstrap-dropdown-effects-animatecss

希望对您有所帮助,并且第二篇文章的详细程度达到了所需的水平 汤姆

【讨论】:

最好在你的答案中至少包含一些细节 - 仅仅链接到你的网站并不是一个很好的答案。 @GeraldSchneider 更详细地更新了答案。是我在 *** 上的第一个答案,非常感谢您的反馈。【参考方案13】:

点击后可以使用以下代码完成

$('.dropdown-toggle').click(function() 
  $(this).next('.dropdown-menu').slideToggle(500);
);

【讨论】:

这很好用,但是当你失去焦点时,下拉菜单保持打开状态,这是不正常的。【参考方案14】:

这是我的幻灯片和淡入淡出效果的解决方案:

   // Add slideup & fadein animation to dropdown
   $('.dropdown').on('show.bs.dropdown', function(e)
      var $dropdown = $(this).find('.dropdown-menu');
      var orig_margin_top = parseInt($dropdown.css('margin-top'));
      $dropdown.css('margin-top': (orig_margin_top + 10) + 'px', opacity: 0).animate('margin-top': orig_margin_top + 'px', opacity: 1, 300, function()
         $(this).css('margin-top':'');
      );
   );
   // Add slidedown & fadeout animation to dropdown
   $('.dropdown').on('hide.bs.dropdown', function(e)
      var $dropdown = $(this).find('.dropdown-menu');
      var orig_margin_top = parseInt($dropdown.css('margin-top'));
      $dropdown.css('margin-top': orig_margin_top + 'px', opacity: 1, display: 'block').animate('margin-top': (orig_margin_top + 10) + 'px', opacity: 0, 300, function()
         $(this).css('margin-top':'', display:'');
      );
   );

【讨论】:

谢谢。您的代码完美运行。但是我不太明白为什么我们需要$(this).css('margin-top':'');@Vedmant 这是在动画完成后删除 margin-top 参数,以防下拉菜单在自定义样式中添加了一些边距(如在我的主题中)。它必须在没有它的情况下工作,因为它动画到 orig_margin_top 回来,但在动画完成后不要留下额外的内联样式会更干净。【参考方案15】:
$('.navbar .dropdown').hover(function() 
    $(this).find('.dropdown-menu').first().stop(true, true).slideDown();
, function() 
    $(this).find('.dropdown-menu').first().stop(true, true).slideUp();
);

如果您想在悬停时显示下拉菜单,则此代码有效。

我只是将.slideToggle 更改为.slideDown.slideUp,并删除了(400) 计时

【讨论】:

【参考方案16】:

我正在使用上面的代码,但是我通过slideToggle更改了延迟效果。

它会在鼠标悬停时滑动下拉菜单。

$('.navbar .dropdown').hover(function() 
    $(this).find('.dropdown-menu').first().stop(true, true).slideToggle(400);
    , function() 
    $(this).find('.dropdown-menu').first().stop(true, true).slideToggle(400)
    );

【讨论】:

真的不需要定义两次。使用切换时,无需第二个即可。

以上是关于向引导下拉菜单添加幻灯片效果的主要内容,如果未能解决你的问题,请参考以下文章

引导下拉菜单按钮在单击按钮以及单击屏幕时更改

如何制作悬停效果而不是单击引导程序 4 下拉菜单? [复制]

引导导航下拉菜单切换问题

将图标添加到引导下拉菜单项

将图像添加到引导下拉菜单

将引导程序下拉更改为水平向右