1

I've written a simple code for an FAQ list; each question is opened on click event and must be manually closed. Is there a way re-work this code so that in the event of clicking on a question to open (slidedown), ones that have been previously been open, automatically slideup to close?

{% javascript %}
(function() {
  $('body').on('click', '.shopify_explorer_faq__question', function() {
    $(this).next('.shopify_explorer_faq__answer').slideToggle(250).toggleClass('active');
    $(this).toggleClass('active');
  });

  $(document).on('shopify:block:select', '#shopify-section-page-shopify_explorer_faq-template', function(event) {
    $(event.target).find('.shopify_explorer_faq__answer').slideDown(250);
  });

  $(document).on('shopify:block:deselect', '#shopify-section-page-shopify_explorer_faq-template', function(event) {
    $(event.target).find('.shopify_explorer_faq__answer').slideUp(250);
  });
}());
{% endjavascript %}

3 Answers3

1

You can do this keeping track of the current opened FAQ box. Just to fix the idea and make it as simple as possible, let's say each of the FAQ boxes has an id and the boxes themselves are divs:

<div id="faq-1" class="faq-box">
  Text of the FAQ 1
</div>

<div id="faq-2" class="faq-box">
  Text of the FAQ 2
</div>

...

<div id="faq-n" class="faq-box">
  Text of the FAQ n
</div>

You can get the behavior you're looking for like this:

var current_faq = ''; // Keep track of the current faq box opened

jQuery( '.faq-box' ).on( 'click', function() {
  // Check if it has been clicked the current box
  if ( jQuery( this ).attr( 'id' ) == current_faq ) {
    // It has been clicked the current box, just slide it up to close
    jQuery( this ).removeClass( 'active' ).slideUp();
    // Set current box opened to empty
    current_faq = '';
  else {
    // Slide down this box
    jQuery( this ).addClass( 'active' ).slideDown();
    // Check if there's a current box opened
    if ( current_faq != '' ) {
      // Slide up the current box
      jQuery( current_faq ).removeClass( 'active' ).slideUp();
    }
    // Set the current box
    current_faq = jQuery( this ).attr( 'id' );
  }
} );
Jazzpaths
  • 645
  • 5
  • 9
0

You can achieve your desired behaviour by simply adding a line at the top of your first function that uses the .not() selector:

$('body').on('click', '.shopify_explorer_faq__question', function() {
  $('.shopify_explorer_faq__question').not(this).next('.shopify_explorer_faq__answer').slideUp(250);
  $(this).next('.shopify_explorer_faq__answer').slideToggle(250).toggleClass('active');
  $(this).toggleClass('active');
});
Rob Kwasowski
  • 2,690
  • 3
  • 13
  • 32
0

HTML

Assuming the HTML is a alternating pattern of .question and .answer scheme.

<ul class="faq">
  <li class="question">...</li>
  <li class="answer">...</li>
  ...
</ul>

Narrow down the primary selector to .faq. $("body") and $(document) should be used for certain events like "key" or "load" event types -- not common events like "click".


jQuery

The second parameter event.data is used to designate this (in this circumstance is also event.target). In the example below .question is this:

$('.faq').on('click', '.question', function(event) {...

Reference $(this).next('.answer') with a variable. A variable referencing a jQuery Object is commonly prefixed with a $ (recommended but not required).

let $answer = $(this).next('.answer');

The desired behavior is that of an accordion:

  • When an element among multiple alike element siblings is clicked (.question that is this or event.target), will toggle open (and/or its associated element - $answer) if it was originally closed and vice versa.

    $answer.slideToggle(250).toggleClass('active');
    $(this).toggleClass('active');
    
  • All sibling elements will close with the exception of this (and associated elements if applicable). The exception can be selected by using the .not() method.

    $('.question').not(this).removeClass('active')
    $('.answer').not($answer).slideUp(250).removeClass('active');
    

'shopify:block:select/deselect' events are non-standard events that are unique to the Shopify platform. Not 100% sure if the following would work, but if they can be delegated with the .on() method, then it could probably be triggered with the .trigger() method.

if ($(this).hasClass('active')) {
  $answer.trigger('shopify:block:select');
  $('.answer').not($answer).trigger('shopify:block:deselect');
} else {
  $('.answer').trigger('shopify:block:deselect');
} 

Demo

$(function() {
  $('.answer').hide();
  $('.faq').on('click', '.question', function(e) {
    let $answer = $(this).next('.answer');
    $answer.slideToggle(250).toggleClass('active');
    $(this).toggleClass('active');
    $('.question').not(this).removeClass('active');
    $('.answer').not($answer).slideUp(250).removeClass('active');
    if ($(this).hasClass('active')) {
      $answer.trigger('shopify:block:select');
      $('.answer').not($answer).trigger('shopify:block:deselect');
    } else {
      $('.answer').trigger('shopify:block:deselect');
    }
  });
});
:root {
  font: 400 3vw/6vh Arial
}

li {
  padding: 3vh 2vw 1vh;
  margin-bottom: 2vh
}

.question {
  cursor: pointer
}

.answer {
  list-style: none;
  color: blue
}

.active {
  text-decoration: underline;
  font-weight: 900
}
<main>
  <section>
    <header>
      <h1>FAQ</h1>
    </header>
    <ul class='faq'>
      <li class='question'>Question?</li>
      <li class='answer'>Answer.</li>
      <li class='question'>Question?</li>
      <li class='answer'>Answer.</li>
      <li class='question'>Question?</li>
      <li class='answer'>Answer.</li>
      <li class='question'>Question?</li>
      <li class='answer'>Answer.</li>
      <li class='question'>Question?</li>
      <li class='answer'>Answer.</li>
    </ul>
  </section>
</main>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Community
  • 1
  • 1
zer00ne
  • 41,936
  • 6
  • 41
  • 68