如何有效地管理多个自定义下拉 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 也是该元素的一部分,而不是 adding and/or removeing 这个 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>