Tags Jumping to Bottom on Hover due to Insufficient Space - How to Resolve?

197 Views Asked by At

I am encountering an issue where, upon hovering over the tags, a remove icon is displayed, which increases the space occupied by each tag. As a result, when I hover over the rightmost tag and there is only a small amount of space remaining that cannot accommodate the remove icon, the tag jumps to the bottom or does not get hovered. This behavior is causing a visual inconsistency and disrupting the user experience.

I would appreciate any suggestions or solutions on how to address this problem. Is there a way to prevent the tags from jumping when there is insufficient space for the remove icon upon hovering? Is it possible to incorporate a safe area of remove icon size width on the right side of the design that would only be used when hovering? It's crucial for me to maintain this specific design layout.

.container {
  max-width: 500px;
    margin: 0 auto;
  display: flex; 
}
.tags-container { 
  background-color: #a3b3c6; 
  padding: 20px;
  display:flex;
  flex-wrap: wrap;
}
.tag {
  display: inline-flex;
  transistion: all 1s ease-in-out; 
  padding: 3px;
  background-color: white;
  border: 1px solid black;
}
.tag-close {
  margin-left: 10px; 
  height: 16px;
  width: 16px;
  display:none;
}
.tag:hover {
  background-color: red; 
}
.tag:hover .tag-close {
  display: block;
}

.tags-remove-all {
      display: flex;
    align-items: center;
    padding: 0 10px;
    font-size: 32px;
}
<div class="container">
<div class="tags-container">
  <div class="tag">
    LBL-12334455579
    <div class="tag-close">
      x
    </div>
    </div>
  <div class="tag">
    LBL-12334578
    <div class="tag-close">
      x
    </div>
    </div>
  <div class="tag">
    LBL-1233
    <div class="tag-close">
      x
    </div>
    </div>
  <div class="tag">
    LBL-12334
    <div class="tag-close">
      x
    </div>
    </div>
  <div class="tag">
    LBL-12334
    <div class="tag-close">
      x
    </div>
    </div>
  <div class="tag">
    LBL-12334
    <div class="tag-close">
      x
    </div>
    </div>
</div>
  <div class="tags-remove-all">
    x
  </div>
</div>

6

There are 6 best solutions below

0
On

You can add a negative margin-right of size .tag-close to .tags-container. But wrap .tags-container in an additional element with overflow:hidden;, so that the displacement is not visible:

.container {
  max-width: 500px;
  margin: 0 auto;
  display: flex; 
}

/* wrapper */
.tags{
  overflow: hidden;
}

.tags-container { 
  background-color: #a3b3c6; 
  padding: 20px;
  display: flex;
  flex-wrap: wrap;
}

.tags-container:hover{
    margin-right: -16px; /*  */
}

.tag {
  display: inline-flex;
  transition:background-color .2s; 
  padding: 3px;
  background-color: white;
  border: 1px solid black;
}

.tag-close {
  margin-left: 10px; 
  height: 16px;
  width: 16px;
  display:none;
}

.tag:hover {
  background-color: red; 
}

.tag:hover .tag-close {
  display: block;
}

.tags-remove-all {
  display: flex;
  align-items: center;
  padding: 0 10px;
  font-size: 32px;
}
<div class="container">
  <div class="tags">
    <div class="tags-container">
      <div class="tag">
        LBL-12334455579
        <div class="tag-close">x</div>
      </div>
      <div class="tag">
        LBL-12334578
        <div class="tag-close">x</div>
      </div>
      <div class="tag">
        LBL-1233
        <div class="tag-close">x</div>
      </div>
      <div class="tag">
        LBL-12334
        <div class="tag-close">x</div>
      </div>
      <div class="tag">
        LBL-12334
        <div class="tag-close">x</div>
      </div>
      <div class="tag">
        LBL-12334
        <div class="tag-close">x</div>
      </div>
    </div>
  </div>
  <div class="tags-remove-all">x</div>
</div>

0
On

You said that you can't change the current design, but are you referring only to the HTML? Here are a few examples that keep the original HTML and change only the CSS.

The first example demonstrates padding the button on the left and right (centering the text) to allow for the width of the remove icon. On hover, take away the extra padding and display the icon.

.tag {
  padding: 3px 16px;
}
.tag:hover {
  padding: 3px;
}

