我正在尝试不使用Bootstrap,但是遇到了一些我一直无法弄清的事情。
对于响应式顶部导航栏,当我单击汉堡菜单时,我希望它为打开和关闭动画(取决于它是打开还是关闭)。
我遇到的问题是在导航中使用display: none
会杀死所有/所有动画。 .nav-top__nav-list
是动画需要发生的位置,其显示从“无”切换为“块”。我这样做是为了实现可访问性(这意味着除非扩展导航,否则屏幕阅读器不会进入该区域)。我应该采取另一种方式来保持隐藏状态的可访问性,同时仍然能够对导航菜单的打开/关闭进行动画处理吗?
function topNavToggle() {
var topNavList = document.getElementById("topNavList");
var topNavToggle = document.getElementById("topNavToggle");
if (topNavList.className === "nav-top__nav-list") {
topNavList.className += " responsive";
topNavToggle.className += " toggled";
} else {
topNavList.className = "nav-top__nav-list";
topNavToggle.className = "nav-top__toggle-button";
}
}
.nav-top,
.navbar-nav {
grid-column-start: 1;
grid-column-end: 13;
}
.wrapper__1170-max-width {
display: grid;
grid-template-columns: repeat(12, 1fr);
max-width: 1170px;
margin: 0 auto;
padding: 0 1rem;
}
.display-desktop {
display: block;
}
.display-mobile {
display: none;
}
.nav-top {
background: #466a62;
min-height: 40px;
}
.nav-top__header {
grid-column-start: 1;
grid-column-end: 13;
}
.nav-top__header button {
background: none;
border: none;
cursor: pointer;
grid-column-start: 12;
grid-column-end: 13;
grid-row-start: 1;
justify-self: right;
}
.nav-top__header button .bar-1,
.nav-top__header button .bar-2,
.nav-top__header button .bar-3 {
width: 35px;
height: 5px;
background-color: #333;
margin: 6px 0;
transition: 0.4s;
}
.nav-top__header button.change {
background: red;
}
.nav-top__header .toggled .bar-1 {
transform: rotate(-45deg) translate(-9px, 6px);
}
.nav-top__header .toggled .bar-2 {
opacity: 0;
}
.nav-top__header .toggled .bar-3 {
transform: rotate(45deg) translate(-8px, -8px);
}
.nav-top__header .nav-brand {
grid-column-start: 1;
grid-column-end: 12;
grid-row-start: 1;
}
.nav-top__nav-list {
grid-column-start: 1;
grid-column-end: 13;
}
.nav-top__nav-list ul {
list-style: none;
margin: 0;
padding: 0;
}
.nav-top__nav-list ul li {
float: left;
}
.nav-top__nav-list ul li a {
color: #FFF;
display: block;
padding: 11px;
text-decoration: none;
}
.nav-top__nav-list ul li a:hover {
background: #FFF;
color: #18453b;
}
@media only screen and (max-width: 992px) {
.display-desktop {
display: none;
}
.display-mobile {
display: block;
}
.nav-top__header {
display: grid;
}
.nav-top__nav-list {
display: none;
}
.nav-top__nav-list.responsive {
display: block;
}
.nav-top__nav-list.responsive ul li {
display: block;
float: none;
}
}
<nav class="nav-top" aria-label="primary">
<div class="wrapper__1170-max-width">
<div class="nav-top__header display-mobile">
<span class="nav-top__brand">Menu</span>
<button type="button" id="topNavToggle" class="nav-top__toggle-button" aria-label="Expand and collapse primary site navigation" data-toggle="collapse" data-target="#topNavList" onclick="topNavToggle()">
<div class="bar-1"></div>
<div class="bar-2"></div>
<div class="bar-3"></div>
</button>
</div>
<div class="nav-top__nav-list" id="topNavList">
<ul>
<li class="active"><a href="#">Home</a></li>
<li><a href="#">Page 1</a></li>
<li><a href="#">Page 2</a></li>
<li><a href="#">Page 2</a></li>
</ul>
</div>
</div>
</nav>
答案 0 :(得分:1)
您无法在显示的两个值之间设置动画,因此使用另一种隐藏/显示扩展菜单的方式是正确的。这是一个使用高度的示例,另一个示例是不透明度。
.nav-top__nav-list {
height: 0;
transition: height 0.15s ease-in;
}
.nav-top__nav-list.responsive {
display: block;
height: 180px;
}
答案 1 :(得分:1)
display: none
无法设置动画。相反,您可以使用visibility: hidden
和visibility: visible
。
答案 2 :(得分:1)
在CSS中,无法设置display
属性的动画。您可以使用height
或max-height
。使用max-height
的好处是,即使您通过媒体查询在不同屏幕尺寸下更改了字体大小,该容器也将保持最佳高度。因此,我在CSS过渡中使用了max-height
。我已经更新了您的代码以获得预期的效果。
function topNavToggle() {
var topNavList = document.getElementById("topNavList");
var topNavToggle = document.getElementById("topNavToggle");
var topNavListLi = document.querySelectorAll("#topNavList li");
if (topNavList.className === "nav-top__nav-list") {
topNavList.className += " responsive";
topNavToggle.className += " toggled";
for (let i = 0; i < topNavListLi.length; i++) {
var hiddenAttribute = document.createAttribute("hidden");
//topNavListLi[i].setAttribute('hidden',false);
topNavListLi[i].setAttributeNode(hiddenAttribute);
}
} else {
topNavList.className = "nav-top__nav-list";
topNavToggle.className = "nav-top__toggle-button";
setTimeout(function(){
for (let i = 0; i < topNavListLi.length; i++) {
//topNavListLi[i].setAttribute('hidden',true);
topNavListLi[i].removeAttribute('hidden');
}
},300);
}
}
.nav-top,
.navbar-nav {
grid-column-start: 1;
grid-column-end: 13;
}
.wrapper__1170-max-width {
display: grid;
grid-template-columns: repeat(12, 1fr);
max-width: 1170px;
margin: 0 auto;
padding: 0 1rem;
}
.display-desktop {
display: block;
}
.display-mobile {
display: none;
}
.nav-top {
background: #466a62;
min-height: 40px;
}
.nav-top__header {
grid-column-start: 1;
grid-column-end: 13;
}
.nav-top__header button {
background: none;
border: none;
cursor: pointer;
grid-column-start: 12;
grid-column-end: 13;
grid-row-start: 1;
justify-self: right;
}
.nav-top__header button .bar-1,
.nav-top__header button .bar-2,
.nav-top__header button .bar-3 {
width: 35px;
height: 5px;
background-color: #333;
margin: 6px 0;
transition: 0.4s;
}
.nav-top__header button.change {
background: red;
}
.nav-top__header .toggled .bar-1 {
transform: rotate(-45deg) translate(-9px, 6px);
}
.nav-top__header .toggled .bar-2 {
opacity: 0;
}
.nav-top__header .toggled .bar-3 {
transform: rotate(45deg) translate(-8px, -8px);
}
.nav-top__header .nav-brand {
grid-column-start: 1;
grid-column-end: 12;
grid-row-start: 1;
}
.nav-top__nav-list {
grid-column-start: 1;
grid-column-end: 13;
}
.nav-top__nav-list ul {
list-style: none;
margin: 0;
padding: 0;
}
.nav-top__nav-list ul li {
float: left;
}
.nav-top__nav-list ul li a {
color: #FFF;
display: block;
padding: 11px;
text-decoration: none;
}
.nav-top__nav-list ul li a:hover {
background: #FFF;
color: #18453b;
}
@media only screen and (max-width: 992px) {
.display-desktop {
display: none;
}
.display-mobile {
display: block;
}
.nav-top__header {
display: grid;
}
.nav-top__nav-list {
max-height: 0;
overflow: hidden;
transition: max-height .3s ease;
}
.nav-top__nav-list ul li {
float: none;
}
.nav-top__nav-list.responsive {
max-height: 170px;
transition: max-height .3s ease;
}
.nav-top__nav-list.responsive ul li {
display: block;
}
}
<nav class="nav-top" aria-label="primary">
<div class="wrapper__1170-max-width">
<div class="nav-top__header display-mobile">
<span class="nav-top__brand">Menu</span>
<button type="button" id="topNavToggle" class="nav-top__toggle-button" aria-label="Expand and collapse primary site navigation" data-toggle="collapse" data-target="#topNavList" onclick="topNavToggle()">
<div class="bar-1"></div>
<div class="bar-2"></div>
<div class="bar-3"></div>
</button>
</div>
<div class="nav-top__nav-list" id="topNavList">
<ul>
<li class="active"><a href="#">Home</a></li>
<li><a href="#">Page 1</a></li>
<li><a href="#">Page 2</a></li>
<li><a href="#">Page 2</a></li>
</ul>
</div>
</div>
</nav>
答案 3 :(得分:1)
由于无法对显示进行动画处理,因此我通常使用setTimeout或requestAnimationFrame进行两步操作。这也是某些框架(例如ng-animate)在内部工作的方式。像这样:
var state = Enums.NavState.Closed;
function openNav() {
state = Enums.NavState.Opening;
var nav = $('.topNavList')
nav.addClass('navVisible');
requestAnimationFrame(function() {
// This is async, so make sure we're still opening;
if (state === Enums.NavState.Opening) {
state = Enums.NavState.Open;
nav.addClass('in');
}
})
}
还有一些示例CSS:
.navVisible {
display: block;
opacity: 0;
transition: opacity 300ms;
}
.navVisible.in {
opacity: 1;
}