代CSS强大的令人难以置信。
这次我们来用 CSS 实现这样一个功能:有多个宽度不同的标签水平排列,当外层宽度不足时,会提示超出的数量,演示效果如下
如果让我用 JavaScript来实现估计都有点折腾,毕竟宽度都是动态的,要监听各部分的尺寸变化,包括标签的位置和外层的宽度,总之不是一两行代码就能搞定的。
现如今,纯CSS也能完美的实现这样的效果,而且比 JS 实现更加简单,一起来看看吧
很多时候,CSS并没有直接的实现方式,需要“绕”点弯路,将需求拆分成很多小点,然后逐一突破。
回到本文这里,其实有几个问题需要考虑:
关于第3点,其实在之前这篇文章中有详细介绍,有兴趣可以回顾一下,非常有意思
纯 CSS 检测文本是否溢出
那么第一点,CSS有没有什么方式可以统计数量呢?
没错,相信很多小伙伴已经猜到了,就是利用 CSS计数器,示意如下
counter-reset: num var(--num);
counter-increment: num;
::after{
content: "+"counter(num);
}
关于计数器,我之前也在很多文章中有用到
你可能不需要 JS!CSS实现一个计时器
如何让CSS计数器支持小数的动态变化?
还在使用定时器吗?CSS 也能实现电子时钟
动画合成小技巧!CSS 实现动感的倒计时效果
自定义计数器小技巧!CSS 实现长按点赞累积动画
然后是第二点,CSS有什么方式可以知道元素是出去了还是在视野之内呢?
如果是用 JS来判断的话,最稳妥的方式应该Intersection Observer(交叉观察者),相信很多同学都用过,这个在图片懒加载非常有用
交叉观察器 API - Web API 接口参考 | MDN (mozilla.org)[1]
那么,CSS中有什么类似的呢?没错,还是之前提到过的CSS滚动驱动动画。
不过这里用到的是视图进度,也就是关注的是元素自身位置,元素进入到容器范围之内就会触发动画,非常类似 JS中的Intersection Observer
tag{
animation: appear;
animation-timeline: view(inline);
animation-range: contain;
}
@keyframes appear{
to {
background-color: #9747FF;
}
}
这里就不详细介绍了,有兴趣回顾之前这篇文章
CSS 滚动驱动动画终于正式支持了~
好了,关键原理就这些了,我们需要做的就是想办法将「计数器」和「元素进出容器范围」关联起来就行了,接着往下看
利用 CSS 计数器,我们可以很轻松的统计元素的数量。
先简单布局一下文章开头的例子,HTML如下所示
<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><!--用来计数的标签-->
然后美化一下
.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;
}
这样就得到了水平排列的 tag 布局(超出隐藏),后面跟一个数字
现在我们定义一个计数器,然后通过每个tag进行累计,有多个tag相当于多少次累加,最后在::after中显示出来
.con{
counter-reset: num; /*计数器初始值,默认为0*/
}
.tag{
counter-increment: num; /*计数器增量值,默认为1*/
}
.num::after{
content: "+"counter(num);
}
默认计数器的起始值是 0,每次累计是1,所以这里最后得到了7,这样就能实时统计元素的数量了
当然,我们也可以自定义这些默认值,比如
.con{
counter-reset: num 10;
}
.tag{
counter-increment: num -1;
}
这种情况下,起始值是10,增量值是-1,所以最后得到了3,如下
那么,该如何统计容器之外的标签数量呢?
这里我们要利用CSS 视图进度时间轴,也就是观察元素自身的位置,在进入到容器范围之内执行动画。
拿上面这个例子,我们给标签添加一个动画,让标签在进入到容器之内时变个颜色,实现如下
.tag{
animation: appear;
animation-timeline: view(inline);
}
@keyframes appear{
to {
background-color: #4d47ff;
}
}
由于是水平方向,所以这里需要设置view(inline) ,效果如下
好像不是很明显?这是因为动画是在整个视区内过渡的,我们可以改变动画时间线的区间animation-range,让这个动画在进出的一瞬间就变化
.tag{
animation: appear;
animation-timeline: view(inline);
animation-range: contain;
}
@keyframes appear{
from,to {
background-color: #4d47ff;
}
}
效果如下
那么,这个效果和前面的计数器有什么关联呢?
从效果上来看,在视区内会执行一个动画,如果在这个动画中加入计数器的累加会怎么样呢?先去除原先的累加器
.tag{
/* counter-increment: num; */
}
@keyframes appear{
from,to {
background-color: #4d47ff;
counter-increment: num;
}
}
效果如下
可以看到计数器已经生效了,不过现在统计的在「可视区内」标签的数量。而我们要统计的是「可视区外」的数量,好像反过来了,如何实现呢?
这里有两种思路。
第一种,用总数减去可视区内的数量,不就行了吗?由于现在动画是针对可视区内的,我们可以给计数器设置一个初始值,就是总量,然后动画中给累加值设置为-1,就相当于减去当前数量了,实现如下
.con{
counter-reset: num 7;
}
@keyframes appear{
from,to {
background-color: #4d47ff;
counter-increment: num -1;
}
}
这样就完美统计出了可视区外的数量了
还有一种方式,我觉得更加巧妙,需要反向思考。原理是重置当前的累加值,比如默认情况下,正常累加,进入可视范围,把累加值设置为0,不就相当于可视区外的正常累加了吗?
.tag{
counter-increment: num;
}
@keyframes appear{
from,to {
background-color: #4d47ff;
counter-increment: num 0;
}
}
同样能达到相同的效果
现在基本已经达到我们需要的效果了
首先是,在超出范围时,需要在边缘出添加一个半透明蒙层,这样体验效果会更好。
半透明蒙层很好实现,只需要添加一个水平渐变的mask遮罩就行了
.con{
-webkit-mask: linear-gradient(to right, #fff calc(100% - 30px), transparent);
}
效果如下
那么,如何在没有超出时,不出现这个遮罩呢?
这个其实在之前的文章中有详细介绍了,建议回顾一下
纯 CSS 检测文本是否溢出
利用CSS滚动驱动动画,可以检测容器是否可滚动,也就是是否超出。所以我们只需要将遮罩放在滚动驱动动画里就行了,关键实现如下
.con{
animation: check;
animation-timeline: scroll(x self);
}
@keyframes check{
from,to {
-webkit-mask: linear-gradient(to right, #fff calc(100% - 30px), transparent);
}
}
效果如下
还有一个问题,希望在没有超出的时候不显示后面的数量。
关于这个,我本来是打算用样式查询来实现,但是遇到了一个问题,由于样式查询只能匹配到子元素,所以不得不将显示数量的标签放到.con容器内。但是这样一来,mask遮罩就会出现问题,就像这样
遮罩层连同数量标签一起被裁剪了!
于是又想出了另一种方案,这个在以前的布局中其实用到的更多,那就是负margin。
实现很简单,给标签容器一个的负margin-right,这样,右边的数量就会被左边的标签盖住,比如
.con{
margin-right: -20px;
}
效果如下
是不是已经被盖住一部分了?我们继续向左偏移
.con{
margin-right: -50px;
}
这样就完全看不到右边的数量了
那么,如何在超出时显示数量呢?这里就体现出负margin的好处了,可以借助前一个元素来隐藏后一个元素,在这里,我们直接在前面的动画中还原margin就行了
.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;
}
}
这样就完美实现了文章开头所示效果了
你也可以访问以下链接来体验真实效果(Chrome115+)
以上就是本文的全部内容了,不知道你学会了吗?下面总结一下实现重点:
[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
来源-微信公众号:前端侦探
出处:https://mp.weixin.qq.com/s/_m5UxLQQtDU1GxbLRnCUhQ
增的结构标签
section元素
表示页面中的一个内容区块,比如章节、页眉、页脚或页面的其他部分。可以和h1、 h2...等元素结合起来使用,表示文档结构。
例:HTML5中<section>...</section>;HTML4中<div>...</div>。
article元素
表示页面中一块与上下文不相关的独立内容。比如一篇文章。
aside元素
表示article元素内容之外的、与article元素内容相关的辅助信息。
header元素
表示页面中一个内容区块或真个页面的标题。
hgroup元素
表示对真个页面或页面中的一个内容区块的标题进行组合。
footer元素
表示整个页面或页面中一个内容区块的脚注。一般来说,他会包含创作者的姓名、创作日期以及创作者的联系信息。
nav元素
表示页面中导航链接的部分。
figure元素
表示一段独立的流内容,一般表示文档主体流内容中的一个独立单元。使用figcaption元素为figure元素组添加标题。
figure 定义媒介内容的分组, 以及它们的标题。
figcaption 定义 figure 元素的标题。
例如:
<figure>
<figcaption>PRC</figcaption>
<p>The People's Republic of China was born in 1949</p>
</figure>
HTML4中常写作
<dl>
<h1>prc</h1>
<p>The People's Republic of China was born in 1949</p>
</dl>
新增的其他元素
video元素
定义视频。像电影片段或其他视频流。例:
<video src="movie.ogg" controls="controls">video元素</video>
HTML4中写法:
<object type="video/ogg" data="move.ogv">
<param name ="src" value="movie.ogv">
</object>
audio元素
定义音频。如音乐或其他音频流。例:
<audio src ="someaudio.wav">audio元素</audio>
html4中写法:
<object tyle="application/ogg" data="someaudio.wav">
<param name ="src" value= "someaudio.wav">
</object>
embed元素
用来嵌入内容(包括各种媒体)。格式可以是Midi、Wav、AIFF、AU、MP3,flash等。例:<embed src="flash.swf" />
HTML4中代码示例<object data="flash.swf" type="application/x-shockwave-flash"><object>
mark元素
主要用来在视觉上向用户呈现哪些需要突出显示或高亮显示的文字。典型应用搜索结果中高亮显示搜素关键字。
HTML5<mark></mark>;HTML4 <span></span>。
progress元素
表示运行中的进程,可以使用progress元素显示JavaScript中耗时时间函数的进程。等待中……、请稍后等。<progress></progress>。
time元素
表示日期或时间,也可以两者同时。
ruby元素
定义 ruby 注释(中文注音或字符)。
与 <ruby> 以及 <rt> 标签一同使用。ruby 元素由一个或多个字符(需要一个解释/发音)和一个提供该信息的 rt 元素组成,
还包括可选的 rp 元素,定义当浏览器不支持 "ruby" 元素时显示的内容。
<ruby>
汉朝<rt><rp>(</rp>西汉和东汉<rp>)</rp></rt>
</ruby>
rt元素
定义字符(中文注音或字符)的解释或发音。
rp元素
在 ruby 注释中使用, 以定义不支持 ruby 元素的浏览器所显示的内容。
wbr元素
表示软换行。与br元素的区别:br元素表示此处必须换行;wbr表示浏览器窗口或父级元素足弓宽时(没必要换行时),
不换行,而宽度不够时主动在此处换行。
canvas元素
定义图形,比如图表和其他图像。<canvas> 元素只是图形容器(画布),必须使用脚本来绘制图形。
<canvas id="myCanvas"></canvas>
<script type="text/javascript">
var canvas=document.getElementById('myCanvas');
var ctx=canvas.getContext('2d');
ctx.fillStyle='#FF0000';
ctx.fillRect(0,0,80,100);
</script>
command元素 各版本浏览器支持有问题
表示命令按钮,比如单选按钮、复选框或按钮。
只有当 command 元素位于 menu 元素内时,该元素才是可见的。否则不会显示这个元素,但是可以用它规定键盘快捷键。。
<menu>
<command onclick="alert('Hello World')">
Click Me!</command>
</menu>
details标签
用于描述文档或文档某个部分的细节 。
可与 summary 标签配合使用,summary可以为 details 定义标题。
标题是可见的,用户点击标题时,会显示出 details。summary应该是details的第一个子元素。
<details>
<summary>迪丽热巴</summary>
<p>迪丽热巴(Dilraba),1992年6月3日出生于新疆乌鲁木齐市,中国内地影视女演员。</p>
</details>
fieldset标签
组合表单中的相关元素
fieldset 标签用于从逻辑上将表单中的元素组合起来。
legend 标签为 fieldset 元素定义标题。
<form>
<fieldset>
<legend>健康信息</legend>
身高:<input type="text" />
体重:<input type="text" />
</fieldset>
</form>
datalist标签
定义选项列表。请与 input 元素配合使用该元素,来定义 input 可能的值。
datalist 及其选项不会被显示出来,它仅仅是合法的输入值列表。使用 input 元素的 list 属性来绑定 datalist。
<input id="myCar" list="cars" />
<datalist id="cars">
<option value="BMW">
<option value="Ford">
<option value="Volvo">
</datalist>
datagrid标签 如何用?
定义可选数据的列表。datagrid 作为树列表来显示。
如果把 multiple 属性设置为 true,则可以在列表中选取一个以上的项目。
keygen标签 如何用?
标签规定用于表单的密钥对生成器字段。
当提交表单时,私钥存储在本地,公钥发送到服务器。
<form action="demo_keygen.asp" method="get">
Username: <input type="text" name="usr_name" />
Encryption: <keygen name="security" />
<input type="submit" />
</form>
output标签
定义不同类型的输出,比如脚本的输出。
<form action="form_action.asp" method="get" name="sumform">
<output name="sum"></output>
</form>
source标签
标签为媒介元素(比如 <video> 和 <audio>)定义媒介资源。
menu标签
定义菜单列表。当希望列出表单控件时使用该标签。注意与nav的区别,menu专门用于表单控件。
<menu>
<li><input type="checkbox" />Red</li>
<li><input type="checkbox" />blue</li>
</menu>
abbr: 标记一个缩写
The <abbr title="People's Republic of China">PRC</abbr> was founded in 1949.
显示结果
The PRC was founded in 1949.
mark标签
定义有记号的文本。
<mark>有记号的文本</mark>
progress 定义任何类型的任务的进度。
<progress min="0" max="100" value="60"></progress>
Flask 的 Jinja2 模板引擎中,使用 block 标签可以在基模板(父模板)中声明替换的区域,子模板则可以继承父模板并重写这些 block 区域中的内容。向 Flask 的父模板中添加或追加内容,你可以使用 super() 函数来引用父模板中的原始内容。
假设你有一个父模板 base.html,其中定义了两个 block:
<!-- base.html -->
<html>
<head>
...
{% block head %}
<!-- 父模板的 head 内容 -->
{% endblock %}
</head>
<body>
{% block content %}
<!-- 父模板的主体内容 -->
{% endblock %}
</body>
</html>
如果你想在子模板中不仅替换这些 block,还想保留并在其前面或后面追加内容,你可以在子模板中这样写:
<!-- child.html -->
{% extends "base.html" %}
{% block head %}
{{ super() }} <!-- 这里使用 super() 调用父模板的原始内容 -->
<!-- 子模板追加的头部内容 -->
<link rel="stylesheet" href="style.css">
{% endblock %}
{% block content %}
<!-- 子模板的主体内容 -->
<div>这里是子模板独有的内容。</div>
<!-- 还可以在后面追加 super() 来保留父模板的内容 -->
{{ super() }}
{% endblock %}
在这个例子中,child.html 继承了 base.html 并重写了 head 和 content 块。通过调用 {{ super() }},子模板 child.html 在新内容的前后分别保留了父模板 base.html 中相应块的内容。这允许你在现有内容上扩展,而不是替换掉整个块的内容。
总之,通过 block 和 super(),你可以很灵活地控制模板继承的具体行为,既可以重写整块内容,也可以在不修改父模板的情况下对其内容进行扩展。
*请认真填写需求信息,我们会在24小时内与您取得联系。