如何仅使用Vanilla Javascript定位子节点而不知道其位置

时间:2018-08-15 16:38:37

标签: javascript mouseevent dom-events

我创建了一个模型导航栏,并使用mouseenter事件显示了其父li元素内的子菜单。

这是通过使用children属性并找到子菜单的位置来实现的,但是想知道如何在不知道的情况下实现该目标,或者是否有更好的惯用方法。

子菜单和列表项具有相同的类名。

下面,您会找到我的代码:

'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://stackoverflow.com">4</a></li>
				</ul>
			</li>
			<li><a href="#">No effect</a></li>
			<li><a href="#">Same here</a></li>
		</ul>
	</nav>
</header>

2 个答案:

答案 0 :(得分:1)

一种选择是向目标元素添加id(或class es),​​然后向控制元素添加一些属性以表示其控制的目标元素。在不了解两个节点之间的DOM关系的情况下,您将不得不添加一些信息以连接它们。

我本来要使用data-属性,但后来我想起来,通过使用aria-controls也可以添加一些别名。

'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://stackoverflow.com">4</a></li>
                    </ul>
                </li>
                <li><a href="#">No effect</a></li>
                <li><a href="#">Same here</a></li>
            </ul>
        </nav>
    </header>

答案 1 :(得分:1)

另一种方法(我更喜欢一种方法)是使用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://stackoverflow.com">4</a></li>
				</ul>
			</li>
			<li><a href="#">No effect</a></li>
			<li><a href="#">Same here</a></li>
		</ul>
	</nav>
</header>