javascripthtmltwitter-bootstrapbootstrap-4twitter-bootstrap-4

Bootstrap 4 change icon in collapse


I use bootstrap 4 alpha 6 version. In my page I have several collapse blocks. I want to change icon when user open/close that block. As I have several of these blocks I used class but it didnt work. Whats wrong I did? P.S. When I use id in JS works fine, but as you see I have several collapse block and dont want to "copy & paste".

html:

<div class="card">
    <div class="card-header">
         <div class="d-flex align-items-center justify-content-between">
              <button data-toggle="collapse" data-target="#collapse-projects" aria-expanded="false" aria-controls="collapse-projects">
                    <i class="fa fa-eye" aria-hidden="true"></i>
              </button>
         </div>
    </div>
    <div class="card-block">
        <div class="collapse" id="collapse-projects">
           ***SOME CONTENT***
        </div>
    </div>
</div>

<div class="card">
    <div class="card-header">
         <div class="d-flex align-items-center justify-content-between">
              <button data-toggle="collapse" data-target="#collapse-tasks" aria-expanded="false" aria-controls="collapse-tasks">
                    <i class="fa fa-eye" aria-hidden="true"></i>
              </button>
         </div>
    </div>
    <div class="card-block">
        <div class="collapse" id="collapse-tasks">
           ***SOME CONTENT***
        </div>
    </div>
</div>

JS:

$(document).ready(function () {
            $('.collapse')
                .on('shown.bs.collapse', function() {
                    $(this)
                        .parent()
                        .find(".fa-eye")
                        .removeClass("fa-eye")
                        .addClass("fa-eye-slash");
                })
                .on('hidden.bs.collapse', function() {
                    $(this)
                        .parent()
                        .find(".fa-eye-slash")
                        .removeClass("fa-eye-slash")
                        .addClass("fa-eye");
                });
        });

Solution

  • The problem is: all your button and .collapse elements are in the same DOM level, which means when .collapse's shown.bs.collapse or hidden.bs.collapse event is fired, the $(this).parent() in the event handler point to .collapse's parent -- the most outside one that is not displayed in your code. Let's make it as OUTER. Then, when you invoke find, removeClass and addClass on OUTER, all icons will be changed, which is why "it didn't work".

    To fix this issue, you need to make a unit wrapper which contains a group of button and .collapse elements. In this way, $(this).parent() will point to the group and only the clicked button icon is changed.

    I've made a snippet for the fix, please check:

    $(document).ready(function() {
      $('.collapse')
        .on('shown.bs.collapse', function() {
          $(this)
            .parent()
            .find(".fa-eye")
            .removeClass("fa-eye")
            .addClass("fa-eye-slash");
        })
        .on('hidden.bs.collapse', function() {
          $(this)
            .parent()
            .find(".fa-eye-slash")
            .removeClass("fa-eye-slash")
            .addClass("fa-eye");
        });
    });
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
    <script src="https://use.fontawesome.com/b4f468aefc.js"></script>
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" rel="stylesheet"/>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/js/bootstrap.min.js"></script>
    
    <div class="wrapper">
      <button data-toggle="collapse" data-target="#collapse-projects" aria-expanded="false" aria-controls="collapse-projects">
        <i class="fa fa-eye" aria-hidden="true"></i>
      </button>
      <div class="collapse" id="collapse-projects">
        ***SOME CONTENT***
      </div>
    </div>
    
    <div class="wrapper">
      <button data-toggle="collapse" data-target="#collapse-tasks" aria-expanded="false" aria-controls="collapse-tasks">
        <i class="fa fa-eye" aria-hidden="true"></i>
      </button>
      <div class="collapse" id="collapse-tasks">
        ***SOME CONTENT***
      </div>
    </div>