laitimes

Pure CSS implements tags to automatically display excess quantities

author:Flash Gene
Modern CSS is incredibly powerful.

This time, let's use CSS to implement such a function: there are multiple tags with different widths arranged horizontally, and when the outer width is insufficient, the excess amount will be prompted, as shown below

Pure CSS implements tags to automatically display excess quantities

If I were to use JavaScript to estimate it, it would be a bit of a toss-up, after all, the width is dynamic, and I have to listen to the size changes of each part, including the position of the label and the width of the outer layer, in short, it is not a line or two of code can be done.

Nowadays, pure CSS can also achieve this effect perfectly, and it is easier than JS implementation, let's take a look

1. CSS implementation ideas

In many cases, CSS does not have a direct implementation, and it needs to "detour" the point, break the requirement into many small points, and then break through one by one.

Coming back to this article, there are actually a few questions to consider:

  1. How does CSS dynamically accumulate numbers?
  2. How does CSS know which elements are outside the container?
  3. How CSS distinguishes if it's overflowing (number of displays)

Regarding point 3, in fact, it was introduced in detail in the previous article, so if you are interested, you can review it, which is very interesting

Pure CSS detects if the text is overflowing

So the first point, is there any way for CSS to count the numbers?

That's right, I believe many friends have guessed it, that is, to use CSS counters, which are shown below

counter-reset: num var(--num);
counter-increment: num;
::after{
	content: "+"counter(num);
}
           

I've also used counters in many articles before

You probably don't need JS! CSS implements a timer

How do I get a CSS counter to support dynamic changes in decimals?

Still using a timer? CSS also implements electronic clocks

Animation compositing tips! CSS for dynamic countdowns

Custom counter tips! CSS implements long-press like accumulation animation

And then there's the second point, is there any way for CSS to know if an element is out or in view?

If you use JS to judge, the safest way should be Intersection Observer, I believe many students have used it, this is very useful in lazy loading of images

交叉观察器 API - Web API 接口参考 | MDN (mozilla.org)[1]

So, what's similar in CSS? That's right, it's the same CSS scroll-driven animation mentioned earlier.

However, what is used here is the view progression, that is, the focus is on the position of the element itself, and the animation will be triggered when the element enters the container range, much like the Intersection Observer in JS

tag{
  animation: appear;
  animation-timeline: view(inline);
  animation-range: contain;
}
@keyframes appear{
  to {
    background-color: #9747FF;
  }
}
           

I won't go into detail here, but I'm interested in reviewing the previous article

CSS scroll-driven animations are finally officially supported~

Okay, that's the key principle, all we need to do is figure out how to associate the "counter" with the "element in and out of the container range", and then read on

2. CSS counters

With CSS counters, we can easily count the number of elements.

Let's briefly lay out the example at the beginning of the article, as shown in the HTML below

<div class="con">
  <a class="tag">HTML</a>
  <a class="tag">CSS</a>
  <a class="tag">JavaScript</a>
  <a class="tag">Flutter</a>
  <a class="tag">Vue</a>
  <a class="tag">React</a>
  <a class="tag">Svelte</a>
</div>
<span class="num"></span><!--用来计数的标签-->
           

Then beautify it

.con{
  display: flex;
  gap: 5px;
  padding: 5px;
  overflow: hidden;
}
.tag{
  padding: .2em .5em;
  background-color: #c49ef5;
  color: #fff;
  border-radius: 4px;
  animation: appear;
}
.num::after{
  content: "+0";
  padding: .2em .5em;
  background-color: #FFE8A3;
  color: #191919;
  border-radius: 4px;
}
           

This results in a horizontally arranged tag layout (beyond hidden) followed by a number

Pure CSS implements tags to automatically display excess quantities

Now we define a counter and then accumulate it through each tag, there are multiple tags equivalent to how many times to add up, and finally show up in ::after

.con{
  counter-reset: num;  /*计数器初始值,默认为0*/
}
.tag{
  counter-increment: num; /*计数器增量值,默认为1*/
}
.num::after{
  content: "+"counter(num); 
}
           

The default counter starts with 0 and accumulates 1 each time, so we end up with 7, so you can count the number of elements in real time

Pure CSS implements tags to automatically display excess quantities

Of course, we can also customize these defaults, such as:

.con{
  counter-reset: num 10;
}
.tag{
  counter-increment: num -1;
}

           

In this case, the starting value is 10 and the increment value is -1, so we end up with 3, as follows

Pure CSS implements tags to automatically display excess quantities

So, how do you count the number of labels outside of a container?

3. CSS scroll-driven animations

Here we need to use the CSS view progress timeline, which is to observe the position of the element itself, and perform the animation within the scope of the container.

In the example above, let's animate the label so that it changes color when it enters the container, as follows

.tag{
  animation: appear;
  animation-timeline: view(inline);
}
@keyframes appear{
  to {
    background-color: #4d47ff;
  }
}
           

Since it is horizontal, you need to set view(inline) here, and the effect is as follows

Pure CSS implements tags to automatically display excess quantities

Doesn't seem obvious? This is because the animation transitions across the viewport, and we can change the animation-range of the animation timeline so that the animation changes the moment it enters and exits

.tag{
  animation: appear;
  animation-timeline: view(inline);
  animation-range: contain;
}
@keyframes appear{
  from,to {
    background-color: #4d47ff;
  }
}
           

The effect is as follows

