Home > front end >  Preventing a HTML element from oscillating up and down when changing padding
Preventing a HTML element from oscillating up and down when changing padding

Time:11-17

I decided to write a basic blackjack card game using HTML, CSS, and JS, and I'm having a problem where a card oscillates up and down because I'm changing the top margin of the card.

.card {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin-top: 50px;
  border: 5px solid black;
  width: 60px;
  height: 100px;
  background-color: #F0F0F0;
  transition-duration: 0.3s;
  transition-property: margin-top, border;
}

.card:hover {
  margin-top: 0px;
  border: 5px solid #3000A0;
  z-index: 0 !important;
  transition-duration: 0.3s;
  transition-property: margin-top, border;
}
<div class="card">Card</div>
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

So when I hover over the card, the top margin of 50px is removed and the card slides up. However, if I hover over the bottom 50px of the card, the card will slide up (out of the selection of :hover, and the card starts to slide back down. The card will then become selected, slide up, become unselected, slide down... repeat.

I tried to fix this by adding

.card::after {
  content: "";
  width: 60px;
  height: 50px;
}

but it didn't seem to do anything.

I considered a solution by adding <div>s before and after the card element and changing their heights, but I'd rather use a CSS solution before JS for this scenario. What could I do to fix this?

CodePudding user response:

When animating elements based on mouse position it's best to delegate the location of the event to another element (in this case, a parent) and animate the transform property as it is only a visual transform (padding, margin, etc. affect the document flow).

For example:

.card-wrapper {
  display: inline-block;
  margin-top: 50px;
  border: 1px solid #ccc;
  padding: 5px;
}

.card-wrapper:hover .card {
  z-index: 0 !important;
  border: 5px solid #3000A0;
  transform: translateY(-50px);
}

.card {
  display: flex;
  flex-direction: column;
  align-items: center;
  border: 5px solid black;
  width: 60px;
  height: 100px;
  background-color: #F0F0F0;
  transition: all 300ms ease;
}
<div class="card-wrapper">
  <div class="card">Card</div>
</div>
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

I added a border and padding to the parent to show you the bounds of the parent. As the mouse enters/exits the parent the child card animates without affecting other elements.

Note that the bounds of the parent includes the child as it transforms to extend beyond the top of the parent.

CodePudding user response:

You can use :after to make an extension of the :hover area on the card. Make the card relative and then absolute position the :after below the card. You can set a background-color on the :after to see where your psudo hover state covers.

.card {
    display: flex;
    flex-direction: column;
    align-items: center;
    margin-top: 50px;
    border: 5px solid black;
    width: 60px;
    height: 100px;
    background-color: #F0F0F0;
    transition-duration: 0.3s;
    transition-property: margin-top, border;
    position: relative;
}

.card:hover:after {
    content: '';
    height: 50px;
    position: absolute;
    top: 100%;
    left: -5px;
    right: -5px;
}

.card:hover {
    margin-top: 0px;
    border: 5px solid #3000A0;
    z-index: 0 !important;
    transition-duration: 0.3s;
    transition-property: margin-top, border;
}
<div class="card"></div>
<iframe name="sif3" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

  • Related