【问题标题】:Display none some content and block one element不显示某些内容并阻止一个元素
【发布时间】:2022-02-03 19:08:12
【问题描述】:

我想制作 3 个按钮,每个按钮将所有内容 div 设置为 display: none,并根据您单击的按钮将内容 div 更改为 display: block。例如,如果我点击第二个按钮,它将只显示第二个 div 内容。

function showPanel(id) {

  var elements = document.getElementsByClassName("content");

  for (let i = 0; i < elements.length; i++) {
    document.getElementById(i).style.display = "none";
  }

  document.getElementById(id).style.display = "block";
}
<button onclick="showPanel('1')">test1</button>
<button onclick="showPanel('2')">test2</button>
<button onclick="showPanel('3')">test3</button>

<div class="content">
  <div id="1" class="content">
    <p>TEST1</p>
  </div>
  <div id="2" class="content">
    <p class="other">TEST2</p>
  </div>
  <div id="3" class="content ">
    <p class="other">TEST3</p>
  </div>
</div>

【问题讨论】:

  • elements.length() 不是函数,而是属性。尝试改用 .length!

标签: javascript html jquery css


【解决方案1】:

您的代码中有几个问题。首先length 是一个属性,而不是一个方法,所以你不需要() 后缀来调用它。其次,HTML 中没有 className 属性。这应该只是class。最后,父容器与您要隐藏的元素共享同一个类,因此所有子元素都会被隐藏,即使它们已应用display: block

纠正这些问题后,您的代码将如下所示:

function showPanel(id) {
  var elements = document.getElementsByClassName("panel");
  for (let i = 0; i < elements.length; i++) {
    elements[i].style.display = "none";
  }
  document.getElementById(id).style.display = "block";
}
<button onclick="showPanel('p1')">test1</button>
<button onclick="showPanel('p2')">test2</button>
<button onclick="showPanel('p3')">test3</button>

<div class="content">
  <div id="p1" class="panel">
    <p>TEST1</p>
  </div>
  <div id="p2" class="panel">
    <p class="other">TEST2</p>
  </div>
  <div id="p3" class="panel">
    <p class="other">TEST3</p>
  </div>
</div>

但是值得注意的是,使用onX 属性已经过时,不是好的做法。更好的解决方案是使用不显眼的事件处理程序,并通过放置在元素上的 data 属性向事件处理程序提供自定义元数据。

改进版的逻辑如下所示:

let buttons = document.querySelectorAll('button');
let panels = document.querySelectorAll('.panel');

buttons.forEach(button => {
  button.addEventListener('click', e => {
    panels.forEach(panel => {
      panel.style.display = panel.id === e.target.dataset.panel ? 'block' : 'none';
    });
  }); 
});
<button data-panel="1">test1</button>
<button data-panel="2">test2</button>
<button data-panel="3">test3</button>

<div class="content">
  <div id="1" class="panel">
    <p>TEST1</p>
  </div>
  <div id="2" class="panel">
    <p class="other">TEST2</p>
  </div>
  <div id="3" class="panel">
    <p class="other">TEST3</p>
  </div>
</div>

