如何仅使用 Vanilla Javascript 在不知道其位置的情况下定位子节点
How to Target a Child Node Without Knowing Its Position Using Only Vanilla Javascript
我创建了一个模型导航栏,并使用 mouseenter
事件,已经能够在其父 li
元素内显示子菜单。
这是通过使用 children
属性 和定位子菜单的位置来实现的,但我想知道这是如何在不知情的情况下实现的,或者是否有更好的惯用方法。
子菜单和列表项具有相同的 class 名称。
下面是我的代码:
'use strict';
var dropdown = document.getElementsByClassName('dropdown');
document.addEventListener('DOMContentLoaded', function(e) {
e.preventDefault();
Array.from(dropdown).forEach(function(node) {
node.addEventListener('mouseenter', function(event) {
event.target.children[1].style.display = 'block';
});
});
Array.from(dropdown).forEach(function(node) {
node.addEventListener('mouseleave', function(event) {
event.target.children[1].style.display = 'none';
});
});
});
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
header {
height: 20vh;
width: 100vw;
background-color: #000;
}
.navbar {
position: relative;
width: 100%;
margin: 0;
padding: 0;
float: left;
}
.navbar > li {
display: inline-block;
list-style-type: none;
position: relative;
float: left;
margin: 0;
padding: 0;
}
.navbar li a {
text-align: center;
padding-left: 30px;
white-space: nowrap;
}
.menu {
display: none;
position: absolute;
top: 100%;
left: 0;
padding: 0;
}
.menu > li {
list-style-type: none;
padding: 10px 0;
float: none;
}
li {
width: 100px;
}
a {
font-family: Helvetica Neue;
text-decoration: none;
color: #fff;
display: block;
}
<header>
<nav>
<ul class="navbar">
<li class="dropdown"><a href="#">I drop down</a>
<ul class="menu">
<li><a href="#">1</a></li>
<li><a href="https://google.co.uk">2</a></li>
</ul>
</li>
<li class="dropdown"><a href="#">So do I</a>
<ul class="menu">
<li><a href="#">3</a></li>
<li><a href="https://whosebug.com">4</a></li>
</ul>
</li>
<li><a href="#">No effect</a></li>
<li><a href="#">Same here</a></li>
</ul>
</nav>
</header>
一种选择是向目标元素添加id
s(或class
es),然后向控制元素添加一些属性以表达它控制的目标元素。如果不知道两个节点之间的 DOM 关系,您将不得不添加一些信息来连接它们。
我本来打算使用 data-
属性,但后来我想起使用 aria-controls
也可以添加一些 a11y。
'use strict';
var dropdown = document.getElementsByClassName('dropdown');
document.addEventListener('DOMContentLoaded', function(e) {
e.preventDefault();
Array.from(dropdown).forEach(function(node) {
node.addEventListener('mouseenter', function(event) {
var menuId = event.srcElement.getAttribute('aria-controls');
document.getElementById(menuId).style.display = 'block';
});
node.addEventListener('mouseleave', function(event) {
var menuId = event.srcElement.getAttribute('aria-controls');
document.getElementById(menuId).style.display = 'none';
});
});
});
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
header {
height: 20vh;
width: 100vw;
background-color: #000;
}
.navbar {
position: relative;
width: 100%;
margin: 0;
padding: 0;
float: left;
}
.navbar > li {
display: inline-block;
list-style-type: none;
position: relative;
float: left;
margin: 0;
padding: 0;
}
.navbar li a {
text-align: center;
padding-left: 30px;
white-space: nowrap;
}
.menu {
display: none;
position: absolute;
top: 100%;
left: 0;
padding: 0;
}
.menu > li {
list-style-type: none;
padding: 10px 0;
float: none;
}
li {
width: 100px;
}
a {
font-family: Helvetica Neue;
text-decoration: none;
color: #fff;
display: block;
}
<header>
<nav>
<ul class="navbar">
<li class="dropdown" aria-controls="menu1"><a href="#">I drop down</a>
<ul class="menu" id="menu1">
<li><a href="#">1</a></li>
<li><a href="https://google.co.uk">2</a></li>
</ul>
</li>
<li class="dropdown" aria-controls="menu2"><a href="#">So do I</a>
<ul class="menu" id="menu2">
<li><a href="#">3</a></li>
<li><a href="https://whosebug.com">4</a></li>
</ul>
</li>
<li><a href="#">No effect</a></li>
<li><a href="#">Same here</a></li>
</ul>
</nav>
</header>
另一种方法(我更喜欢)是使用 HTML 的结构来查找节点。为此,ul.menu
必须是 currentTarget
的后代。然后就可以用querySelector定位了
'use strict';
var dropdown = document.getElementsByClassName('dropdown');
document.addEventListener('DOMContentLoaded', function(e) {
e.preventDefault();
Array.from(dropdown).forEach(function(node) {
node.addEventListener('mouseenter', function(event) {
this.querySelector(':scope > ul.menu').style.display = 'block';
});
node.addEventListener('mouseleave', function(event) {
this.querySelector(':scope > ul.menu').style.display = 'none';
});
});
});
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
header {
height: 20vh;
width: 100vw;
background-color: #000;
}
.navbar {
position: relative;
width: 100%;
margin: 0;
padding: 0;
float: left;
}
.navbar > li {
display: inline-block;
list-style-type: none;
position: relative;
float: left;
margin: 0;
padding: 0;
}
.navbar li a {
text-align: center;
padding-left: 30px;
white-space: nowrap;
}
.menu {
display: none;
position: absolute;
top: 100%;
left: 0;
padding: 0;
}
.menu > li {
list-style-type: none;
padding: 10px 0;
float: none;
}
li {
width: 100px;
}
a {
font-family: Helvetica Neue;
text-decoration: none;
color: #fff;
display: block;
}
<header>
<nav>
<ul class="navbar">
<li class="dropdown"><a href="#">I drop down</a>
<ul class="menu">
<li><a href="#">1</a></li>
<li><a href="https://google.co.uk">2</a></li>
</ul>
</li>
<li class="dropdown"><a href="#">So do I</a>
<ul class="menu">
<li><a href="#">3</a></li>
<li><a href="https://whosebug.com">4</a></li>
</ul>
</li>
<li><a href="#">No effect</a></li>
<li><a href="#">Same here</a></li>
</ul>
</nav>
</header>
我创建了一个模型导航栏,并使用 mouseenter
事件,已经能够在其父 li
元素内显示子菜单。
这是通过使用 children
属性 和定位子菜单的位置来实现的,但我想知道这是如何在不知情的情况下实现的,或者是否有更好的惯用方法。
子菜单和列表项具有相同的 class 名称。
下面是我的代码:
'use strict';
var dropdown = document.getElementsByClassName('dropdown');
document.addEventListener('DOMContentLoaded', function(e) {
e.preventDefault();
Array.from(dropdown).forEach(function(node) {
node.addEventListener('mouseenter', function(event) {
event.target.children[1].style.display = 'block';
});
});
Array.from(dropdown).forEach(function(node) {
node.addEventListener('mouseleave', function(event) {
event.target.children[1].style.display = 'none';
});
});
});
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
header {
height: 20vh;
width: 100vw;
background-color: #000;
}
.navbar {
position: relative;
width: 100%;
margin: 0;
padding: 0;
float: left;
}
.navbar > li {
display: inline-block;
list-style-type: none;
position: relative;
float: left;
margin: 0;
padding: 0;
}
.navbar li a {
text-align: center;
padding-left: 30px;
white-space: nowrap;
}
.menu {
display: none;
position: absolute;
top: 100%;
left: 0;
padding: 0;
}
.menu > li {
list-style-type: none;
padding: 10px 0;
float: none;
}
li {
width: 100px;
}
a {
font-family: Helvetica Neue;
text-decoration: none;
color: #fff;
display: block;
}
<header>
<nav>
<ul class="navbar">
<li class="dropdown"><a href="#">I drop down</a>
<ul class="menu">
<li><a href="#">1</a></li>
<li><a href="https://google.co.uk">2</a></li>
</ul>
</li>
<li class="dropdown"><a href="#">So do I</a>
<ul class="menu">
<li><a href="#">3</a></li>
<li><a href="https://whosebug.com">4</a></li>
</ul>
</li>
<li><a href="#">No effect</a></li>
<li><a href="#">Same here</a></li>
</ul>
</nav>
</header>
一种选择是向目标元素添加id
s(或class
es),然后向控制元素添加一些属性以表达它控制的目标元素。如果不知道两个节点之间的 DOM 关系,您将不得不添加一些信息来连接它们。
我本来打算使用 data-
属性,但后来我想起使用 aria-controls
也可以添加一些 a11y。
'use strict';
var dropdown = document.getElementsByClassName('dropdown');
document.addEventListener('DOMContentLoaded', function(e) {
e.preventDefault();
Array.from(dropdown).forEach(function(node) {
node.addEventListener('mouseenter', function(event) {
var menuId = event.srcElement.getAttribute('aria-controls');
document.getElementById(menuId).style.display = 'block';
});
node.addEventListener('mouseleave', function(event) {
var menuId = event.srcElement.getAttribute('aria-controls');
document.getElementById(menuId).style.display = 'none';
});
});
});
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
header {
height: 20vh;
width: 100vw;
background-color: #000;
}
.navbar {
position: relative;
width: 100%;
margin: 0;
padding: 0;
float: left;
}
.navbar > li {
display: inline-block;
list-style-type: none;
position: relative;
float: left;
margin: 0;
padding: 0;
}
.navbar li a {
text-align: center;
padding-left: 30px;
white-space: nowrap;
}
.menu {
display: none;
position: absolute;
top: 100%;
left: 0;
padding: 0;
}
.menu > li {
list-style-type: none;
padding: 10px 0;
float: none;
}
li {
width: 100px;
}
a {
font-family: Helvetica Neue;
text-decoration: none;
color: #fff;
display: block;
}
<header>
<nav>
<ul class="navbar">
<li class="dropdown" aria-controls="menu1"><a href="#">I drop down</a>
<ul class="menu" id="menu1">
<li><a href="#">1</a></li>
<li><a href="https://google.co.uk">2</a></li>
</ul>
</li>
<li class="dropdown" aria-controls="menu2"><a href="#">So do I</a>
<ul class="menu" id="menu2">
<li><a href="#">3</a></li>
<li><a href="https://whosebug.com">4</a></li>
</ul>
</li>
<li><a href="#">No effect</a></li>
<li><a href="#">Same here</a></li>
</ul>
</nav>
</header>
另一种方法(我更喜欢)是使用 HTML 的结构来查找节点。为此,ul.menu
必须是 currentTarget
的后代。然后就可以用querySelector定位了
'use strict';
var dropdown = document.getElementsByClassName('dropdown');
document.addEventListener('DOMContentLoaded', function(e) {
e.preventDefault();
Array.from(dropdown).forEach(function(node) {
node.addEventListener('mouseenter', function(event) {
this.querySelector(':scope > ul.menu').style.display = 'block';
});
node.addEventListener('mouseleave', function(event) {
this.querySelector(':scope > ul.menu').style.display = 'none';
});
});
});
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
header {
height: 20vh;
width: 100vw;
background-color: #000;
}
.navbar {
position: relative;
width: 100%;
margin: 0;
padding: 0;
float: left;
}
.navbar > li {
display: inline-block;
list-style-type: none;
position: relative;
float: left;
margin: 0;
padding: 0;
}
.navbar li a {
text-align: center;
padding-left: 30px;
white-space: nowrap;
}
.menu {
display: none;
position: absolute;
top: 100%;
left: 0;
padding: 0;
}
.menu > li {
list-style-type: none;
padding: 10px 0;
float: none;
}
li {
width: 100px;
}
a {
font-family: Helvetica Neue;
text-decoration: none;
color: #fff;
display: block;
}
<header>
<nav>
<ul class="navbar">
<li class="dropdown"><a href="#">I drop down</a>
<ul class="menu">
<li><a href="#">1</a></li>
<li><a href="https://google.co.uk">2</a></li>
</ul>
</li>
<li class="dropdown"><a href="#">So do I</a>
<ul class="menu">
<li><a href="#">3</a></li>
<li><a href="https://whosebug.com">4</a></li>
</ul>
</li>
<li><a href="#">No effect</a></li>
<li><a href="#">Same here</a></li>
</ul>
</nav>
</header>