【问题标题】:JS loop to add event listeners not working during one condition for second elementJS循环添加事件侦听器在第二个元素的一个条件下不起作用
【发布时间】:2020-05-11 11:01:01
【问题描述】:

我正在编写一段代码,通过单击相应按钮来切换某些 UL 项目的可见性,方法是切换 UL 上将不透明度/高度更改为 0 的类(这样我也可以应用过渡)。当第一个元素切换为不可见时,第二个元素不起作用。 onclick 事件未注册。

当按钮和 h3 的样式未设置为出现在同一行时,该代码有效,当我尝试使用 flex、float 或 inline 将两个元素并排放置时,该代码会中断。有没有一种方法可以用来定位它们并且仍然保留完整的功能?

const buttons = document.getElementsByClassName("toggle");
const lists = document.getElementsByClassName("list");

for (let i = 0; i < buttons.length; i++) {
  buttons[i].addEventListener('click', function() {
    toggle(i);
  })
};

function toggle(i) {
  if (lists[i].classList.contains("hide")) {
    lists[i].classList.remove("hide");
  } else {
    lists[i].classList.add("hide");
  }
}
<div id="sidebar">
  <div class="side">
    <div class="header">
      <h3>Protein</h3>
      <button class="toggle"></button>
    </div>
    <div>
      <ul class="list">
        <li class="fi">Beef</li>
        <li class="fi">Fish</li>
        <li class="fi">Lamb</li>
        <li class="fi">Pork</li>
        <li class="fi">Poultry</li>
        <li class="fi">Shellfish</li>
        <li class="fi">Vegetarian</li>
      </ul>
    </div>
  </div>
  <div class="side">
    <div class="header">
      <h3>Cuisine</h3>
      <button class="toggle"></button>
    </div>
    <ul class="list">
      <li class="fi">African</li>
      <li class="fi">American</li>
      <li class="fi">Asian</li>
      <li class="fi">British</li>
      <li class="fi">Cajun Creole</li>
      <li class="fi">Carribean</li>
      <li class="fi">Eastern European</li>
      <li class="fi">Show More</li>
    </ul>
  </div>
</div>

<style>
   .header{
      display: flex;
      align-items: center;
   }
   
   .hide {
   height: 0;
   opacity: 0;
   margin: 0;
   }
</style>

gif of the issue

【问题讨论】:

  • 代码看起来很合理。您能否将 HTML 编辑到实时堆栈片段中的问题中,以便我们也能看到问题?
  • 刚刚添加,感谢反馈!
  • 有趣...它的工作方式就像我希望它作为 sn-p 一样。我将尝试在没有额外 CSS 的情况下进行重建,或者查看环境是否存在差异。感谢您提供 Stack Overflow 提示!
  • sn-p 工作得很好。
  • 您是否有理由必须使用不透明度进行隐藏?它正在干扰并覆盖第二个列表。

标签: javascript html dom-events nodelist


【解决方案1】:

由于 user120242 的响应说您覆盖了下面的 side class div,您可以通过添加 &lt;div class="side"&gt; 元素和 overflow: hidden; 样式来解决它,以避免溢出下面的 div,所以试试这个:

如果你想将元素放在一起,你需要像@user120242 所说的那样使用 flexbox。

const buttons = document.getElementsByClassName("toggle");
const lists = document.getElementsByClassName("list");

for (let i = 0; i < buttons.length; i++) {
  buttons[i].addEventListener('click', function() {
    toggle(i);
  })
};

function toggle(i) {
  if (lists[i].classList.contains("hide")) {
    lists[i].classList.remove("hide");
  } else {
    lists[i].classList.add("hide");
  }
}
.side {
overflow: hidden;
}
<div id="sidebar">
  <div class="side">
    <div class="header">
      <h3>Protein</h3>
      <button class="toggle"></button>
    </div>
    <div>
      <ul class="list">
        <li class="fi">Beef</li>
        <li class="fi">Fish</li>
        <li class="fi">Lamb</li>
        <li class="fi">Pork</li>
        <li class="fi">Poultry</li>
        <li class="fi">Shellfish</li>
        <li class="fi">Vegetarian</li>
      </ul>
    </div>
  </div>
  <div class="side">
    <div class="header">
      <h3>Cuisine</h3>
      <button class="toggle"></button>
    </div>
    <ul class="list">
      <li class="fi">African</li>
      <li class="fi">American</li>
      <li class="fi">Asian</li>
      <li class="fi">British</li>
      <li class="fi">Cajun Creole</li>
      <li class="fi">Carribean</li>
      <li class="fi">Eastern European</li>
      <li class="fi">Show More</li>
    </ul>
  </div>