Pure CSS implements tags to automatically display excess quantities

So, what does this effect have to do with the previous counter?

As an effect, an animation is executed in the viewport, what happens if you add a counter to the animation? Remove the original accumulator first

.tag{
  /* counter-increment: num; */
}
@keyframes appear{
  from,to {
    background-color: #4d47ff;
    counter-increment: num;
  }
}
           

The effect is as follows

Pure CSS implements tags to automatically display excess quantities

You can see that the counter is already active, but now counts the number of tabs in the "viewport". And what we want to count is the number of "out of view", it seems to be the other way around, how to achieve this?

There are two lines of thought here.

First, subtract the number of viewports from the total number, wouldn't it? Since the animation is now for the viewport, we can set an initial value for the counter, which is the total amount, and then set the cumulative value to -1 in the animation, which is equivalent to subtracting the current amount, as follows

.con{
	counter-reset: num 7;
}
@keyframes appear{
  from,to {
    background-color: #4d47ff;
    counter-increment: num -1;
  }
}
           

This gives a perfect count of the number of people outside the viewport

Pure CSS implements tags to automatically display excess quantities

There is another way, which I think is more subtle, and requires thinking backwards. The principle is to reset the current accumulation value, for example, by default, normal accumulation, enter the visual range, and set the accumulation value to 0, isn't it equivalent to the normal accumulation outside the viewport?

.tag{
  counter-increment: num;
}
@keyframes appear{
  from,to {
    background-color: #4d47ff;
    counter-increment: num 0;
  }
}
           

The same effect can be achieved

Pure CSS implements tags to automatically display excess quantities

Now we have basically achieved the effect we need

4. Other details are corrected

First of all, when the range is out, you need to add a translucent mask at the edge, so that the experience will be better.

Translucency masks are easy to implement, just add a horizontal gradient mask

.con{
  -webkit-mask: linear-gradient(to right, #fff calc(100% - 30px), transparent);
}
           

The effect is as follows

Pure CSS implements tags to automatically display excess quantities

So, how do you not have this mask when you don't go over?

This is actually described in detail in the previous article, so it is recommended to review it

Pure CSS detects if the text is overflowing

Using CSS scroll-driven animations, you can detect if the container is scrollable, i.e. exceeded. So we just need to put the mask in the scroll-driven animation, and the key implementation is as follows

.con{
  animation: check;
  animation-timeline: scroll(x self);
}
@keyframes check{
  from,to {
    -webkit-mask: linear-gradient(to right, #fff calc(100% - 30px), transparent);
  }
}
           

The effect is as follows

Pure CSS implements tags to automatically display excess quantities

There is also the issue of wanting not to show the following quantity when it is not exceeded.

Regarding this, I was going to use a style query to implement this, but I ran into a problem where I had to put the display number of tags inside the .con container because the style query could only match the child elements. But then there is a problem with the mask, like this

Pure CSS implements tags to automatically display excess quantities

The mask layer has been cropped along with the quantity labels!

So I came up with another solution, which was more practical in the previous layout, that is, negative margin.

The implementation is as simple as giving the label container a negative margin-right, so that the quantity on the right is covered by the label on the left, for example

.con{
  margin-right: -20px;
}
           

The effect is as follows

Pure CSS implements tags to automatically display excess quantities

Has it been partially covered? We continue to offset to the left

.con{
  margin-right: -50px;
}
           

This way you can't see the number on the right at all

Pure CSS implements tags to automatically display excess quantities

So, how do you show the quantity when it is exceeded? This is where the benefits of negative margin come in, you can use the previous element to hide the latter element, here, we can directly restore the margin in the previous animation

.con{
  animation: check;
  animation-timeline: scroll(x self);
}
@keyframes check{
  from,to {
    -webkit-mask: linear-gradient(to right, #fff calc(100% - 30px), transparent);
    margin-right: 0;
  }
}
           

This perfectly achieves the effect shown at the beginning of the article

Pure CSS implements tags to automatically display excess quantities

You can also visit the following link to experience the real effect (Chrome 115+)

  • CSS auto tag num (juejin.cn)[2]
  • CSS auto tag num (codepen.io)[3]

Fifth, to sum up

That's all for this article, I don't know if you've learned it? Here is a summary of the key points of implementation:

  1. In many cases, CSS doesn't have a direct implementation, and it needs to be "detoured" around the point
  2. CSS counters can be used to accumulate numbers
  3. CSS scroll-driven animations can detect if an element is within view
  4. CSS scroll-driven animations can also detect if an element is out of content
  5. The count accumulator can be placed in a scroll-driven animation, so that when the animation is triggered, the counter will take effect, so that the number of labels in the viewport can be counted
  6. Style queries can only match child elements, but using child elements will be affected by mask masks
  7. Negative margin can hide the latter element with the help of the previous element

[1]交叉观察器 API - Web API 接口参考 | MDN (mozilla.org): https://developer.mozilla.org/zh-CN/docs/Web/API/Intersection_Observer_API

[2] CSS auto tag num (juejin.cn): https://code.juejin.cn/pen/7352565668073439241

[3] CSS auto tag num (codepen.io): https://codepen.io/xboxyan/pen/rNbYpZV

作者:XboxYan

Source-WeChat public account: front-end detective

Source: https://mp.weixin.qq.com/s/_m5UxLQQtDU1GxbLRnCUhQ

Read on