【讨论】:

    【解决方案2】:

    多个问题。

    • 可以使用elements.length 而不是elements.length() 计算长度
    • 您为父 div 和子 div 指定了相同的类名。所以隐藏类名content 的所有元素将隐藏你的整个父母本身。所以在将style.display = "block" 更新为所需的目标后,它将无法正常工作。因为你的父母已经是style.display = "none"。所以你应该在那里进行逻辑更新。所以我改了父类名。

    function showPanel(id) {
      var elements = document.getElementsByClassName("content");
      for (let i = 0; i < elements.length; i++) {
        elements[i].style.display = "none";
      }
      document.getElementById(id).style.display = "block";
    }
    <button onclick="showPanel('1')">test1</button>
    <button onclick="showPanel('2')">test2</button>
    <button onclick="showPanel('3')">test3</button>
    
    <div>
      <div id="1" class="content">
        <p>TEST1</p>
      </div>
      <div id="2" class="content">
        <p class="other">TEST2</p>
      </div>
      <div id="3" class="content ">
        <p class="other">TEST3</p>
      </div>
    </div>

    【讨论】:

      【解决方案3】:

      不需要 JS 或 Jquery。您可以使用锚标记代替按钮。然后你用锚点调用元素的 id。最后但并非最不重要的一点是,您通过 CSS 隐藏框并使用 :target 选择器显示元素:

      .content {
        display: none;
      }
      
      .content:target {
        display: block;
      }
      <a href="#1">test1</a><br>
      <a href="#2">test2</a><br>
      <a href="#3">test3</a><br>
      
      <div class="content-container">
        <div id="1" class="content">
          <p>TEST1</p>
        </div>
        <div id="2" class="content">
          <p class="other">TEST2</p>
        </div>
        <div id="3" class="content ">
          <p class="other">TEST3</p>
        </div>
      </div>

      【讨论】:

      • 这是一个非常优雅的解决方案,可能也是我推荐的解决方案,只要您的页面上没有您要定位的其他元素,这会导致与使用 :target 发生冲突。
      • @BrandonMcConnell 这就是为什么您应该在:target 之前调用仅限于这些元素的“唯一”id 或类,因为这将防止您提到的问题。
      • 是的,同意。我只是说,如果他想要有条件地显示的内容中有任何目标链接,单击这些链接将产生关闭这些框的副作用。他也可能需要在其他地方使用定位,同时始终显示这些框之一。
      【解决方案4】:

      我可能会采用一种更优雅的方式来解决问题,这样的问题是使用数据属性将面板及其触发器联系在一起。这样,您就不会冒与页面上可能相同的其他 ID 冲突的风险(ID 应该始终是唯一的)。

      在设置我的事件监听器之前,我会初始化一个openPanel 变量并将其设置为已使用active 类名创建的任何面板。每当我们打开一个新面板时,我们都会覆盖这个变量 vaklue,所以我们不需要每次都做一个新的querySelctor

      然后,在 CSS 中,不是隐藏所有面板,然后显示具有活动类的面板,我们可以编写一个样式,使用 :not 否定选择器隐藏没有活动类的任何面板。

      这就是它的样子(在默认情况下打开面板 #1 来初始化它,但如果您不希望这样做,您可以简单地在 HTML 中从中删除活动类):

      let openPanel = document.querySelector('[data-panel-id].active');
      
      document.addEventListener('click', e => {
        if (e.target?.matches?.('[data-panel-target]')) {
          const id = e.target.dataset.panelTarget;
          if (id) {
            const panel = document.querySelector(`[data-panel-id="${id}"]`);
            if (panel) {
              openPanel?.classList.remove('active');
              panel.classList.add('active');
              openPanel = panel;
            }
          }
        }
      })
      [data-panel-id]:not(.active) {
        display: none;
      }
      <button data-panel-target="1">test1</button>
      <button data-panel-target="2">test2</button>
      <button data-panel-target="3">test3</button>
      
      <main>
        <div data-panel-id="1" class="active">
          <p>TEST #1</p>
        </div>
        <div data-panel-id="2">
          <p>TEST #2</p>
        </div>
        <div data-panel-id="3">
          <p>TEST #3</p>
        </div>
      </main>

      【讨论】:

        【解决方案5】:

        我已经提交了带有我首选建议的单独解决方案,但我想使用与您开始时相同的方法来回答您的问题,以免偏离您已有的代码。

        您已经拥有的代码实际上已经非常接近工作了。我看到的主要问题是您使用的是document.getElementById(i),而您实际上应该使用的是elements[i]。不过,我们可以进一步改进这一点,将 for 循环替换为 for..of 循环,并内联确定正在评估的当前元素是否是我们想要显示的元素。如果是,我们使用'block',否则使用'none'

        初始化我们的函数后,我们可以在 JS 中的一个 ID 上调用它,以默认打开一个面板。 **所有这些.content 元素的父元素也不包含类名content 也很重要,因为这会与您的函数冲突。我已经用一个简单的&lt;main&gt;…&lt;/main&gt; 元素替换了那个父元素。

        以下是我将如何使用您现有的方法来解决这个问题:

        function showPanel(contentId) {
          const elements = Array.from(document.getElementsByClassName('content'));
          for (const element of elements) {
            element.style.display = element.id === contentId ? 'block' : 'none';
          }
        }
        showPanel('1');
        <button onclick="showPanel('1')">test1</button>
        <button onclick="showPanel('2')">test2</button>
        <button onclick="showPanel('3')">test3</button>
        
        <main>
          <div id="1" class="content">
            <p>TEST1</p>
          </div>
          <div id="2" class="content">
            <p>TEST2</p>
          </div>
          <div id="3" class="content ">
            <p>TEST3</p>
          </div>
        </main>

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2016-06-12
          • 1970-01-01
          • 1970-01-01
          • 2016-06-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多