.container {
  max-width: 500px;
  margin: auto;
  display: flex; 
}
.tags-container { 
  background-color: #a3b3c6; 
  padding: 20px;
  display: flex;
  flex-wrap: wrap;
}
.tag {
  display: flex;
  /* transistion: all 1s ease-in-out; */
  transition: background .25s ease-in-out;
  padding: 3px 16px;
  background-color: white;
  border: 1px solid black;
}
.tag-close {
  margin-left: 10px; 
  height: 16px;
  width: 16px;
  display: none;
}
.tag:hover {
  padding: 3px;
  background-color: red; 
}
.tag:hover .tag-close {
  display: block;
}
.tags-remove-all {
  display: flex;
  align-items: center;
  padding: 0 10px;
  font-size: 32px;
}
<div class="container">
  <div class="tags-container">
    <div class="tag">
      LBL-12334455579
      <div class="tag-close">
        x
      </div>
    </div>
    <div class="tag">
      LBL-12334578
      <div class="tag-close">
        x
      </div>
    </div>
    <div class="tag">
      LBL-1233
      <div class="tag-close">
        x
      </div>
    </div>
    <div class="tag">
      LBL-12334
      <div class="tag-close">
        x
      </div>
    </div>
    <div class="tag">
      LBL-12334
      <div class="tag-close">
        x
      </div>
      </div>
    <div class="tag">
      LBL-12334
      <div class="tag-close">
        x
      </div>
    </div>
  </div>
  <div class="tags-remove-all">
    x
  </div>
</div>


The second example demonstrates hiding the remove icon until hover. This leaves extra space on the right side where the icon will appear but prevents the buttons from shifting around.

.tag-close {
  visibility: hidden;
}
.tag:hover .tag-close {
  visibility: visible;
}

.container {
  max-width: 500px;
  margin: auto;
  display: flex; 
}
.tags-container { 
  background-color: #a3b3c6; 
  padding: 20px;
  display: flex;
  flex-wrap: wrap;
}
.tag {
  display: flex;
  transition: background .25s ease-in-out;
  padding: 3px;
  background-color: white;
  border: 1px solid black;
}
.tag-close {
  margin-left: 10px; 
  height: 16px;
  width: 16px;
  /* display: none; */
  visibility: hidden;
}
.tag:hover {
  background-color: red; 
}
.tag:hover .tag-close {
  /* display: block; */
  visibility: visible;
}
.tags-remove-all {
  display: flex;
  align-items: center;
  padding: 0 10px;
  font-size: 32px;
}
<div class="container">
  <div class="tags-container">
    <div class="tag">
      LBL-12334455579
      <div class="tag-close">
        x
      </div>
    </div>
    <div class="tag">
      LBL-12334578
      <div class="tag-close">
        x
      </div>
    </div>
    <div class="tag">
      LBL-1233
      <div class="tag-close">
        x
      </div>
    </div>
    <div class="tag">
      LBL-12334
      <div class="tag-close">
        x
      </div>
    </div>
    <div class="tag">
      LBL-12334
      <div class="tag-close">
        x
      </div>
      </div>
    <div class="tag">
      LBL-12334
      <div class="tag-close">
        x
      </div>
    </div>
  </div>
  <div class="tags-remove-all">
    x
  </div>
</div>


Adding to the previous demo, this example demonstrates declaring flex-grow: 1 in the .tag rule

.tag {
  flex-grow: 1;
}

.container {
  max-width: 500px;
  margin: auto;
  display: flex; 
}
.tags-container { 
  background-color: #a3b3c6; 
  padding: 20px;
  display: flex;
  flex-wrap: wrap;
}
.tag {
  display: flex;
  flex-grow: 1;
  transition: background .25s ease-in-out;
  padding: 3px;
  background-color: white;
  border: 1px solid black;
}
.tag-close {
  margin-left: 10px; 
  height: 16px;
  width: 16px;
  visibility: hidden;
}
.tag:hover {
  background-color: red; 
}
.tag:hover .tag-close {
  visibility: visible;
}
.tags-remove-all {
  display: flex;
  align-items: center;
  padding: 0 10px;
  font-size: 32px;
}
<div class="container">
  <div class="tags-container">
    <div class="tag">
      LBL-12334455579
      <div class="tag-close">
        x
      </div>
    </div>
    <div class="tag">
      LBL-12334578
      <div class="tag-close">
        x
      </div>
    </div>
    <div class="tag">
      LBL-1233
      <div class="tag-close">
        x
      </div>
    </div>
    <div class="tag">
      LBL-12334
      <div class="tag-close">
        x
      </div>
    </div>
    <div class="tag">
      LBL-12334
      <div class="tag-close">
        x
      </div>
      </div>
    <div class="tag">
      LBL-12334
      <div class="tag-close">
        x
      </div>
    </div>
  </div>
  <div class="tags-remove-all">
    x
  </div>
</div>


Figured I'd include a Grid example as well

