skip to Main Content

I have a container with text that I want to show a read more/read less function that when clicked, displays or hides text. The problem is, I have about 10 of these containers on the same page and when I click a read more in one of the containers it affects every container on the page. I need the read more / read less function to only happen on the container where I click.

jQuery(document).ready(function($) {

  var productDescription = $('.summary'),
    productReadMore = $('.description-read-more'),
    currentHeight = productDescription.height(),
    autoHeight = productDescription.css('height', 'auto').height(),
    showMoreText = 'Read More',
    showLessText = 'Read Less';

  productDescription.css('height', currentHeight);

  productReadMore.click(function() {

    if ($(this).hasClass('active')) {
      $(this).removeClass('active');
      productDescription.removeClass('active');
      productDescription.height(currentHeight).animate({
        height: currentHeight
      }, 0);
      productReadMore.find('.text_more').html(showMoreText);
    } else {
      $(this).addClass('active');
      productDescription.addClass('active');
      productDescription.height(currentHeight).animate({
        height: autoHeight
      }, 0);
      productReadMore.find('.text_more').html(showLessText);
    }
  });
});
<div class="summary">
  <p><strong>Description:</strong></p>
  <p>initial content</p>
  <p>more content</p>
</div>
<div class="description-read-more"><span class="text_more">Read More</span></div>

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

3

Answers


  1. Use event delegation. Add a listener to the document for which the handler only works if the event target (the element that was clicked on) has a read-more class.

    I would then group the section that needs to be displayed within the section that’s triggering its opening/closing. From there grab the summary element of the clicked button using closest, find the section with the text-more class, and then toggle it.

    $(document).on('click', '.read-more', function () {
      $(this)
        
        // Find the closest summary element
        .closest('.summary')
        
        // Find its text-more element
        .find('.text-more')
        
        // Toggle its class
        .toggle();
    });
    .read-more { border: 0; }
    .text-more { display: none; }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <section class="summary">
      <p>Section 1</p>
      <button class="read-more">Read More</button>
      <section class="text-more">
        <p>Section 1 more text</p>
      </section>
    </section>
    <section class="summary">
      <p>Section 2</p>
      <button class="read-more">Read More</button>
      <section class="text-more">
        <p>Section 2 more text</p>
      </section>
    </section>
    <section class="summary">
      <p>Section 3</p>
      <button class="read-more">Read More</button>
      <section class="text-more">
        <p>Section 3 more text</p>
      </section>
    </section>

    If you don’t want to group the markup together you can use data attributes to indicate which section should be toggled instead.

    $(document).on('click', '.read-more', function () {
      
      // Find the id attached to the summary section
      const id = $(this).closest('.summary').data('id');
      
      // Use the id to create a selector that matches
      // the text-more section with that id
      const selector = `.text-more[data-id=${id}]`;
      
      // Use the selector to find that elenent
      const $text = $(document).find(selector);
      
      // Toggle it
      $text.toggle();
    });
    .read-more { border: 0; }
    .text-more { display: none; }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <section class="summary" data-id="1">
      <p>Section 1</p>
      <button class="read-more">Read More</button>
    </section>
    <section class="text-more" data-id="1">
      <p>Section 1 more text</p>
    </section>
    <section class="summary" data-id="2">
      <p>Section 2</p>
      <button class="read-more">Read More</button>
    </section>
    <section class="text-more" data-id="2">
      <p>Section 2 more text</p>
    </section>
    <section class="summary" data-id="3">
      <p>Section 3</p>
      <button class="read-more">Read More</button>
    </section>
    <section class="text-more" data-id="3">
      <p>Section 3 more text</p>
    </section>
    Login or Signup to reply.
  2. One idea, without using JavaScript, would be to animate to auto height using CSS grid and use a checkbox (or radio) input for the toggling.
    In this example I’m using the :has(), but to support Firefox (2023. ATM)
    you can place the input in the parent wrapper and reference sibling elements using ~.

    .additional-outer {
      display: grid;
      grid-template-rows: 0fr;
      transition: grid-template-rows 0.5s ease-in-out;
    }
    
    .additional-inner {
      overflow: hidden;
    }
    
    .more {
      display: inline-flex;
      background: #0bf;
      cursor: pointer;
      padding: 0.5rem 1rem;
      margin-top: 1rem;
    }
    
    .more::before {
      content: "Read more";
    }
    
    .summary:has(input:checked) .more::before {
      content: "Read less";
    }
    
    .summary:has(input:checked) .additional-outer {
      grid-template-rows: 1fr;
    }
    <div class="summary">
      <h3>Some title</h3>
      <div class="initial">
        Initial. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Nesciunt iste, ut vero voluptatem exercitationem dolorem minus facilis natus, officiis porro reiciendis aspernatur ex quae enim tenetur quia dicta veniam amet.
      </div>
      <div class="additional-outer">
        <div class="additional-inner">
          Additional. Repellendus accusantium doloremque et, iusto deserunt alias, cumque totam libero officiis voluptate sunt quisquam nam, consequuntur ipsa placeat nihil. Labore at, quaerat.
        </div>
      </div>
      <label class="more"><input type="checkbox" hidden></label>
    </div>
    
    <div class="summary">
      <h3>Some other description</h3>
      <div class="initial">
        Initial. Adipisicing elit. Placeat illum repudiandae repellat ex accusantium neque quibusdam pariatur aut dignissimos assumenda aliquam, ducimus asperiores consequuntur sint quaerat quia porro voluptatem. Facere!
      </div>
      <div class="additional-outer">
        <div class="additional-inner">
          Additional. Temporibus nisi ut dolorum corrupti officia unde similique, atque aliquid mollitia, rem dolores, dignissimos molestias qui enim, quae repellendus repellat asperiores consectetur..
        </div>
      </div>
      <label class="more"><input type="checkbox" hidden></label>
    </div>
    Login or Signup to reply.
  3. In jQuery when you make a search of the kind of $(someselector) then all the items matching that selector apply. Hence, for example, productReadMore is defined as

    productReadMore = $('.description-read-more')
    

    and that applies for all elements having this class. Which is good when you call the .click() function, because it attaches a click event for all elements having this class. However, inside the function you need to differentiate and use $(this) rather than productReadMore to identify the item to change rather than change all items.

    jQuery(document).ready(function($) {
    
      var productDescription = $('.summary'),
        productReadMore = $('.description-read-more'),
        currentHeight = productDescription.height(),
        autoHeight = productDescription.css('height', 'auto').height(),
        showMoreText = 'Read More',
        showLessText = 'Read Less';
    
      productDescription.css('height', currentHeight);
    
      productReadMore.click(function() {
        productDescription = $(this).prev();
        currentHeight = productDescription.height();
        autoHeight = productDescription.css('height', 'auto').height();
    
        if ($(this).hasClass('active')) {
          $(this).removeClass('active');
          productDescription.removeClass('active');
          productDescription.height(currentHeight).animate({
            height: currentHeight
          }, 0);
          $(this).find('.text_more').html(showMoreText);
        } else {
          $(this).addClass('active');
          productDescription.addClass('active');
          productDescription.height(currentHeight).animate({
            height: autoHeight
          }, 0);
          $(this).find('.text_more').html(showLessText);
        }
      });
    });
    <div class="summary">
      <p><strong>Description:</strong></p>
      <p>initial content</p>
      <p>more content</p>
    </div>
    <div class="description-read-more"><span class="text_more">Read More</span></div>
    <div class="summary">
      <p><strong>Description:</strong></p>
      <p>initial content</p>
      <p>more content</p>
    </div>
    <div class="description-read-more"><span class="text_more">Read More</span></div>
    
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search