Why won't Jquery .show() display anchor div within bootstrap accordion collapse?

491 Views Asked by At

I am fairly new to web dev. I've spent several days working on this (embarrassing) and researched everything I can think of.

I'm using Rails 5, bootstrap 3 and jquery 2.1.3.

I have a bootstrap accordion. Within the various collapse content divs I want links to content that resides in the same accordion but a different collapse panel.

In the current collapse panel I have a link like this:

<a class="self-link" href="#buried_sub_anchor">Buried Sub Anchor</a>

In a different collapse panel I have a content div like this:

<div class="anchor" id="buried_sub_anchor">

My jquery code to handle the click is:

  $('.self-link').on('click', function() {expandCollapseWithHash(this.hash); });

  function expandCollapseWithHash(hash) {
    var $collapses = $(hash).parents('.collapse');
    if ($collapses.length > 0) {
      var $collapse = $collapses.first()
      $collapse.collapse('show');
    }
  }

When the .collapse('show') is called I'm expecting bootstrap to magically close the current panel and open the target. Then, once that transition is done I'm expecting the 'shown' event to fire, which I handle like this:

  $('.collapse').on('shown.bs.collapse', function() {
    if (location.hash) {
      $(location.hash).show();
    }
  });

I'm expecting the .show() call to jump the page right to the anchored div. But no dice.

To summarize, when I click on the link I want:

  1. The current panel to .collapse('hide')
  2. The target panel to .collapse('show')
  3. The page to jump to the anchored div in the target panel

Instead, always:

  1. The current panel doesn't change (i.e. it stays shown)
  2. The target panel does show
  3. The page jumps to a new location but nowhere near the desired anchor div section

My questions are:

  1. Why doesn't the current panel collapse? I would expect the bootstrap accordion functionality to do this as a result of the $collapse.collapse('show') call.
  2. Why doesn't the page scroll to the linked content?

Here is a fiddle. To reproduce:

  1. click on the "More Details" section
  2. scroll all the way down
  3. click the "Buried sub anchor" link
2

There are 2 best solutions below

0
On BEST ANSWER

Well, I stuck at it and finally have a solution. The only thing I was doing wrong was this line $(location.hash).show(); needed to instead be location.href = location.hash;.

That makes it so it jumps to show the desired div at the very top of the page just below the header.

Here's the updated fiddle.

Within, click the "More Details" panel header, then click the link within that panel (i.e. "Buried Sub Anchor"). That will expand the "Miscellaneous" panel header and jump the page right to the "Buried Sub Anchor text" div.

Along the journey I picked up something else of value. If the above-mentioned code change was all I made (i.e. $(location.hash).show(); -> location.href = location.hash;), when clicking on the link it would expand the target panel and jump to where you want to go. But it would also leave the panel expanded / shown that you just left as well.

The reason was not obvious to me and is fixed with the following code:

$('#accordion .panel-collapse').collapse({ 
  parent: '#accordion', 
  toggle: false 
});

I found that code on this github page. It initializes the individual accordion collapse elements because

the data api...lazy instantiates, something which .collapse('show') doesn't take into account.

So now in the updated fiddle when you click the link it closes the leaving panel, opens the target panel and jumps the page right to the target div, just the way I wanted.

1
On

You have to force a scrollTop to do this.
I like to make it within an animate(), which makes it smoother.

Here is the only changes I've made to your Fiddle:

$('.collapse').on('shown.bs.collapse', function() {
    if (location.hash) {
        $(location.hash).show();
    }

    var thisId = $(this).attr("id");
    var thisOffsetTop = $("#" + thisId).offset().top;
    var headerHeight = $(".navbar").height();
    var myTitleHeight = $(".container h1").first().height();

    console.log(thisId);
    console.log("thisOffsetTop: " + thisOffsetTop);
    console.log("headerHeight: " + headerHeight);
    console.log("MyTitleHeight: " + myTitleHeight);

    // Animation to scrollTo the right position
    $('html, body').animate({
        scrollTop: thisOffsetTop - headerHeight - myTitleHeight
    });
});

I left all relevant console.log()...
See the updated Fiddle.