.tags-container { 
  display: grid;
  grid-template-columns: repeat(3, auto);
}

.container {
  max-width: 500px;
  margin: auto;
  display: flex; 
}
.tags-container { 
  background-color: #a3b3c6; 
  padding: 20px;
  display: grid;
  grid-template-columns: repeat(3, auto);
}
.tag {
  display: flex;
  transition: background .25s ease-in-out;
  padding: 3px;
  background-color: white;
  border: 1px solid black;
}
.tag-close {
  margin-left: 10px; 
  height: 16px;
  width: 16px;
  visibility: hidden;
}
.tag:hover {
  background-color: red; 
}
.tag:hover .tag-close {
  visibility: visible;
}
.tags-remove-all {
  display: flex;
  align-items: center;
  padding: 0 10px;
  font-size: 32px;
}
<div class="container">
  <div class="tags-container">
    <div class="tag">
      LBL-12334455579
      <div class="tag-close">
        x
      </div>
    </div>
    <div class="tag">
      LBL-12334578
      <div class="tag-close">
        x
      </div>
    </div>
    <div class="tag">
      LBL-1233
      <div class="tag-close">
        x
      </div>
    </div>
    <div class="tag">
      LBL-12334
      <div class="tag-close">
        x
      </div>
    </div>
    <div class="tag">
      LBL-12334
      <div class="tag-close">
        x
      </div>
      </div>
    <div class="tag">
      LBL-12334
      <div class="tag-close">
        x
      </div>
    </div>
  </div>
  <div class="tags-remove-all">
    x
  </div>
</div>

0
On

To resolve this issue and ensure that the tags don't jump to the bottom on hover, you can use the visibility property instead of display to hide and show the close button. The visibility property hides the element but still takes up the original space, preventing the jumping effect.

.container {
  max-width: 500px;
  margin: 0 auto;
  display: flex;
}

.tags-container {
  background-color: #a3b3c6;
  padding: 20px;
  display: flex;
  flex-wrap: wrap;
}

.tag {
  display: inline-flex;
  transition: all 1s ease-in-out;
  padding: 3px;
  background-color: white;
  border: 1px solid black;
  position: relative; /* Add this to allow absolute positioning */
}

.tag-close {
  position: absolute;
  top: 0; /* Position the close button at the top-right corner of the tag */
  right: 0;
  height: 16px;
  width: 16px;
  visibility: hidden; /* Hide the close button by default */
}

.tag:hover {
  background-color: red;
}

/* Change the selector to apply to the .tag element instead of .tag:hover */
.tag:hover .tag-close {
  visibility: visible; /* Show the close button when hovering over the tag */
}

.tags-remove-all {
  display: flex;
  align-items: center;
  padding: 0 10px;
  font-size: 32px;
}
<div class="container">
<div class="tags-container">
  <div class="tag">
    LBL-12334455579
    <div class="tag-close">
      x
    </div>
    </div>
  <div class="tag">
    LBL-12334578
    <div class="tag-close">
      x
    </div>
    </div>
  <div class="tag">
    LBL-1233
    <div class="tag-close">
      x
    </div>
    </div>
  <div class="tag">
    LBL-12334
    <div class="tag-close">
      x
    </div>
    </div>
  <div class="tag">
    LBL-12334
    <div class="tag-close">
      x
    </div>
    </div>
  <div class="tag">
    LBL-12334
    <div class="tag-close">
      x
    </div>
    </div>
</div>
  <div class="tags-remove-all">
    x
  </div>
</div>

0
On

Here is what I have done I have added a small padding to the right of each tag to ignore the flickering.

Also to show container close button I have set opacity of close button to 0 so that it wont flicker when we hover over last tags in each row (I assume that what you want from your last response).

Finally showing or hidding close button upon hovering last tag is impossible to do with css alone so I have included little script too. Thanks.

document.addEventListener("DOMContentLoaded", function() {
  updateRightmostTags();
  showCloseButton();
});

window.addEventListener('resize', function() {
  updateRightmostTags();
});

function updateRightmostTags() {
  var tags = Array.from(document.querySelectorAll('.tags-container .tag'));
  tags.forEach(tag => tag.classList.remove('rightmost'));

  var currentTop = tags[0].getBoundingClientRect().top;
  tags.forEach((tag, i) => {
    if (tag.getBoundingClientRect().top !== currentTop) {
      tags[i - 1].classList.add('rightmost');
      currentTop = tag.getBoundingClientRect().top;
    }
  });

  tags[tags.length - 1].classList.add('rightmost');
}

