Home > database >  How does negative margin work in bootstrap carousel?
How does negative margin work in bootstrap carousel?

Time:09-17

I have created a demo below for the bootstrap 4 carousel having transitions slowed down to 30ses, so that everyone can inspect and have a proper look at the dynamic css being added by js:

.carousel-item {
  transition: transform 30s ease-in-out!important;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<script src="https://getbootstrap.com/docs/4.6/dist/js/bootstrap.bundle.js"></script>

<link href="https://getbootstrap.com/docs/4.6/dist/css/bootstrap.css" rel="stylesheet"/>
<div id="carouselExampleCaptions" class="carousel slide" data-ride="carousel">
  <ol class="carousel-indicators">
    <li data-target="#carouselExampleCaptions" data-slide-to="0" class="active"></li>
    <li data-target="#carouselExampleCaptions" data-slide-to="1"></li>
    <li data-target="#carouselExampleCaptions" data-slide-to="2"></li>
  </ol>
  <div class="carousel-inner">
    <div class="carousel-item active">
      <svg class="bd-placeholder-img bd-placeholder-img-lg d-block w-100" width="800" height="400" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Placeholder: First slide" preserveAspectRatio="xMidYMid slice" focusable="false"><title>Placeholder</title><rect width="100%" height="100%" fill="#777"></rect><text x="50%" y="50%" fill="#555" dy=".3em">First slide</text></svg>
    </div>
    <div class="carousel-item">
      <svg class="bd-placeholder-img bd-placeholder-img-lg d-block w-100" width="800" height="400" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Placeholder: First slide" preserveAspectRatio="xMidYMid slice" focusable="false"><title>Placeholder</title><rect width="100%" height="100%" fill="#777"></rect><text x="50%" y="50%" fill="#555" dy=".3em">Second slide</text></svg>
    </div>
    <div class="carousel-item">
      <svg class="bd-placeholder-img bd-placeholder-img-lg d-block w-100" width="800" height="400" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Placeholder: First slide" preserveAspectRatio="xMidYMid slice" focusable="false"><title>Placeholder</title><rect width="100%" height="100%" fill="#777"></rect><text x="50%" y="50%" fill="#555" dy=".3em">Third slide</text></svg>
    </div>
  </div>
  <a class="carousel-control-prev" href="#carouselExampleCaptions" role="button" data-slide="prev">
    <span class="carousel-control-prev-icon" aria-hidden="true"></span>
    <span class="sr-only">Previous</span>
  </a>
  <a class="carousel-control-next" href="#carouselExampleCaptions" role="button" data-slide="next">
    <span class="carousel-control-next-icon" aria-hidden="true"></span>
    <span class="sr-only">Next</span>
  </a>
</div>

As you can see the all slides are given margin-right: -100% and the active slide is then transformed translateX(-100%). What I don't get is:

Question 1: The margin-right -100% should create negative space for each slide, making all of them appearing on top of each other, but in reality they come side to side, why?

Question 2: If only the active slide is being transformed, why does the slide on right also get transformed with the active slide?

For question 1 consider the following demo: https://jsfiddle.net/2j1ug8x4/ here you can see how 1 and 2 come at the same place. They are given float: left as well just like bootstrap.

CodePudding user response:

Question 1:

The margin-right actually is placing the slides on top of one another - it's the CSS transform that fixes this and makes the slides appear next to each other. Take a look at this example where the slides are forced to display: block and you'll see the third slide, with the other two stacked beneath.

The real reason for using margin-right: -100% is to make sure the slides appear in a row, rather than stacking vertically. See this example where the slides are visible and the negative margins removed - you can scroll down to see all the slides are stacked vertically.

Question 2:

So why does the slide on the right also get transformed? This one is a little tricky to debug, but what I think is happening is bootstrap is adding two classes to the right slide: carousel-item-next and carousel-item-left

If you look at the source CSS, carousel-item-next adds a transform: translateX(100%) to the element. But that CSS rule only includes the transform if the second class is not present:

.carousel-item-next:not(.carousel-item-left),
  transform: translateX(100%);
}

So what I believe is happening is that bootstrap is adding the first class (carousel-item-next), then there is a slight delay before the second class (carousel-item-left) is added.

That delay means the right slide is briefly given a transform of translateX(100%) which is then immediately removed. Because there is a transition on the slide elements, this causes the right slide to animate from translateX(100%) to translateX(0), or from right to left.

Here is a simplified example replicating this behaviour without bootstrap - click on the Move button to trigger the movement. Note the setTimeout used to artificially simulating a gap between the adding of the first and second CSS classes:

const button = document.getElementById('move');

button.addEventListener('click', () => {
  const childElements = document.querySelectorAll('.child');

  childElements[0].classList.toggle('left');
  childElements[1].classList.toggle('right');

  setTimeout(() => {
    childElements[1].classList.toggle('skip');
  }, 0);
});
#move {
  position: absolute;
  top: 10px;
  left: 55%;
}

.container {
  position: relative;
  width: 50%;
}

.child {
  display: none;
  position: relative;
  width: 100%;
  height: 300px;
  border: 1px dotted blue;
  float: left;
  margin-right: -100%;
  transition: 1s transform ease;
}

.left {
  transform: translateX(-100%);
}

.right:not(.skip) {
  transform: translateX(100%);
}

.active,
.right {
  display: block;
}
<div class="container">
  <div class="child active">1</div>
  <div class="child">2</div>
</div>

<button id="move">Move</button>

  • Related