How to show only one div at the time?

Hubert Strawa :

I've got three sections, inside of which there are two divs. Inside the first one I have a button and after clicking it I should have the next one opened. However, only one div should be visible at the time (so when you click the next one, previous one should be closed). And I've got this functionality, but after clicking on the button again - it doesn't close the corresponding div.

I set up an example of my problem on codepen:

https://codepen.io/hubertstrawa/pen/abOwWMJ
<section>
  <div class="product">
    <span class="btn">Show more</span>
    <p>Lorem ipsum</p>
  </div>
  <div class="product-more displayNone">
    Test
  </div>
</section>
$('.btn').click(function(e) {

  // only one div to be shown but can't be closed as well.
  $('.product-more').each(function(i, v) {
  $(this).removeClass('displayBlock');
  $(this).addClass('displayNone');
  })

  if ($(e.target).parent().next().hasClass('displayNone')) {
    $(e.target).parent().next().removeClass('displayNone');
    $(e.target).parent().next().addClass('displayBlock');
  } else {
    $(e.target).parent().next().removeClass('displayBlock');
    $(e.target).parent().next().addClass('displayNone');
  }

});

Any ideas how can I make it work?

Thank you

Roko C. Buljan :

Just to show you a better way to handle the desired

Just change a .is-open on a parent element.

<section class="product is-open">    <!-- is-open toggled by JS -->
    <div class="product-more"></div> <!-- handle children styles using CSS -->
</section>
                 .product-more { display: none; }  /* default */
.product.is-open .product-more { display: block; } /* when ancestor is .is-open*/

Use delegateTarget inside the .on() method to get back the .product delegator element

const $product = $('.product'); // Collect all current products

$product.on('click', '.btn', function(e) {

  const $thisProd = $(e.delegateTarget);          // The .product delegator
  $product.not($thisProd).removeClass('is-open'); // Handle all (but not this)
  $thisProd.toggleClass('is-open');               // Handle current

});
/* QuickReset */ * {margin: 0; box-sizing: border-box;}

.product {
  background-color: #ededed;
  width: 400px;
  margin: 0 auto;
  margin-bottom: 1rem;
}

.product-title {
  position: relative;
  padding: 1rem;
}

.product .btn {
  position: absolute;
  bottom: 0;
  right: 0;
  padding: .7rem;
  background-color: cyan;
  cursor: pointer;
}

.product-more {
  width: 100%;
  padding: 1rem;
  background-color: cyan;
  display: none; /* by default */
}

.product.is-open .product-more {
  display: block;
}
<section class="product">
  <div class="product-title">
    <p>Lorem ipsum</p>
    <span class="btn">Show more</span>
  </div>
  <div class="product-more">Test</div>
</section>

<section class="product">
  <div class="product-title">
    <p>Lorem ipsum</p>
    <span class="btn">Show more</span>
  </div>
  <div class="product-more">Test</div>
</section>

<section class="product">
  <div class="product-title">
    <p>Lorem ipsum</p>
    <span class="btn">Show more</span>
  </div>
  <div class="product-more">Test</div>
</section>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>

This is preferred, since it lets you change HTML and CSS, and not worry any more about JavaScript - whilst by using .prev(), .next() or .parent() (like the other answers suggest) JS is just waiting for you to change the markup - to break.

  • No need to traverse back and forth your selectors.
  • No need for .displayNone and .displayBlock on the product-more element.

Handling dynamic .product

if your .product are dynamic elements, here's another solution to the above concept:

$('.allProducts').on('click', '.btn', function(e) {

  const $product = $(e.delegateTarget).find('.product'); // Get all .product
  const $thisProd = $(this).closest('.product'); // The closest .product ancestor
  $product.not($thisProd).removeClass('is-open'); // Handle all (but not this)
  $thisProd.toggleClass('is-open'); // Handle current

});
/* QuickReset */ * {margin: 0; box-sizing: border-box;}

.product {
  background-color: #ededed;
  width: 400px;
  margin: 0 auto;
  margin-bottom: 1rem;
}

.product-title {
  position: relative;
  padding: 1rem;
}

.product .btn {
  position: absolute;
  bottom: 0;
  right: 0;
  padding: .7rem;
  background-color: cyan;
  cursor: pointer;
}

.product-more {
  width: 100%;
  padding: 1rem;
  background-color: cyan;
  display: none; /* by default */
}

.product.is-open .product-more {
  display: block;
}
<div class="allProducts">

  <section class="product">
    <div class="product-title">
      <p>Lorem ipsum</p>
      <span class="btn">Show more</span>
    </div>
    <div class="product-more">Test</div>
  </section>

  <section class="product">
    <div class="product-title">
      <p>Lorem ipsum</p>
      <span class="btn">Show more</span>
    </div>
    <div class="product-more">Test</div>
  </section>

  <section class="product">
    <div class="product-title">
      <p>Lorem ipsum</p>
      <span class="btn">Show more</span>
    </div>
    <div class="product-more">Test</div>
  </section>

</div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=18273&siteId=1