function showCloseButton() {
  var tagElements = document.querySelectorAll('.tag');
  var closeButton = document.querySelector(".tags-remove-all");

  tagElements.forEach(tag => {
    tag.addEventListener('mouseenter', function(event) {
      if (event.target.classList.contains('rightmost')) {
        closeButton.style.opacity = "1";
      }
    });

    tag.addEventListener('mouseleave', function(event) {
      if (event.target.classList.contains('rightmost')) {
        closeButton.style.opacity = "0";
      }
    });
  });
}
.container {
  max-width: 600px;
  margin: 0 auto;
  display: flex;
}

.tags-container {
  max-width: 500px;
  background-color: #a3b3c6;
  padding: 20px;
  display: flex;
  flex-wrap: wrap;
}

.tag {
  display: inline-flex;
  position: relative;
  transition: all 1s ease-in-out;
  padding: 3px;
  padding-right: 20px;
  background-color: white;
  border: 1px solid black;
}

.tag-close {
  position: absolute;
  right: 3px;
  top: 50%;
  transform: translateY(-50%);
  margin-left: 10px;
  height: 16px;
  width: 16px;
  visibility: hidden;
}

.tag:hover .tag-close {
  visibility: visible;
}

.tags-remove-all {
  display: flex;
  align-items: center;
  padding: 0 10px;
  font-size: 32px;
  opacity: 0;
}

.tags-container:after {
  content: '';
  width: 26px;
  display: inline-block;
  opacity: 0;
}

.tags-container:hover:after {
  opacity: 1;
}

.tags-container {
  background-color: #a3b3c6;
  padding: 20px;
  padding-right: 50px;
  display: flex;
  flex-wrap: wrap;
  flex-grow: 1;
}
<div class="container">
  <div class="tags-container">
    <div class="tag">
      LBL-12334455579
      <div class="tag-close">
        x
      </div>
    </div>
    <div class="tag">
      LBL-12334578
      <div class="tag-close">
        x
      </div>
    </div>
    <div class="tag">
      LBL-12334578
      <div class="tag-close">
        x
      </div>
    </div>
    <div class="tag">
      LBL-12334578
      <div class="tag-close">
        x
      </div>
    </div>
    <div class="tag">
      LBL-12334578
      <div class="tag-close">
        x
      </div>
    </div>
    <div class="tag">
      LBL-1233
      <div class="tag-close">
        x
      </div>
    </div>
    <div class="tag">
      LBL-12334
      <div class="tag-close">
        x
      </div>
    </div>
    <div class="tag">
      LBL-12334
      <div class="tag-close">
        x
      </div>
    </div>
    <div class="tag">
      LBL-12334
      <div class="tag-close">
        x
      </div>
    </div>
  </div>
  <div class="tags-remove-all">
    x
  </div>
</div>

1
On

I think that having x displayed on all elements by default would be the most expected based on other software practices.

But that being said, the top answer on this question may offer you exactly what you're looking for Expanding div on hover in a flex row wrap container causes shifting of children elements

1
On

This seems like a stylistic choice, in your case I would simply make the tags longer to accommodate the cross even without hovering

If you really want it to only appear on hover I would place the cross over the text in the tag on hover, this wouldn't make the tag longer as it's just placed over the text.

Very simple demo:

.container {
  max-width: 500px;
    margin: 0 auto;
  display: flex; 
}
.tags-container { 
  background-color: #a3b3c6; 
  padding: 20px;
  display:flex;
  flex-wrap: wrap;
}
.tag {
  display: inline-flex;
  transistion: all 1s ease-in-out; 
  padding: 3px;
  background-color: white;
  border: 1px solid black;
}
.tag-close {
  height: 16px;
  width: 16px;
  background-color: white;
  position: absolute;
  float: right;
  display:none;
}
.tag:hover {
  background-color: red; 
}
.tag:hover .tag-close {
  display: block;
}

.tags-remove-all {
      display: flex;
    align-items: center;
    padding: 0 10px;
    font-size: 32px;
}
<div class="container">
<div class="tags-container">
  <div class="tag">
    LBL-12334455579
    <div class="tag-close">
      x
    </div>
    </div>
  <div class="tag">
    LBL-12334578
    <div class="tag-close">
      x
    </div>
    </div>
  <div class="tag">
    LBL-1233
    <div class="tag-close">
      x
    </div>
    </div>
  <div class="tag">
    LBL-12334
    <div class="tag-close">
      x
    </div>
    </div>
  <div class="tag">
    LBL-12334
    <div class="tag-close">
      x
    </div>
    </div>
  <div class="tag">
    LBL-12334
    <div class="tag-close">
      x
    </div>
    </div>
</div>
  <div class="tags-remove-all">
    x
  </div>
</div>