CSS伪类

简介

CSS 伪类 (Pseudo-classes)是W3C里制定的一套选择器的特殊状态,有几十个之多。语法如下:

selector:pseudo-class {property: value;}

我们比较常见的有:link:focus:active等等。今天我来介绍几个比较有趣的伪类选择器,并在某些场景里实现一些酷炫的功能。

:first-child

前段时间在开发一个dashboard的页面,客户提了一个需求,当dashboard页面的todo为空时,显示no task图标。需求很简单,有todo时隐藏图标,没todo显示图标。

no task icon

我看了一下组里小朋友的实现,大体思路如下。很娴熟地使用了v-if条件渲染。

<section v-if="todos.length">
    <h1>TODO</h1>
    <ol>
        <li v-for="todo in todos"> {{todo}}</li>
    </ol>
</section>
<section v-else>
    <v-icon>inbox</v-icon>
    <p>No Task</p>
</section>

不过后来dashboard里需要增加与todo同级的news了,然后代码就变成了这样:

<section v-if="todos.length+news.length">
    <div v-if="todos.length">
        <h1>TODO</h1>
        ...
    </div>
    <div v-if="news.length">
        <h1>NEWS</h1>
        ...
    </div>   
</section>
<section v-else>
   ...
</section>

先不论代码可读性,<section v-if="...">判断语句会随着新条目的增加不断修改,从开放闭合原则的角度来说,这并不是很适宜。

其实,这个案例里用一个简单的CSS伪类:first-child就可以很好的满足需求。如下,为图标所在section添加一个no-task类,并在样式表里添加:first-child选择器。

<section v-if="todos.length">
    ...
</section>
<!-- 增加新的条目且不需要修改原先的代码 -->
<section v-if="news.length">
    ...
</section>
<section class="no-task">
   <v-icon>inbox</v-icon>
   <p>No Task</p>
</section>

顾名思义,:first-child用于选取属于其父元素的首个子元素的指定选择器。通俗来说,first-child指的是该元素本事为长子时的状态。

样式表内容如下。当no-task类为首元素时,显示图标,其他形态下隐藏自身。

.no-task {
  display: none;
}

.no-task:first-child  {
  display: block;
}

除此之外,还有这么几个类似功用的伪类,大家有兴趣都可以去看一看:

  • :last-child
  • :first-of-type
  • :last-of-type
  • :nth-child
  • :nth-last-child

CSS Counters

我发现很多同事学过CSS,但是知道CSS也能计数的并不多。只要回想一下<ol>标签能产生从1~n的数值时,其实也不会觉得太奇怪了。

CSS计数器的值通过使用counter-resetcounter-increment 操作,在 content 上应用 counter()counters()函数来显示在页面上。

再以上面todo为例加一个计数器。

<section class="to-dos">
    <h1>TODO</h1>
    <ul>
      <li class="to-do" v-for="item in todos"> {{item}}</li>
    </ul>
    <!-- counter of todos -->
    <div class="counter"><div> 
  </section>

使用CSS计数器之前,必须重置一个值(如:todo),默认是0。接着,每渲染一个 <li class='to-do'>元素,todo计数+1。最后在.counter:aftercontent里显示计数结果。(注意:content只出现在html伪元素::before或是::after里)

section.to-dos {
  counter-reset: todo;  /* Set a counter named 'todo'*/
}

li.to-do {
  counter-increment: todo; /* Increment 'todo' counter by 1 */
}

.counter:after {
  content: counter(todo); /* Display the value of counter */
}

再稍微加工一下就可以实现如下功能了。请注意右上角的数字,这是利用CSS计数器动态生成的数值。

todo counter

:checked and :not(checked)

我在浏览器default actions这一期里提到过用radio做互斥按钮。

radio button

这里就用到了input radio的:checked伪类。那unchecked呢?没有这个伪类,但是可以用否定伪类:not(checked)

回忆一下上述互斥按钮的代码:

<li v-for="item in items">
    <span>{{item}}</span>
    <label>
        <input type="radio" name="radio" :value="item"/>
        <div class="btn btn-primary selected">selected</div>
        <div class="btn btn-light unselected">unselected</div>
    </label>
</li>

列表项<li>里的<input type="radio" name="radio">共享同一个name,形成互斥效果。点击<label>后,内嵌的radio被置为:checked,其他radio自动变为:not(checked)。我们可以通过这个状态差异来让:checked.selected兄弟可见,.unselected兄弟隐藏;反之,:not(checked).unselected兄弟可见,.selected兄弟隐藏。代码实现如下。是的,没有用到一点Javascript。

input[type="radio"]:checked ~ .selected {
  display: block;
}

input[type="radio"]:checked ~ .unselected {
  display: none;
}

input[type="radio"]:not(checked) ~ .unselected {
  display: block;
}

input[type="radio"]:not(checked) ~ .selected {
  display: none;
}

checked, unchecked, and indeterminate.

突然想到了一个很冷的知识:radio和checkbox一共有三种状态 checked、 unchecked和indeterminate。没错,还有一个中间态。checkbox三态如下所示。我记得早些年的QQ mail就利用过indeterminate制作过一个特殊效果。

three status
  • checkbox的indeterminate只能通过JS来设置

    let checkbox = document.getElementById("checkbox")
    checkbox.indeterminate = true;
    
  • radio的话,当所有同名radio都未被选择时,它们呈现的状态叫indeterminate

总结

CSS伪类是一个很有趣知识点,很多人觉得这个伪类太过奇技淫巧,甚至有哗众取宠之嫌。其实CSS伪类,还有html的伪元素有许多实用的功能,熟练掌握可以极大地简化html、减少JS的绑定。你的页面也将更加简洁优雅。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,657评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,889评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,057评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,509评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,562评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,443评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,251评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,129评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,561评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,779评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,902评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,621评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,220评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,838评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,971评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,025评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,843评论 2 354

推荐阅读更多精彩内容