CSS过渡没有按照预期的方式被触发[重复]。

问题描述 投票:0回答:1

下面的提琴包含以下代码 扩大 一个可折叠的菜单,它类似于Bootstrap的Collapse组件。它类似于Bootstrap的Collapse组件。下面是它的工作原理。

  1. 菜单一开始是隐藏的,用 display: none.
  2. display: none 移除,使菜单可见,这样我们就可以检索并存储其当前高度,以便以后使用。
  3. 菜单被赋予了 transition 配置的CSS属性。height 属性,现在设置为 0.
  4. height 的值被设置为我们在 第二步.
  5. transitionend 事件处理程序应该在过渡结束后启动。

虽然菜单扩展到了预期的高度,但它没有进行过渡。我需要你的帮助来找出原因。

在这里,试试吧。https:/jsfiddle.netavdbk89j3

一个重要的观察:如果你打开检查器工具,选择了 <nav> (菜单),然后切换 height inline样式,我们的代码在你点击按钮后添加了这个样式,过渡会立即被触发,并且我们的 transitionend 处理程序被执行。这里会不会是一个竞赛条件?高度应该是在设置过渡后改变,而不是在之前。

javascript css css-transitions
1个回答
1
投票

你需要摆脱 display: none 并让css来处理更多的过渡。

我将.np-collapsible设置为这样,以便让元素永远存在。

.np-collapsible:not(.np-expanded) {
  height: 0;
  overflow: hidden;
}

我将过渡类设置为不做任何会启动过渡的改变(它只包括过渡属性)。

然后在JS中,过渡的做法和你原来的类似,但主要的区别是我用了 menu.scrollHeight 来获取菜单的高度,以避免有额外的过渡来正常获取高度。

我还在功能中添加了收缩菜单的功能。在收缩菜单的情况下,你必须移除 np-expanded 的过渡前,由于 :not(.np-expanded) 选择器前面停止溢出:无。

  // Retrieve the height of the menu
  var targetHeight = menu.scrollHeight + 'px';
  if (menu.classList.contains('np-expanded')) {
    // It's already expanded, time to contract.
    targetHeight = 0;
    menu.classList.remove('np-expanded');
    button.setAttribute('aria-expanded', false);
  }
  // Enable transition
  menu.classList.add('np-transitioning');
  menu.addEventListener('transitionend', function(event) {
    // Disable transition
    menu.classList.remove('np-transitioning');

    // Indicate that the menu is now expanded
    if (targetHeight) {
      menu.classList.add('np-expanded');
      button.setAttribute('aria-expanded', true);
    }
  }, {
    once: true
  });

  // Set the height to execute the transition
  menu.style.height = targetHeight;

这是一个工作的例子。

var button = document.querySelector('.np-trigger');
var menu = document.querySelector(button.dataset.target);

button.addEventListener('click', function(event) {
  expand();
}, false);

function expand() {
  if (isTransitioning()) {
    // Don't do anything during a transition
    return;
  }

  // Retrieve the height of the menu
  var targetHeight = menu.scrollHeight + 'px';
  if (menu.classList.contains('np-expanded')) {
    // It's already expanded, time to contract.
    targetHeight = 0;
    menu.classList.remove('np-expanded');
    button.setAttribute('aria-expanded', false);
  }
  // Enable transition
  menu.classList.add('np-transitioning');
  menu.addEventListener('transitionend', function(event) {
    // Disable transition
    menu.classList.remove('np-transitioning');

    // Indicate that the menu is now expanded
    if (targetHeight) {
      menu.classList.add('np-expanded');
      button.setAttribute('aria-expanded', true);
    }
  }, {
    once: true
  });

  // Set the height to execute the transition
  menu.style.height = targetHeight;
}

function isTransitioning() {
  if (menu.classList.contains('np-transitioning')) {
    return true;
  }

  return false;
}
.np-collapsible:not(.np-expanded) {
  height: 0;
  overflow: hidden;
}

.np-transitioning {
  transition: height 0.25s ease;
}

.navigation-menu {
  display: flex;
  flex-direction: column;
  position: fixed;
  top: 4rem;
  left: 1rem;
  width: 270px;
}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<button class="btn btn-dark np-trigger" data-target="#menu">Menu</button>

<nav id="menu" class="bg-dark navigation-menu np-collapsible">
  <a class="nav-link" href="#">Link</a>
  <a class="nav-link" href="#">Link</a>
  <a class="nav-link" href="#">Link</a>
</nav>

正如前面提到的,在原来的例子中存在一个竞赛条件, 因为有多个过渡是通过设置原来的类来启动的。这个方法避免了与 display: none 和其他比赛条件下保持简单。

有趣的是,将 menu.classList.add('np-transitioning)排到与去除 np-collapsible 允许它工作。我觉得可能只是当时的改动基本上是批量在一起的。原来的jquery代码之所以能用,很可能是因为它的类被添加removed,中间没有任何其他DOM工作。

下面是原版更新后的工作方法,以上述方法为例https:/jsfiddle.net5w12rcbh

这里是同样的更新,但使用了一些方法进行了扩展和清理,如 classList.replace 来同时执行更多的工作。这样就增加了和我原来的代码段一样的切换功能。https:/jsfiddle.netbzc7uy2s

© www.soinside.com 2019 - 2024. All rights reserved.