如何有效地管理多个自定义下拉 DOM 元素的行为和可见性状态?
How does one effectively manage the behavior and visibility state of multiple custom drop-down DOM-elements?
document.querySelectorAll('.selector').forEach((item) => {
item.addEventListener("click", (event) => {
document.querySelectorAll('.dropdown').classList.add("active");
});
});
.body-ct .selector{display:inline-block;width:200px;text-align:center;border:1px solid navy;border-radius:4px;}
.body-ct .selector:hover{cursor:pointer;}
.body-ct .selector h4{padding:5px 0;}
.body-ct .selector {position:relative;}
.body-ct .selector .dropdown{display:none;position:absolute;top:55px;z-index:2;width:200px;border:3px solid red;background-color:#FFF;}
.body-ct .selector .dropdown.active{display:block;}
.body-ct .selector .dropdown ul{list-style-type:none;text-align:left;background-color:#FFF;}
.body-ct .selector .dropdown ul li{background-color:#FFF;}
.body-ct .selector .dropdown ul li:hover{font-weight:700;}
<section class="body-ct">
<div class="container">
<div class="row">
<div class="col-12 sel-ct">
<div class="story-type-sel selector">
<h4>Story Type</h4>
<div class="dropdown story-selector">
<ul>
<li data-type="article">Article</li>
<li data-type="pr">Press Release</li>
<li data-type="analyst">Analyst Report</li>
</ul>
</div>
</div>
<div class="year-sel selector">
<h4>Year</h4>
<div class="dropdown year-selector">
<ul>
<li data-year="2021">2021</li>
<li data-year="2020">2020</li>
<li data-year="2019">2019</li>
<li data-year="2018">2018</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</section>
我无法从我的事件侦听器中引用 dropdown
class。我认为这与使用 .target
或 .currentTarget
有关,但我不确定如何将它们放在一起。
当用户点击带有 .selector
的元素时,下拉菜单应该通过添加 .active
class.
来显示
document.querySelectorAll(".selector").forEach((item) => {
item.addEventListener("click", (event) => {
document.querySelector(".dropdown").classList.add("active");
});
});
jQuery也是一样的,但是我不想用jQuery。
$(".selector").on("click", function () {
$(this).find(".dropdown").addClass("active");
});
问题是 document.querySelectorAll()
returns 您代码中所有 dropdown
元素的数组,document.querySelector()
returns 他找到的第一个元素,但不一定是触发事件的元素内的那个。
如果要将 active
class 添加到被单击的 selector
元素中包含的下拉元素,您可以从当前事件中调用 querySelector()
目标而不是来自 document
:
document.querySelectorAll('.selector').forEach((item) => {
item.addEventListener("click", (event) => {
event.currentTarget.querySelector('.dropdown').classList.add("active");
});
});
querySelectorAll returns 一个节点列表。使用 forEach.
document.querySelectorAll('.selector').forEach((item) => {
item.addEventListener("click", (event) => {
event.target.querySelectorAll(".dropdown").forEach((dopdownItem) => {
dopdownItem.classList.add("active");
});
});
});
.body-ct .selector {
display: inline-block;
width: 200px;
text-align: center;
border: 1px solid navy;
border-radius: 4px;
}
.body-ct .selector:hover {
cursor: pointer;
}
.body-ct .selector h4 {
padding: 5px 0;
}
.body-ct .selector {
position: relative;
}
.body-ct .selector .dropdown {
display: none;
position: absolute;
top: 55px;
z-index: 2;
width: 200px;
border: 3px solid red;
background-color: #FFF;
}
.body-ct .selector .dropdown.active {
display: block;
}
.body-ct .selector .dropdown ul {
list-style-type: none;
text-align: left;
background-color: #FFF;
}
.body-ct .selector .dropdown ul li {
background-color: #FFF;
}
.body-ct .selector .dropdown ul li:hover {
font-weight: 700;
}
<section class="body-ct">
<div class="container">
<div class="row">
<div class="col-12 sel-ct">
<div class="story-type-sel selector">
<h4>Story Type</h4>
<div class="dropdown story-selector">
<ul>
<li data-type="article">Article</li>
<li data-type="pr">Press Release</li>
<li data-type="analyst">Analyst Report</li>
</ul>
</div>
</div>
<div class="year-sel selector">
<h4>Year</h4>
<div class="dropdown year-selector">
<ul>
<li data-year="2021">2021</li>
<li data-year="2020">2020</li>
<li data-year="2019">2019</li>
<li data-year="2018">2018</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</section>
一个 naive/basic 事件和 state/appearance 处理方法可能看起来像下一个...
function deactivateDropdown(elm) {
elm.classList.remove('active');
}
function activateDropdown(elm) {
elm.classList.add('active');
}
function deactivateAnyDropdown() {
document
.querySelectorAll('.dropdown')
.forEach(deactivateDropdown);
}
function handleSelectorClick(evt) {
evt.stopPropagation();
deactivateAnyDropdown();
activateDropdown(
evt
.currentTarget
.querySelector('.dropdown')
);
}
function init() {
document
.body
.addEventListener('click', deactivateAnyDropdown)
document
.querySelectorAll('.selector')
.forEach(item =>
item.addEventListener('click', handleSelectorClick)
);
}
init();
.body-ct .selector{display:inline-block;width:200px;text-align:center;border:1px solid navy;border-radius:4px;}
.body-ct .selector:hover{cursor:pointer;}
.body-ct .selector h4{padding:5px 0;}
.body-ct .selector {position:relative;}
.body-ct .selector .dropdown{display:none;position:absolute;top:55px;z-index:2;width:200px;border:3px solid red;background-color:#FFF;}
.body-ct .selector .dropdown.active{display:block;}
.body-ct .selector .dropdown ul{list-style-type:none;text-align:left;background-color:#FFF;}
.body-ct .selector .dropdown ul li{background-color:#FFF;}
.body-ct .selector .dropdown ul li:hover{font-weight:700;}
<section class="body-ct">
<div class="container">
<div class="row">
<div class="col-12 sel-ct">
<div class="story-type-sel selector">
<h4>Story Type</h4>
<div class="dropdown story-selector">
<ul>
<li data-type="article">Article</li>
<li data-type="pr">Press Release</li>
<li data-type="analyst">Analyst Report</li>
</ul>
</div>
</div>
<div class="year-sel selector">
<h4>Year</h4>
<div class="dropdown year-selector">
<ul>
<li data-year="2021">2021</li>
<li data-year="2020">2020</li>
<li data-year="2019">2019</li>
<li data-year="2018">2018</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</section>
...它为停用当前 used/clicked-on 下拉菜单提供基本支持。
但上述行为当然远非理想。因此,一种更高级的方法将只关注一个元素,在这种情况下,它是下拉列表的触发器 class 由 OP 定义为 .selector
.
人们希望 active
-class 也是该元素的一部分,而不是 add
ing and/or remove
ing 这个 class 宁愿 toggle
它。
其中一个原因需要相应地更改 css-选择器以实现相关的切换行为。
上面的示例代码然后更改为以下代码...
function deactivateDropdown(elm) {
elm.classList.remove('active');
}
function toggleDropdown(elm) {
elm.classList.toggle('active');
}
function deactivateAnyNonActiveDropdown(evt) {
const { currentTarget, target } = evt;
Array
.from(
document.querySelectorAll('.selector')
)
.filter(elm =>
elm !== currentTarget ||
elm
.querySelector('.dropdown')
.contains(target)
)
.forEach(deactivateDropdown);
}
function handleSelectorClick(evt) {
evt.stopPropagation();
deactivateAnyNonActiveDropdown(evt);
toggleDropdown(evt.currentTarget);
}
function init() {
document
.body
.addEventListener('click', deactivateAnyNonActiveDropdown)
document
.querySelectorAll('.selector')
.forEach(item =>
item.addEventListener('click', handleSelectorClick)
);
}
init();
.body-ct .selector{display:inline-block;width:200px;text-align:center;border:1px solid navy;border-radius:4px;}
.body-ct .selector:hover{cursor:pointer;}
.body-ct .selector h4{padding:5px 0;}
.body-ct .selector {position:relative;}
.body-ct .selector .dropdown{display:none;position:absolute;top:55px;z-index:2;width:200px;border:3px solid red;background-color:#FFF;}
/*.body-ct .selector .dropdown.active{display:block;}*/
.body-ct .selector.active .dropdown {display:block;}
.body-ct .selector .dropdown ul{list-style-type:none;text-align:left;background-color:#FFF;}
.body-ct .selector .dropdown ul li{background-color:#FFF;}
.body-ct .selector .dropdown ul li:hover{font-weight:700;}
<section class="body-ct">
<div class="container">
<div class="row">
<div class="col-12 sel-ct">
<div class="story-type-sel selector">
<h4>Story Type</h4>
<div class="dropdown story-selector">
<ul>
<li data-type="article">Article</li>
<li data-type="pr">Press Release</li>
<li data-type="analyst">Analyst Report</li>
</ul>
</div>
</div>
<div class="year-sel selector">
<h4>Year</h4>
<div class="dropdown year-selector">
<ul>
<li data-year="2021">2021</li>
<li data-year="2020">2020</li>
<li data-year="2019">2019</li>
<li data-year="2018">2018</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</section>
document.querySelectorAll('.selector').forEach((item) => {
item.addEventListener("click", (event) => {
document.querySelectorAll('.dropdown').classList.add("active");
});
});
.body-ct .selector{display:inline-block;width:200px;text-align:center;border:1px solid navy;border-radius:4px;}
.body-ct .selector:hover{cursor:pointer;}
.body-ct .selector h4{padding:5px 0;}
.body-ct .selector {position:relative;}
.body-ct .selector .dropdown{display:none;position:absolute;top:55px;z-index:2;width:200px;border:3px solid red;background-color:#FFF;}
.body-ct .selector .dropdown.active{display:block;}
.body-ct .selector .dropdown ul{list-style-type:none;text-align:left;background-color:#FFF;}
.body-ct .selector .dropdown ul li{background-color:#FFF;}
.body-ct .selector .dropdown ul li:hover{font-weight:700;}
<section class="body-ct">
<div class="container">
<div class="row">
<div class="col-12 sel-ct">
<div class="story-type-sel selector">
<h4>Story Type</h4>
<div class="dropdown story-selector">
<ul>
<li data-type="article">Article</li>
<li data-type="pr">Press Release</li>
<li data-type="analyst">Analyst Report</li>
</ul>
</div>
</div>
<div class="year-sel selector">
<h4>Year</h4>
<div class="dropdown year-selector">
<ul>
<li data-year="2021">2021</li>
<li data-year="2020">2020</li>
<li data-year="2019">2019</li>
<li data-year="2018">2018</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</section>
我无法从我的事件侦听器中引用 dropdown
class。我认为这与使用 .target
或 .currentTarget
有关,但我不确定如何将它们放在一起。
当用户点击带有 .selector
的元素时,下拉菜单应该通过添加 .active
class.
document.querySelectorAll(".selector").forEach((item) => {
item.addEventListener("click", (event) => {
document.querySelector(".dropdown").classList.add("active");
});
});
jQuery也是一样的,但是我不想用jQuery。
$(".selector").on("click", function () {
$(this).find(".dropdown").addClass("active");
});
问题是 document.querySelectorAll()
returns 您代码中所有 dropdown
元素的数组,document.querySelector()
returns 他找到的第一个元素,但不一定是触发事件的元素内的那个。
如果要将 active
class 添加到被单击的 selector
元素中包含的下拉元素,您可以从当前事件中调用 querySelector()
目标而不是来自 document
:
document.querySelectorAll('.selector').forEach((item) => {
item.addEventListener("click", (event) => {
event.currentTarget.querySelector('.dropdown').classList.add("active");
});
});
querySelectorAll returns 一个节点列表。使用 forEach.
document.querySelectorAll('.selector').forEach((item) => {
item.addEventListener("click", (event) => {
event.target.querySelectorAll(".dropdown").forEach((dopdownItem) => {
dopdownItem.classList.add("active");
});
});
});
.body-ct .selector {
display: inline-block;
width: 200px;
text-align: center;
border: 1px solid navy;
border-radius: 4px;
}
.body-ct .selector:hover {
cursor: pointer;
}
.body-ct .selector h4 {
padding: 5px 0;
}
.body-ct .selector {
position: relative;
}
.body-ct .selector .dropdown {
display: none;
position: absolute;
top: 55px;
z-index: 2;
width: 200px;
border: 3px solid red;
background-color: #FFF;
}
.body-ct .selector .dropdown.active {
display: block;
}
.body-ct .selector .dropdown ul {
list-style-type: none;
text-align: left;
background-color: #FFF;
}
.body-ct .selector .dropdown ul li {
background-color: #FFF;
}
.body-ct .selector .dropdown ul li:hover {
font-weight: 700;
}
<section class="body-ct">
<div class="container">
<div class="row">
<div class="col-12 sel-ct">
<div class="story-type-sel selector">
<h4>Story Type</h4>
<div class="dropdown story-selector">
<ul>
<li data-type="article">Article</li>
<li data-type="pr">Press Release</li>
<li data-type="analyst">Analyst Report</li>
</ul>
</div>
</div>
<div class="year-sel selector">
<h4>Year</h4>
<div class="dropdown year-selector">
<ul>
<li data-year="2021">2021</li>
<li data-year="2020">2020</li>
<li data-year="2019">2019</li>
<li data-year="2018">2018</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</section>
一个 naive/basic 事件和 state/appearance 处理方法可能看起来像下一个...
function deactivateDropdown(elm) {
elm.classList.remove('active');
}
function activateDropdown(elm) {
elm.classList.add('active');
}
function deactivateAnyDropdown() {
document
.querySelectorAll('.dropdown')
.forEach(deactivateDropdown);
}
function handleSelectorClick(evt) {
evt.stopPropagation();
deactivateAnyDropdown();
activateDropdown(
evt
.currentTarget
.querySelector('.dropdown')
);
}
function init() {
document
.body
.addEventListener('click', deactivateAnyDropdown)
document
.querySelectorAll('.selector')
.forEach(item =>
item.addEventListener('click', handleSelectorClick)
);
}
init();
.body-ct .selector{display:inline-block;width:200px;text-align:center;border:1px solid navy;border-radius:4px;}
.body-ct .selector:hover{cursor:pointer;}
.body-ct .selector h4{padding:5px 0;}
.body-ct .selector {position:relative;}
.body-ct .selector .dropdown{display:none;position:absolute;top:55px;z-index:2;width:200px;border:3px solid red;background-color:#FFF;}
.body-ct .selector .dropdown.active{display:block;}
.body-ct .selector .dropdown ul{list-style-type:none;text-align:left;background-color:#FFF;}
.body-ct .selector .dropdown ul li{background-color:#FFF;}
.body-ct .selector .dropdown ul li:hover{font-weight:700;}
<section class="body-ct">
<div class="container">
<div class="row">
<div class="col-12 sel-ct">
<div class="story-type-sel selector">
<h4>Story Type</h4>
<div class="dropdown story-selector">
<ul>
<li data-type="article">Article</li>
<li data-type="pr">Press Release</li>
<li data-type="analyst">Analyst Report</li>
</ul>
</div>
</div>
<div class="year-sel selector">
<h4>Year</h4>
<div class="dropdown year-selector">
<ul>
<li data-year="2021">2021</li>
<li data-year="2020">2020</li>
<li data-year="2019">2019</li>
<li data-year="2018">2018</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</section>
...它为停用当前 used/clicked-on 下拉菜单提供基本支持。
但上述行为当然远非理想。因此,一种更高级的方法将只关注一个元素,在这种情况下,它是下拉列表的触发器 class 由 OP 定义为 .selector
.
人们希望 active
-class 也是该元素的一部分,而不是 add
ing and/or remove
ing 这个 class 宁愿 toggle
它。
其中一个原因需要相应地更改 css-选择器以实现相关的切换行为。
上面的示例代码然后更改为以下代码...
function deactivateDropdown(elm) {
elm.classList.remove('active');
}
function toggleDropdown(elm) {
elm.classList.toggle('active');
}
function deactivateAnyNonActiveDropdown(evt) {
const { currentTarget, target } = evt;
Array
.from(
document.querySelectorAll('.selector')
)
.filter(elm =>
elm !== currentTarget ||
elm
.querySelector('.dropdown')
.contains(target)
)
.forEach(deactivateDropdown);
}
function handleSelectorClick(evt) {
evt.stopPropagation();
deactivateAnyNonActiveDropdown(evt);
toggleDropdown(evt.currentTarget);
}
function init() {
document
.body
.addEventListener('click', deactivateAnyNonActiveDropdown)
document
.querySelectorAll('.selector')
.forEach(item =>
item.addEventListener('click', handleSelectorClick)
);
}
init();
.body-ct .selector{display:inline-block;width:200px;text-align:center;border:1px solid navy;border-radius:4px;}
.body-ct .selector:hover{cursor:pointer;}
.body-ct .selector h4{padding:5px 0;}
.body-ct .selector {position:relative;}
.body-ct .selector .dropdown{display:none;position:absolute;top:55px;z-index:2;width:200px;border:3px solid red;background-color:#FFF;}
/*.body-ct .selector .dropdown.active{display:block;}*/
.body-ct .selector.active .dropdown {display:block;}
.body-ct .selector .dropdown ul{list-style-type:none;text-align:left;background-color:#FFF;}
.body-ct .selector .dropdown ul li{background-color:#FFF;}
.body-ct .selector .dropdown ul li:hover{font-weight:700;}
<section class="body-ct">
<div class="container">
<div class="row">
<div class="col-12 sel-ct">
<div class="story-type-sel selector">
<h4>Story Type</h4>
<div class="dropdown story-selector">
<ul>
<li data-type="article">Article</li>
<li data-type="pr">Press Release</li>
<li data-type="analyst">Analyst Report</li>
</ul>
</div>
</div>
<div class="year-sel selector">
<h4>Year</h4>
<div class="dropdown year-selector">
<ul>
<li data-year="2021">2021</li>
<li data-year="2020">2020</li>
<li data-year="2019">2019</li>
<li data-year="2018">2018</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</section>