0

Hi I am trying to use a Pinterest style image shower inside a collapsing div using Bootstrap 5 and Masonry (https://masonry.desandro.com/). This is what I tried doing:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-+0n0xVW2eSR5OomGNYDnhzAbDsOXxcvSN1TPprVMTNDbiYZCxYbOOl7+AMvyTG2x" crossorigin="anonymous">
</head>

<body>

  <button type="button" class="btn btn-secondary btn-lg" data-bs-toggle="collapse" data-bs-target="#My-collapse">Button
</button>

  <div id="My-collapse" class="collapse">
    <div class="mt-3"></div>
    <div class="row" data-masonry='{"percentPosition": true }'>
      <div class="col-sm-4 col-md-3 py-3">
        <div class="card">
          <img class="card-img-top" src="https://via.placeholder.com/150" alt="Img">
          <div class="card-body">
            <h5 class="card-title">This is an image</h5>
          </div>
        </div>
      </div>
      <div class="col-sm-4 col-md-3 py-3">
        <div class="card">
          <img class="card-img-top" src="https://via.placeholder.com/150" alt="Img">
          <div class="card-body">
            <h5 class="card-title">This is an image</h5>
          </div>
        </div>
      </div>
      <div class="col-sm-4 col-md-3 py-3">
        <div class="card">
          <img class="card-img-top" src="https://via.placeholder.com/150" alt="Img">
          <div class="card-body">
            <h5 class="card-title">This is an image</h5>
          </div>
        </div>
      </div>
      <div class="col-sm-4 col-md-3 py-3">
        <div class="card">
          <img class="card-img-top" src="https://via.placeholder.com/150" alt="Img">
          <div class="card-body">
            <h5 class="card-title">This is an image</h5>
          </div>
        </div>
      </div>
      <div class="col-sm-4 col-md-3 py-3">
        <div class="card">
          <img class="card-img-top" src="https://via.placeholder.com/150" alt="Img">
          <div class="card-body">
            <h5 class="card-title">This is an image</h5>
          </div>
        </div>
      </div>
    </div>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-gtEjrD/SeCtmISkJkNUaaKMoLD0//ElJ19smozuHV6z3Iehds+3Ulb9Bn9Plx0x4" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/masonry/4.2.2/masonry.pkgd.min.js" crossorigin="anonymous"></script>
</body>

</html>

As you can see all the images stack together and the only way to make them move away is by changing the screen size. As such I decided to try update the layout with a script:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-+0n0xVW2eSR5OomGNYDnhzAbDsOXxcvSN1TPprVMTNDbiYZCxYbOOl7+AMvyTG2x" crossorigin="anonymous">
</head>

<body>

  <button type="button" class="btn btn-secondary btn-lg" data-bs-toggle="collapse" data-bs-target="#My-collapse">Button
</button>

  <div id="My-collapse" class="collapse">
    <div class="mt-3"></div>
    <div class="row" id="mason">
      <div class="col-sm-4 col-md-3 py-3 mason-item">
        <div class="card">
          <img class="card-img-top" src="https://via.placeholder.com/150" alt="Img">
          <div class="card-body">
            <h5 class="card-title">This is an image</h5>
          </div>
        </div>
      </div>
      <div class="col-sm-4 col-md-3 py-3 mason-item">
        <div class="card">
          <img class="card-img-top" src="https://via.placeholder.com/150" alt="Img">
          <div class="card-body">
            <h5 class="card-title">This is an image</h5>
          </div>
        </div>
      </div>
      <div class="col-sm-4 col-md-3 py-3 mason-item">
        <div class="card">
          <img class="card-img-top" src="https://via.placeholder.com/150" alt="Img">
          <div class="card-body">
            <h5 class="card-title">This is an image</h5>
          </div>
        </div>
      </div>
      <div class="col-sm-4 col-md-3 py-3 mason-item">
        <div class="card">
          <img class="card-img-top" src="https://via.placeholder.com/150" alt="Img">
          <div class="card-body">
            <h5 class="card-title">This is an image</h5>
          </div>
        </div>
      </div>
      <div class="col-sm-4 col-md-3 py-3 mason-item">
        <div class="card">
          <img class="card-img-top" src="https://via.placeholder.com/150" alt="Img">
          <div class="card-body">
            <h5 class="card-title">This is an image</h5>
          </div>
        </div>
      </div>
    </div>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-gtEjrD/SeCtmISkJkNUaaKMoLD0//ElJ19smozuHV6z3Iehds+3Ulb9Bn9Plx0x4" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/masonry/4.2.2/masonry.pkgd.min.js" crossorigin="anonymous"></script>
  <script>
    const msnry = new Masonry('#mason', {
      "percentPosition": true,
      "itemSelector": '.mason-item'
    });
    const Col = document.getElementById('My-collapse');

    Col.addEventListener('shown.bs.collapse', function() {
      msnry.layout();
    });
  </script>
</body>

</html>

However this is still not perfect as the images all leave from a single spot and go outwards. What I would like is for them to already be in the right spot from the very beginning. Could anybody help me with this or suggest a different Pinterest style layout library that would work with Bootstrap?

alec
  • 56
  • 2
  • 6

1 Answers1

0

I have found the solution to my problem, it involves initialising the masonry once the height of the collapsed div is the final height. We could wait for the transition to finish but this would mean that the masonry would just pop in during the animation, so instead you need to do it when the collapse is first shown. As such we must add the show class, that bootstrap uses to set the height, then trigger a browser reflow (Force browser to trigger reflow while changing CSS), initialise the masonry and finally remove the class.

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-+0n0xVW2eSR5OomGNYDnhzAbDsOXxcvSN1TPprVMTNDbiYZCxYbOOl7+AMvyTG2x" crossorigin="anonymous">
</head>

<body>

<button type="button" class="btn btn-secondary btn-lg" data-bs-toggle="collapse" data-bs-target="#My-collapse">Button
</button>

<div id="My-collapse" class="collapse">
  <div class="mt-3"></div>
  <div class="row" id="mason">
    <div class="col-sm-4 col-md-3 py-3 mason-item">
      <div class="card">
        <img class="card-img-top" src="https://via.placeholder.com/150" alt="Img">
        <div class="card-body">
          <h5 class="card-title">This is an image</h5>
        </div>
      </div>
    </div>
    <div class="col-sm-4 col-md-3 py-3 mason-item">
      <div class="card">
        <img class="card-img-top" src="https://via.placeholder.com/150" alt="Img">
        <div class="card-body">
          <h5 class="card-title">This is an image</h5>
        </div>
      </div>
    </div>
    <div class="col-sm-4 col-md-3 py-3 mason-item">
      <div class="card">
        <img class="card-img-top" src="https://via.placeholder.com/150" alt="Img">
        <div class="card-body">
          <h5 class="card-title">This is an image</h5>
        </div>
      </div>
    </div>
    <div class="col-sm-4 col-md-3 py-3 mason-item">
      <div class="card">
        <img class="card-img-top" src="https://via.placeholder.com/150" alt="Img">
        <div class="card-body">
          <h5 class="card-title">This is an image</h5>
        </div>
      </div>
    </div>
    <div class="col-sm-4 col-md-3 py-3 mason-item">
      <div class="card">
        <img class="card-img-top" src="https://via.placeholder.com/150" alt="Img">
        <div class="card-body">
          <h5 class="card-title">This is an image</h5>
        </div>
      </div>
    </div>
  </div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-gtEjrD/SeCtmISkJkNUaaKMoLD0//ElJ19smozuHV6z3Iehds+3Ulb9Bn9Plx0x4" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/masonry/4.2.2/masonry.pkgd.min.js" crossorigin="anonymous"></script>
<script>
  const msnry = new Masonry('#mason', {
    "percentPosition": true,
    "itemSelector": '.mason-item',
    "initLayout": false
  });
  const Col = document.getElementById('My-collapse');
  let firstTime = true;
  Col.addEventListener('shown.bs.collapse', function() {
    if (firstTime)
    {
      firstTime = false;
      Col.classList.add('show');
      void(Col.offsetHeight);
      msnry.layout();
      Col.classList.remove('show');
    }
  });
</script>
</body>

</html>

(It doesn't seem to work too well when using the stack overflow embed but it works fine on a normal page)

alec
  • 56
  • 2
  • 6