背景: 我正在为艺术家做页面,但该网站出现了一些有趣的行为。
我使用flexbox而不是bootstrap进行布局,因为该站点很小,并且没有大量元素。 Flexbox非常适合布置站点,尤其是针对不同媒体大小的更改。
但是,在最小的媒体尺寸下,移动版式遇到了一些麻烦。我喜欢引导程序中可折叠菜单的外观,但是因为我没有使用引导程序,所以我从字体中获得了很棒的图标,现在我正尝试使用JavaScript和CSS手动处理可折叠菜单属性。但是,在不使用引导程序的情况下,我不确定为什么可扩展菜单仅在重复单击后才起作用,而不是在第一次单击时才起作用。
期望: 在最小的媒体查询中(窗口<768像素),我以所需的方式设置了默认的移动版式。标语位于页面的大约一半位置,并且汉堡菜单固定在浏览器的顶部。当我单击菜单按钮时,我期望的反应是使菜单链接出现在菜单按钮下方,并使页面横幅消失。
问题: 这种情况会发生并切换,但只有在单击多次后才可以。
问题: 1.是什么导致单击行为仅在单击几下后才能起作用? 2.为什么'none'的display属性不能应用于'nav-left'项目? 3.在显示菜单时,除了使用边距来抵消出现的菜单项外,是否有办法确保它们出现在汉堡包按钮区域下方? (即,我希望链接1-3出现在汉堡菜单下方)。
function menuToggle() {
var x = document.getElementById('nav-right');
var y = document.getElementsByClassName("nav-left");
if (x.style.display == "none") {
x.style.display = "flex";
y.style.display = "none";
} else {
x.style.display = "none";
y.style.display = "flex";
}
}
* {
margin: 0;
padding: 0;
}
html,
body {
background-color: gray;
height: 100%;
width: 100%;
}
.nav-left {
font-family: 'Kristi', cursive;
font-size: 5.5em;
color: #e319b6;
letter-spacing: 2px;
margin: 0;
padding: 0 0 0 50px;
}
.nav-left a {
text-decoration: none;
color: #e319b6;
}
.hamburger {
display: none;
}
li a {
text-decoration: none;
color: white;
}
li a:hover {
text-decoration: none;
color: #e319b6;
}
li {
padding: 50px 10px;
list-style: none;
font-family: 'Montserrat', sans-serif;
font-size: 1.7em;
font-weight: 100;
color: white;
display: inline-block;
}
@media (max-width:767px) {
.hamburger {
width: 100%;
padding: 2% 0;
display: flex;
color: white;
font-size: 2em;
justify-content: center;
background: rgba(0, 0, 0, .1);
position: absolute;
cursor: pointer;
border: none;
}
.pagenav {
width: 100%;
display: flex;
align-items: center;
flex-direction: column;
}
.nav-left {
width: 100%;
display: flex;
justify-content: center;
order: 2;
margin-top: 10%;
padding: 0;
}
#nav-right {
display: none;
justify-content: center;
width: 100%;
}
.nav {
width: 100%;
}
nav ul {
display: flex;
flex-direction: column;
width: 100%;
}
nav ul li {
display: flex;
justify-content: center;
width: 100%;
}
nav ul li {}
nav ul li:hover {
background: rgba(0, 0, 0, .8);
}
nav ul li:hover a {
color: #e319b6;
}
}
<head>
<meta charset="utf-8">
<title></title>
<!-- Font Awesome -->
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.1.0/css/all.css" integrity="sha384-lKuwvrZot6UHsBSfcMvOkWwlCMgc0TaWr+30HWe3a4ltaBwTZhyTEggF5tJv8tbt" crossorigin="anonymous">
</head>
<body>
<div class="pagenav">
<div class="nav-left">
placeholder
</div>
<button class="hamburger" onclick="menuToggle()">
<i class="fas fa-bars"></i>
</button>
<nav id="nav-right">
<ul>
<li><a href="reel.html">Link 1</a></li>
<li><a href="">Link 2</a></li>
<li><a href="">Link 3</a></li>
</ul>
</nav>
</div>
</body>
答案 0 :(得分:3)
您遇到的问题是if (x.style.display == "none")
应该第一次通过,因为它是隐藏的吗?好吧,它没有通过,因为根据style
属性,display
不等于none
,因此,您不能说它是隐藏的。要解决此问题,请执行以下操作:
<nav id='nav-right' style='display: none'>
现在您的代码知道#nav-right
是隐藏的,并且在第一次单击时就可以使用。
您可以使用一次性切换变量。这样,第一次单击按钮将显示菜单,然后将toggle变量设置为false。像这样:
let firstMenuClickToggle = true
function menuToggle() {
...
if (x.style.display == 'none' || firstMenuClickToggle) {
firstMenuToggle = false
...
'none'
就像@flen所说的那样,您可以在'none'
旁边搜索一个空字符串,但是为什么不能同时搜索两者呢?
例如,尝试将您的if
语句替换为以下内容:
if (!'none'.indexOf(x.style.display)) {
实际上,根据我在Benchmark.js上进行的测试,它比@flen的单独比较快得多,当每次运行10,000次时,相差近 5秒。
之所以可行,是因为从技术上讲,两个字符串''
和'none'
在'none'
中都存在,因此,如果其中两个值都存在,它们将返回0
。因此,我使用!
运算符进行检查,!0
将始终返回true,但其他任何数字(即!-1
)都将返回false。
String.prototype.emptyOrEqualTo = function(string) {
return string.indexOf(this)
}
// in your function
if (x.style.display.emptyOrEqualTo('none')) {
尽管通常不执行对String.prototype
的修改,但它确实比大多数其他增加可读性的方法产生更高的可读性。实际上,您甚至可以在上面加上is
,这将是一个句子。
display.isEmptyOrEqualTo('none')
现在您可以实际阅读它了:
如果显示为空或等于“无”
仅供参考,在您的JSFiddle中,y.style
是undefined
,因为您使用了.getElementsByClassName
,但是返回了一个数组,但是没有得到返回数组的第一个索引。
另外一些注意事项:
x
和y
更多的描述性变量名称,因为它们没有传达其含义,您必须查找它们的定义行才能理解,这并不完全是“最佳实践” 。const
和let
。答案 1 :(得分:3)
chbchb55已经解释了问题所在,但是我认为您不应该更改HTML来修复JavaScript代码中的错误。而是在此处将menuToggle
函数更改为此:
function menuToggle() {
var x = document.getElementById('nav-right');
var y = document.getElementsByClassName("nav-left")[0]; //changed to get the first element of the HTMLCollection
if (x.style.display == "none" || x.style.display === "") { //changed to also accept the original value, which is "" instead of "none"
x.style.display = "flex";
y.style.display = "none";
} else {
x.style.display = "none";
y.style.display = "flex";
}
}
请明确说明一下,让我改一下解决方法:第一次调用menuToggle
函数时,它读取的是x.style.display
,其值实际上是""
而不是{{1} },正如您期望的那样。由于不是"none"
,因此它将运行"none"
子句,然后继续将else
分配给x.style.display
,将"none"
分配给y.style.display
。这不应该发生,因为这表示您的菜单已关闭。因此,仅在第二次调用时才打开它,因为该函数的第一次调用运行了"flex"
子句并关闭了(已经关闭)菜单。
else
甚至创建原型作为语法糖来使其更具可读性。我仍然出于两个原因反对使用此方法。
首先,它的可读性仍然不如if (!'none'.indexOf(x.style.display))
。在这里,无论谁阅读代码,都可以立即看到我们正在检查2个条件,而仅检查这2个条件。使用if (x.style.display == "none" || x.style.display === "")
尚不清楚我们是否也在检查indexOf
,因此我们必须在评论中指定。我认为可读性更差。
第二,如果速度是一个问题,那么这实际上是最快的解决方案,用""
代替if
子句:else
,然后执行{{1 }}子句。这样,if (x.style.display === "flex")
将捕获else
和else
以及其他任何内容。如果代码长度是一个问题,我们可以使用三元运算符来进一步改善:
x === ''
仍然,速度和长度在这里不是问题,因为现在是代码。如果改善1毫秒,我会印象深刻。我认为原始解决方案是最易读的解决方案,这就是为什么我仍然建议将其作为答案。不过,使用x == 'none'
的想法非常聪明,如果我没记错的话,它比ES6 function menuToggle() {
var x = document.getElementById('nav-right');
var y = document.getElementsByClassName("nav-left")[0]; //changed to get the first element of the HTMLCollection
x.style.display === "flex" ? (x.style.display = "none", y.style.display = "flex") : (x.style.display = "flex", y.style.display = "none");
}