2

I know that this question has been asked many times before, but I can't find a solution excepting of hard-coding the jQuery file... So, I have to show a description when user presses a button and there are multiple descriptions, each with their buttons.

This is what I've done so far...

HTML:

<div id="trDest1" class="trDest">
    <!-- Some content here -->
    <button class="expandArrow">Show</button>
    <button class="closeArrow">Hide</button>
</div>
<div id="trDest1_details" class="details">
    <p>show details</p>
</div>

<div id="trDest2" class="trDest">
    <!-- Some content here -->
    <button class="expandArrow">Show</button>
    <button class="closeArrow">Hide</button>
</div>
<div id="trDest2_details" class="details">
    <p>show details</p>
</div>

<div id="trDest3" class="trDest">
    <!-- Some content here -->
    <button class="expandArrow">Show</button>
    <button class="closeArrow">Hide</button>
</div>
<div id="trDest3_details" class="details">
    <p>show details</p>
</div>

CSS:

.closeArrow {
  display: none;
}

.visible-description .expandArrow {
  display: none;
}

.visible-description .closeArrow {
  display: inline;
}

.visible-description + .trip_details {
  display: block;
}

.details {
    display: none;
}

jQuery:

  // Show/hide Descriptions
  $('#trDest1 .expandArrow').click(function(){
    $('#trDest1').addClass('visible-description');
    $('#trDest1_details').show();
  });

  $('#trDest1 .closeArrow').click(function(){
    $('#trDest1').removeClass('visible-description');
    $('#trDest1_details').hide();
  });

  // Show/hide Descriptions
  $('#trDest2 .expandArrow').click(function(){
    $('#trDest2').addClass('visible-description');
    $('#trDest2_details').show();
  });

  $('#trDest2 .closeArrow').click(function(){
    $('#trDest2').removeClass('visible-description');
    $('#trDest2_details').hide();
  });

  // Show/hide Descriptions
  $('#trDest3 .expandArrow').click(function(){
    $('#trDest3').addClass('visible-description');
    $('#trDest3_details').show();
  });

  $('#trDest3 .closeArrow').click(function(){
    $('#trDest3').removeClass('visible-description');
    $('#trDest3_details').hide();
  });

As you can see, I wrote a function for each of those divs and I'm wondering if there is another way to clean these functions and add only a function which can do the same...

I can't change the structure of the HTML code.

jsfiddle : https://jsfiddle.net/q84Lnw0y/

1
  • If you know how many divs there are, you could try with a for loop... Commented Nov 21, 2015 at 13:19

6 Answers 6

4

You could use following snippet to target relevant elements:

$('.expandArrow, .hideArrow').on('click', function(){
    var isExpand = $(this).hasClass('expandArrow');
    $(this).closest('.trDest').toggleClass('visible-description', isExpand).next().toggle(isExpand);
});

-jsFiddle

Description:

$(this).closest('.trDest') // Get closest ancestor with class trDest
    .toggleClass('visible-description', isExpand) // Add class `visible-description` if second param `isExpand` is true, else remove class
    .next() // Get immediate next sibling element
    .toggle(isExpand); // Show if `isExpand` is true, else hide it
Sign up to request clarification or add additional context in comments.

9 Comments

Small improvement, cache the result of $(this).hasClass('expandArrow')
@Tushar Ya, i was thinking about it. Let me cache it :)
And it can be done in one line, but less readable $('.expandArrow, .hideArrow').on('click', function () { var hasClass = $(this).hasClass('expandArrow'); $(this).closest('.trDest').toggleClass('visible-description', hasClass).next().toggle(hasClass); });
Thank you @A.Wolff , this works perfectly! As an improvement, is there any function that scrolls to the visible details when the user clicks the expand arrow?
@PavelValeriu Ya, there are some ways, the native js one is scrollIntoView: $(this).closest('.trDest').toggleClass('visible-description', isExpand).next().toggle(isExpand)[0].scrollIntoView();
|
1

I'd personally suggest the following:

// delegating the .trDest elements to detect the 'click' events on
// the descendent elements ('.expandArrow, .hideArrow'):
$('.trDest').on('click', '.expandArrow, .hideArrow', function (e) {

    // a reference to the clicked element:
    var arrow = $(this),

        // a reference to the element you want to affect:
        nextDetails = arrow.closest('div').next('.details');

    // checking that the clicked button has the class of
    // 'expandArrow':
    if (arrow.is('.expandArrow')) {

        // it is, we find all the '.details' elements,
        // that are not the the element to affect,
        // and slide them up (hide() could be used,
        // but slideUp() is often less visually jarring):
        $('.details').not(nextDetails).slideUp();

        // then we slideDown() the element we wish to show
        // (if it's already visible then nothing happens):
        nextDetails.slideDown();

    // otherwise the element (because of the restrictions
    // in the selector for the on() method) must be
    // .hideArrow, in which case we hide the
    // nextDetails element by sliding it up:
    } else {
        nextDetails.slideUp();
    }

// here we now look for the descendant '.hideArrow'
// elements and trigger the click event in order that
// that the 'nextDetails' elements are hidden on page-load:
}).find('.hideArrow').click();

