Я хотел бы сделать 3 кнопки, каждая из которых делает весь контент div display: none, и в зависимости от кнопки, которую вы нажали, один из элементов контента изменится на 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>
1
Alejandro Sánchez 3 Фев 2022 в 12:56
4
elements.length() — это не функция, это свойство. Попробуйте вместо этого использовать .length!
 – 
jns
3 Фев 2022 в 12:56

5 ответов

Множественные проблемы.

  • Длина может быть рассчитана с использованием elements.length, а не elements.length().
  • Вы дали одно и то же имя класса как родительскому, так и дочернему 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>
1
Nitheesh 3 Фев 2022 в 13:10

В вашем коде есть пара проблем. Во-первых, 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>
1
Rory McCrossan 3 Фев 2022 в 13:10

Нет необходимости в JS или Jquery. Вместо кнопки можно использовать тег привязки. Затем вы вызываете с помощью привязки идентификатор элемента. И последнее, но не менее важное: вы делаете поля скрытыми с помощью 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>
1
tacoshy 3 Фев 2022 в 13:13
Это очень элегантное решение, и, вероятно, решение, которое я бы порекомендовал, если на вашей странице нет других элементов, на которые вы будете ориентироваться, что может вызвать конфликт с использованием :target.
 – 
Brandon McConnell
3 Фев 2022 в 13:10
Вот почему вы должны вызывать «уникальный» идентификатор или класс, который ограничен этими элементами до :target, так как это предотвратит указанную вами проблему.
 – 
tacoshy
3 Фев 2022 в 13:11
1
Да, согласен. Я просто говорю, что если в контенте, который он хочет условно отобразить, есть какие-либо целевые ссылки, нажатие на них приведет к закрытию этих полей. Также возможно, что ему нужно будет использовать таргетинг в другом месте, всегда оставляя одно из этих полей открытым.
 – 
Brandon McConnell
3 Фев 2022 в 13:16

Более элегантный способ, которым я мог бы подойти к проблеме, подобной этой, состоял бы в том, чтобы связать панели и их триггеры вместе, используя атрибуты данных. Таким образом, вы не рискуете конфликтовать с другими идентификаторами, которые могут совпадать на странице (идентификаторы всегда должны быть уникальными).

Перед настройкой прослушивателя событий я бы инициализировал переменную 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>
0
Brandon McConnell 3 Фев 2022 в 13:53

Я уже отправил отдельное решение с моей предпочтительной рекомендацией, но я хотел дать ответ на ваш вопрос, используя тот же подход, с которого вы начали, чтобы не отклоняться от кода, который у вас уже есть.

Код, который у вас уже был, на самом деле уже был близок к тому, чтобы работать. Основная проблема, которую я увидел, заключалась в том, что вы использовали document.getElementById(i) там, где на самом деле должны были использовать elements[i]. Однако мы можем улучшить это, заменив цикл for циклом for..of и определив встроенно, является ли текущий оцениваемый элемент тем, который мы хотим показать. Если это так, мы используем 'block', иначе 'none'.

После инициализации нашей функции мы можем вызвать ее для одного из наших идентификаторов в JS, чтобы по умолчанию была открыта одна панель. ** Также важно, чтобы родитель всех этих элементов .content НЕ содержал имя класса content, так как это может конфликтовать с вашей функцией. Я заменил этот родительский элемент простым элементом <main>…</main>.

Вот как я могу решить эту проблему, используя ваш существующий подход:

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>
0
Brandon McConnell 3 Фев 2022 в 14:08