makshh makshh - 2 months ago 20
jQuery Question

Bootstrap 3 nested collapsible elements with shown and hidden events

I'm using Bootstrap collapse to create 3rd menu level. I'm adding

panel-active
class to the parent to change
more
to
less
in CSS. The problem is that when I'm opening
Item 2.1
,
panel-active
class is added to the
#test3
(which is good) AND
#test2
(which is bad). It seems like Bootstrap
shown.bs.collapse
and
hidden.bs.collapse
events are firing twice when I'm opening/hiding
Item 2.1
and I don't know why it's happening.

To reproduce the problem try to hide
Item 2.1
.



$('.panel-collapse').on('shown.bs.collapse', function() {
$(this).parent().addClass('panel-active');
});

$('.panel-collapse').on('hidden.bs.collapse', function() {
$(this).parent().removeClass('panel-active');
});

.container {
margin-top: 50px;
}
.list-group-item.panel {
margin-bottom: 0;
}
.sidebar-submenu {
padding: 0;
list-style: none;
}
.sidebar-submenu a {
padding-left: 30px !important;
padding-top: 10px;
padding-bottom: 10px;
border-bottom: 1px solid #eee;
display: block;
}
.sidebar-submenu .sidebar-submenu a {
padding-left: 60px !important;
}
.list-group a[data-toggle] {
position: relative;
}
.list-group-item > a[data-toggle]:after,
.sidebar-submenu-item > a[data-toggle]:after
{
content: 'more';
position: absolute;
right: 10px;
top: 6px;
color: #333;
z-index: 438
}
.panel-active > a[data-toggle]:after {
content: 'less'
}
.list-group .list-group-item a {
padding: 13px 12px;
display: block;
border-bottom: 1px solid #eee;
}
.list-group .list-group-item {
padding: 0;
}

<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
<div class="container">
<div class="row">
<div class="col-md-4">
<ul class="list-group" id="sidebar-menu">

<li class="list-group-item panel panel-default">
<a href="#test1" data-toggle="collapse" data-parent="#sidebar-menu" class="collapsed"><i class="fa fa-check fa-fw icon-left"></i>Item 1</a>
<div class="panel-collapse collapse" id="test1">
<ul class="sidebar-submenu">
<li class="sidebar-submenu-item"><a href="#">Item 1.1</a>
</li>
<li class="sidebar-submenu-item"><a href="#">Item 1.2</a>
</li>
<li class="sidebar-submenu-item"><a href="#">Item 1.3</a>
</li>
</ul>
</div>
</li>
<li class="list-group-item panel panel-default">
<a href="#test2" data-toggle="collapse" data-parent="#sidebar-menu" class="collapsed"><i class="fa fa-check fa-fw icon-left"></i>Item 2</a>
<div class="panel-collapse collapse" id="test2">
<ul class="sidebar-submenu">
<li class="sidebar-submenu-item">
<a href="#test3" data-toggle="collapse" class="collapsed">Item 2.1</a>
<div class="panel-collapse collapse" id="test3">
<ul class="sidebar-submenu">
<li class="sidebar-submenu-item"><a href="#">Item 2.1.1</a>
</li>
<li class="sidebar-submenu-item"><a href="#">Item 2.1.2</a>
</li>
<li class="sidebar-submenu-item"><a href="#">Item 2.1.3</a>
</li>
</ul>
</div>
</li>
<li class="sidebar-submenu-item"><a href="#">Item 2.2</a>
</li>
<li class="sidebar-submenu-item"><a href="#">Item 2.3</a>
</li>
</ul>
</div>
</li>

</ul>
</div>
</div>
</div>




Answer

When you are clicking on a child, you are also clicking on the parent element too so both events for both the parent and the child both fire. To only fire the child event and stop it propogating up the dom tree, add e.stopPropagation() as below.

$('.panel-collapse').on('shown.bs.collapse', function(e) {
  e.stopPropagation();
  $(this).parent().addClass('panel-active');
});

$('.panel-collapse').on('hidden.bs.collapse', function(e) {
  e.stopPropagation();
  $(this).parent().removeClass('panel-active');
});
.container {
  margin-top: 50px;
}
.list-group-item.panel {
  margin-bottom: 0;
}
.sidebar-submenu {
  padding: 0;
  list-style: none;
}
.sidebar-submenu a {
  padding-left: 30px !important;
  padding-top: 10px;
  padding-bottom: 10px;
  border-bottom: 1px solid #eee;
  display: block;
}
.sidebar-submenu .sidebar-submenu a {
  padding-left: 60px !important;
}
.list-group a[data-toggle] {
  position: relative;
}
.list-group-item > a[data-toggle]:after,
.sidebar-submenu-item > a[data-toggle]:after
{
  content: 'more';
  position: absolute;
  right: 10px;
  top: 6px;
  color: #333;
  z-index: 438
}
.panel-active > a[data-toggle]:after {
  content: 'less'
 }
.list-group .list-group-item a {
  padding: 13px 12px;
  display: block;
  border-bottom: 1px solid #eee;
}
.list-group .list-group-item {
  padding: 0;
}
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
<div class="container">
  <div class="row">
    <div class="col-md-4">
      <ul class="list-group" id="sidebar-menu">

        <li class="list-group-item panel panel-default">
          <a href="#test1" data-toggle="collapse" data-parent="#sidebar-menu" class="collapsed"><i class="fa fa-check fa-fw icon-left"></i>Item 1</a>
          <div class="panel-collapse collapse" id="test1">
            <ul class="sidebar-submenu">
              <li class="sidebar-submenu-item"><a href="#">Item 1.1</a>
              </li>
              <li class="sidebar-submenu-item"><a href="#">Item 1.2</a>
              </li>
              <li class="sidebar-submenu-item"><a href="#">Item 1.3</a>
              </li>
            </ul>
          </div>
        </li>
        <li class="list-group-item panel panel-default">
          <a href="#test2" data-toggle="collapse" data-parent="#sidebar-menu" class="collapsed"><i class="fa fa-check fa-fw icon-left"></i>Item 2</a>
          <div class="panel-collapse collapse" id="test2">
            <ul class="sidebar-submenu">
              <li class="sidebar-submenu-item">
                <a href="#test3" data-toggle="collapse" class="collapsed">Item 2.1</a>
                <div class="panel-collapse collapse" id="test3">
                  <ul class="sidebar-submenu">
                    <li class="sidebar-submenu-item"><a href="#">Item 2.1.1</a>
                    </li>
                    <li class="sidebar-submenu-item"><a href="#">Item 2.1.2</a>
                    </li>
                    <li class="sidebar-submenu-item"><a href="#">Item 2.1.3</a>
                    </li>
                  </ul>
                </div>
              </li>
              <li class="sidebar-submenu-item"><a href="#">Item 2.2</a>
              </li>
              <li class="sidebar-submenu-item"><a href="#">Item 2.3</a>
              </li>
            </ul>
          </div>
        </li>

      </ul>
    </div>
  </div>
</div>

Comments