</div>

<style>
   .header{
      display: flex;
      align-items: center;
   }
   
   .hide {
   height: 0;
   opacity: 0;
   margin: 0;
   }
</style>

【讨论】:

    【解决方案2】:

    我添加了 pointer-events: none 所以它不会拦截鼠标事件并且它可以工作,但你可能应该找到另一种方法来处理它。我不知道您在为动画、代码或其他样式做什么,所以这可能是最好的解决方案,具体取决于您在做什么。
    我添加了一个“显示问题”按钮来显示正在发生的事情。该列表(在由不透明度触发的新堆叠上下文中重新绘制)并覆盖第二个列表。

    另一种解决方案是设置位置样式。所以将position: relative 添加到.hide { position: relative } 也可以

    滚动到底部以阅读原因的详细描述(不透明样式)。

    // show problem
    test.onclick=()=>{
      if (lists[0].classList.contains("hide1")) {
        lists[0].classList.remove("hide1");
      } else {
        lists[0].classList.add("hide1");
      }
    }
    
    
    
    const buttons = document.getElementsByClassName("toggle");
    const lists = document.getElementsByClassName("list");
    
    for (let i = 0; i < buttons.length; i++) {
      buttons[i].addEventListener('click', function() {
        toggle(i);
      })
    };
    
    function toggle(i) {
      if (lists[i].classList.contains("hide")) {
        lists[i].classList.remove("hide");
      } else {
        lists[i].classList.add("hide");
      }
    }
    .header { display:flex; flex-direction:row }
    
       .header{
          display: flex;
          align-items: center;
       }
       
       .hide {
       height: 0;
       opacity: 0;
       margin: 0;
       pointer-events: none /* don't intercept mouse events */
       }
    
    /* show problem */
    .hide1 {
    height: 0;
    margin: 0;
    opacity: 0.5;
    border: 1px solid red;
    }
    
    .hide1 * {
    border: 1px solid blue;
    }
    <button id="test">Show Problem</button>
    
    <div id="sidebar">
      <div class="side">
        <div class="header">
          <h3>Protein</h3>
          <button class="toggle"></button>
        </div>
        <div>
          <ul class="list">
            <li class="fi">Beef</li>
            <li class="fi">Fish</li>
            <li class="fi">Lamb</li>
            <li class="fi">Pork</li>
            <li class="fi">Poultry</li>
            <li class="fi">Shellfish</li>
            <li class="fi">Vegetarian</li>
          </ul>
        </div>
      </div>
      <div class="side">
        <div class="header">
          <h3>Cuisine</h3>
          <button class="toggle"></button>
        </div>
        <ul class="list">
          <li class="fi">African</li>
          <li class="fi">American</li>
          <li class="fi">Asian</li>
          <li class="fi">British</li>
          <li class="fi">Cajun Creole</li>
          <li class="fi">Carribean</li>
          <li class="fi">Eastern European</li>
          <li class="fi">Show More</li>
        </ul>
      </div>
    </div>
    
    <style>
       .header{
          display: flex;
          align-items: center;
       }
       
       .hide {
       height: 0;
       opacity: 0;
       margin: 0;
       pointer-events: none
       }
    </style>

    问题与由于重新渲染导致的不透明样式触发的 z-index 堆叠上下文有关:https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context
    您可以通过在第二个列表的标题上设置opacity: 0.9 来证明这一点,以便第二个列表的标题呈现在相同的上下文层中,然后您将能够单击该按钮。
    这个 SO 答案很好地总结了它:
    What has bigger priority: opacity or z-index in browsers?
    如果未定位不透明度小于 1 的元素,则实现必须在其父堆叠上下文中以相同的堆叠顺序绘制它创建的层如果它是具有 'z-index: 0' 和 'opacity: 1' 的定位元素,将使用该元素

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-14
      • 1970-01-01
      • 1970-01-01
      • 2021-03-17
      • 2013-10-09
      • 1970-01-01
      • 2015-11-01
      • 2013-01-04
      相关资源
      最近更新 更多