$('.trDest').on('click', '.expandArrow, .hideArrow', function(e) {
  var arrow = $(this),
    nextDetails = arrow.closest('div').next('.details');

  if (arrow.is('.expandArrow')) {
    $('.details').not(nextDetails).slideUp();
    nextDetails.slideDown();
  } else {
    nextDetails.slideUp();
  }
}).find('.hideArrow').click();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="trDest1" class="trDest">
  <!-- Some content here -->
  <button class="expandArrow">Show</button>
  <button class="hideArrow">Hide</button>
</div>
<div id="trDest2_details" class="details">
  <p>show details</p>
</div>
<div id="trDest2" class="trDest">
  <!-- Some content here -->
  <button class="expandArrow">Show</button>
  <button class="hideArrow">Hide</button>
</div>
<div id="trDest2_details" class="details">
  <p>show details</p>
</div>
<div id="trDest3" class="trDest">
  <!-- Some content here -->
  <button class="expandArrow">Show</button>
  <button class="hideArrow">Hide</button>
</div>
<div id="trDest2_details" class="details">
  <p>show details</p>
</div>

JS Fiddle demo.

References:

2 Comments

Nice one but better would be to define visibility by default, not trigger click event: jsfiddle.net/ovpftvtk/1 (I'm sure you know...) But still, not same behaviour as OP's code, only one .details is shown at a time (even i know you know what you are doing...) :)
@A.Wolff: thank you very much; I do that because I'm a hobbyist and I remember how difficult it is to learn how JavaScript (or its libraries) worked, and how much I appreciated the time some users spent explaining code to me. Given that, it seems only fair that I try to help teach as much as possible (within the constraints of the the code, obviously).
1

This should do what you want

$('.trDest button').on('click', function() {
    var myButton = $(this),
        trDest = myButton.parent(),
        detailsId = $('#' + trDest.attr('id') + '_details');

    trDest.toggleClass('visible-description');
    myButton.hasClass('expandArrow') ? detailsId.show() : detailsId.hide();
});

Fiddle here: http://jsfiddle.net/orysrvb2/1/

1 Comment

Switched addClass() to toggleClass() to make it work as intended. Also, this is a drop-in replacement for the javascript in OP description. No CSS or html changes needed
0

If you can't change your HTML, maybe something like this will work:

JavaScript

$('.expandArrow').click(function(){
    var parent = $(this).parent();
    parent.addClass('visible-description');
    parent.next('.details').show();
    parent.find(".hideArrow").show();
});

$('.hideArrow').click(function(){
    var parent = $(this).parent();
    parent.removeClass('visible-description');
    parent.next('.details').hide();
    parent.find(".hideArrow").hide();
});

Comments

0

Jsfiddle

  // Show/hide Descriptions
  $('.trDest .expandArrow').click(function() {
    $(this).parent().next().removeClass('hidden');
  });

  $('.trDest .hideArrow').click(function() {
    $(this).parent().next().addClass('hidden');
  });
.hidden {
  display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="trDest1" class="trDest">
  <!-- Some content here -->
  <button class="expandArrow">Show</button>
  <button class="hideArrow">Hide</button>
</div>
<div id="trDest2_details" class="details">
  <p>show details</p>
</div>

<div id="trDest2" class="trDest">
  <!-- Some content here -->
  <button class="expandArrow">Show</button>
  <button class="hideArrow">Hide</button>
</div>
<div id="trDest2_details" class="details">
  <p>show details</p>
</div>

<div id="trDest3" class="trDest">
  <!-- Some content here -->
  <button class="expandArrow">Show</button>
  <button class="hideArrow">Hide</button>
</div>
<div id="trDest2_details" class="details">
  <p>show details</p>
</div>

Comments

0

one of the ways:

var numberDiv = 3;
for(var i=0; i < 4; i++){
  $('#trDest' + i + ' .expandArrow').i = i;
  $('#trDest' + i + ' .closeArrow').i = i;

  $('#trDest' + i + ' .expandArrow').click(function(){
    $('#trDest'+this.i).addClass('visible-description');
    $('#trDest'+this.i+' _details').show();
  });

  $('#trDest' + i + ' .closeArrow').click(function(){
    $('#trDest' + this.i).removeClass('visible-description');
    $('#trDest'+ this.i +' _details').hide();
  });
}

See if this helps

3 Comments

Still e.g: '#trDest' + i + '.expandArrow' should be: '#trDest' + i + ' .expandArrow' Otherwise, you have fixed closure issue :)
Can't you use an IIFE instead of setting i on the buttons? (function(i){ //Code })(i);
@Arg0n Sure you can and it is usually how it is done (or using function passing param)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.