再聊CSS的属性选择器

发布于 大漠

属性选择器是很强大很神奇的。很多时候可以帮助你摆脱一些棘手的问题,可以让你避免类名的添加,甚至还可以指出代码中的一些问题。属性选择器是复杂而又强大的,但不用担心,因为它也是易于学习和使用的。在本文中,将会学习到CSS属性选择器的强大功能以及如何在实际工作中运用它。并提供了一些关于如何使用属性选择器的一些个人想法。

我一直以来都觉得CSS属性选择器很强大,也特别的有魔性。记得第一次接触CSS属性选择器的时候,是在国外的某博客上,鼠标悬浮到链接上,可以立马的知道是外部链接还是本地链接,另外在下载资源的链接上可以立马看到下载的资源格式。这一切让我觉得好神奇,好强大。当然更令我感到惊奇的是,打印Web页面的时候,对应的链接文本后面居然能打印出其对应的链接地址。这些引起我强大好奇心,所以我决定去一探究竟,才知道这一切的魔力都来源于CSS的选择器:CSS属性选择器

对于初学者,我想你此时或许也感到好奇。只要你有这样的好奇心就好办。通过接下来的内容学习之后,你除了能很快掌握前面提到的特性之外,你甚至还可以使用CSS属性选择器在你的项目中做一些Debug的功能,它们能很快的帮助你定位到问题的根源,帮助你修复其他无法解决的问题。而且这种体验非常的爽,就像是变魔术一样。你可能会认为你又再装逼了,其实你只要理解了CSS属性选择器的强大功能之后,你可能会觉得自己太夸张了。

CSS属性选择器的使用非常的简单,你只要把HTML的属性(包括自定义属性)放在一个中括号[]中即可,比如:

[href] {
    color: green;
}

任何具有href属性而且没有其他选择器指定的样式覆盖,那么文本的颜色会变成green

注意CSS选择器权重相关的知识,就权重而言,CSS属性选择器和类选择器权重是一样的。如果你对CSS选择器权重相关的知识有点模糊的话,建议你花点时间阅读《你应该知道的一些事情:CSS权重》一文。

上面的示例,只是CSS属性最基本的用法,其实它可以做更多的事情。就像人体的DNA信息一样,它们有内在的逻辑(比如说简单的正则规则),可以帮助你选择各种属性组合和值。可以匹配属性中的任何属性,甚至是字符串值,而不仅仅是精确的匹配标签、类或id选择器。

在深入探讨CSS属性选择器其他一些高级用法之前,先来简单的了解一点点基础知道,这些基础知识有助于你更好,更快的掌握CSS属性选择器。

CSS属性选择器发展历程

CSS属性选择器最早出现在CSS2之中。随着W3C规范模块化划分的改革,时至今日也就没有CSS3,CSS4之说了。我们也跟着规范来说。在CSS Selectors Level 3CSS Selectors Level 4两个版本中都有CSS属性选择器的身影。从两个版本的规范中中介绍的对比来看,Level4从Level3的四个部分增加到了五个部分:

  • 属性和属性值选择器,CSS2最早引入的四个属性选择器:[attr][attr = val][attr ~= val][attr |= val]
  • 匹配属性选择器的子字符串,CSS3新引入的三个属性选择器:[attr ^= val][attr $= val][attr *= val]
  • 属性选择器和命名空间:属性选择器中属性名是CSS限定名称,前面声明的名称空间前缀可以放在名称空间分隔线分隔的属性名前面,比如[*|attr] { color: yellow }(表示只与属性attr匹配的元素,而不考虑属性的名称空间)
  • DTD中的默认属性值:属性选择器表示文档树中的属性值。如何构造文档树超出了选择器的范围。在某些文档格式中,默认属性值可以在DTD中或其他地方定义,但这些值嗡有在文档树中出现时由属性选择器选择。 (这么多年以来,几乎未曾用过)
  • 忽略大小写敏感:比如 [attr = "val" i],这是在Level4中新增的一条。默认情况下,选择器中的属性名和值有可能有大小写敏感之分(需要取决于文档语言),为了不受文档语言规则影响能匹配到属性值,属性选择器可以在右括号]前带上一个i(或I)标识符。当出现此标志时,UAs必须在ASCII范围内不敏感地匹配属性的值大小写。

特别记住了,现在W3C规范中已经没有CSS版本号一说(比如CSS2,CSS3,CSS4之类的),现在都是按功能模块化进行划分,然后给功能模块添加Level x来进行区分,比如Level 3、Level 4之类的。所以,今后文章看到CSS4一说的,基本上都是在忽悠你的。切记!切记!切记!(^_^)

正则表达式

在上面的内容中,我们看到了在属性选择器中有[attr ^= val][attr $= val][attr *= val]。其中^$*之类的符号,是不是特别的像JavaScript中正则表达式中使用到的一些符号。其实这些关键字符,和JavaScript中的正则表达式中的符号类似。这些符号在CSS属性选择器中也有着其特殊的含义:

  • ^:表示字符串开始位置匹配
  • $:表示字符串结束位置匹配
  • *:表示字符串任意位置匹配
  • i:表示字符串匹配不区分大小写敏感

属性选择器如何使用?

属性选择器可以独立存在,也可以更具体,比如下面的选择器可以匹配到所有带title属性的div

div[title]

也可以像下面这样匹配到div中带title属性的后代子元素:

div [title]

注意这两者之间的差异,如果它们之间没有空格,意味着属性在同一个元素上,如果有空格,则意味着是一个后代选择器,父元素是一个div,后代元素是带title属性的任何元素。表示选择具有该属性的所有后代元素。

在使用属性选择器的时候,也可以设置属性具体的值:

div[title="w3cplus"]

该选择器可以让你匹配到带有title属性,而且其值必须是w3cplus的所有div。记住,其值必须是w3cplus,如果title的值是W3cplusW3CPLUS之类的,那就无法匹配上,因为它们有大小写敏感:

当然,如果你要处理对值大小敏感还是有办法的,稍后我们会介绍。请继续往下阅读。

有的时候你的title不会只是一个单词,有可能变成了I like w3cpuls或者I am from w3cplus,这个时候你还想通过w3cplus值来进行匹配,那么你需要在等=前添加一个波浪号~,即可[attr ~= val]的形式,比如:

div[title~="w3cplus"]

从上面的结果可以看出,[attr ~= "val"]属性选择器,只要是attr属性的值包含val词(记住,是单词,不是字符串)那就可以匹配上,否则不无法正确匹配,比如上面的示例中I like w3cplus !I lick w3cplus!。前者是w3cplus是一个词,而后者不是(它和标点符号在一起),所以前者被匹配上了,后者则未被匹配上。

使用[attr = "val"]属性选择器时,其中val是单词,所以其只对ASCII范围的字符串有用。

虽然有一个精确的匹配对于用来匹配到正确的元素有是很大帮助的,但它的选择条件有可能是苛刻的,比如你只想匹配到以val单词开头的或val-开头的。这个时候可以采用[attr |= "val"]的属性选择器,在属性和值的=号前添加一个管道符号|=,那么就可以精确的匹配以valval-开头的单词。

div[title|="cook"]

上面的示例它可以让你只匹配所有diov带有title属性,并且是以cookcook-单词开头的:

或许你会说精确匹配有点蛋疼,那么在CSS属性选择器中能不能进行模糊匹配呢?可以的。这也是为什么说CSS属性选择器强大的原因之一。

比如说,只要是title属性的值以plus字符串结束的div就能匹配上。那么这个时候,就可以使用:

div[title$="plus"]

既然可以从后面进行匹配,那么就可以从前面进行匹配了。是的,我们可以使用[attr ^= "val"]的方式来实现。比如说,只要是以w3c开头的字符串,都能匹配上:

div[title^="w3c"]

甚至你还可以通过[attr *= "val"]进行更模糊的匹配。只要是attr的值中包含了val字符串就能匹配上。比如:

div[title*="w3c"]

前面我们看到的示例,对于val都有大小写区分,在CSS 选择器Level 4版本中,提供了一个参数i,可以帮助我们忽略字符串的大小写之分:

div[title*="w3c" i]

上面我们看到了CSS属性选择器的一些基本用法,简单的总结一下:

  • [attr]: 是基本的属性选择器使用方法,只要元素包含attr属性就可以被匹配上
  • [attr = "val"]:元素的属性名是attr,属性值必须是val
  • [attr ~= "val"]:元素的属性名是attr,并且attr值中需要含有val单词 (记住是单词,而不是字符串
  • [attr |= "val"]:元素的属性名是attr,并且attr值开头必须是以val单词或val-。(val单词后紧跟一个空格val 都不会被匹配上)
  • [attr ^= "val"]:元素的属性名是attr,属性值以val字符串开头 (可以和[attr |= "val"]对比一下下)
  • [attr $= "val"]:元素的属性名是attr,属性值以val字符串结束
  • [attr *= "val"]:元素的属性名是attr,属性值只要含有val字符串都会被匹配上(val字符串没有位置限制,可以在attr值的任意位置)
  • [attr operator value i]:该属性选择器适合上面所有选择器,忽略val大小写之分

其中[attr = "val"][attr ~= "val"][attr |= "val"]可以归纳为精准匹配,而[attr ^= "val"][attr $= "val"][attr *= "val"]可以归纳为模糊匹配。

大家是否还记得在选择器中有一个组合选择器,比如类选择器的组合:

.calssA.classB{}

那么在CSS属性选择器中也是有类似的,比如:

div[title][class="a]{}
.a[title][data-icon]{}

现在我们对CSS属性选择器的使用应该有了足够多的了解了,接下来看一些示例。下面的示例将分成两部分,一般用途和辅助Debugg。

一般用途

在这一部分中涉及到的一些案例,都是可以在项目开发中使用的一些技巧,通过CSS属性选择器,如何更轻易的向你的用户展示您的产品。

美化input

input美化并不是一件复杂的事情,其是表单中最为常见的一种。大家都知道都知道inputtype类型众多,如果你想更清晰的告诉你的用户,对应的type输入对应的信息时,这个时候就可以借用CSS的属性选择器来帮助你搞定。

正如上图所示,如果需要对emailtel不同的文本颜色,那么就可以通过type属性来完成:

input[type="email"] {
    color: #f36;
}
input[type="tel"] {
    color: #f63;
}

当然,要实现上图的效果,还需要利用一下其他的选择器,比如:focus:not()以及:placeholder-shown等。对于:placholder-shown估计了解的同学并不多,如果你感兴趣的话,可以阅读一下下面几篇文章:

显示电话链接

有的时候,你的Web页是一个响应多的页面,在页面中有一个联系电话显示,只不过,你只是期望这个电话号码只在移动端上显示。那么你可以这样来处理:

span.phone {
    display: none;
}
a[href^="tel"] {
    display: block;
}

链接处理

你可以区别对待内部链接和外部链接,并区别对待安全链接和不安全链接:

a[href^="http"]{
    color: bisque;
}
a:not([href^="http"]) {
    color: darksalmon;
}

a[href^="http://"]:after {
    content: url(unlock-icon.svg);
}
a[href^="https://"]:after {
    content: url(lock-icon.svg);
}

文章开头,我们提到Web打印的时候,外部链接会在对应的链接后面打印出链接地址,这个需求对于CSS属性选择器来说,太简单不过了:

@media print {
    [href^="http://"]:after,
    [href^="https://"]:after {
        content: "(" attr(href) ")";
    }
}

这里涉及到了Web页面打印样式的设计媒体查询相关的知识点。

下载和上传图标

HTML给我们提供了一个download属性(@张鑫旭大湿有一篇文章介绍了这个属性:《了解HTML/HTML5中的download属性》)。这个属性可以告诉浏览器下载那个文件,而不是试图打开它。这对.pdf.doc文档特别有用,你只是想让人们访问,而并不是立马下载。不过download属性的默认视觉区分,它看上去更像一个链接。通常这样的效果并不是你想要的,那么我们可以借用前面学到的知识来美化一下:

a[download]:after { 
    content: url(download-arrow.svg);
}

对于不同文件类型,采用不同的图标:

a[href$="pdf"]:after {
    content: url(pdf-icon.svg);
}
a[href$="docx"]:after {
    content: url(docx-icon.svg);
}
a[href$="odf"]:after {
    content: url(open-office-icon.svg);
}

你还可以通过组合选择器来确保这些图标只在可下载的链接上显示:

a[download][href$="pdf"]:after {
    content: url(pdf-icon.svg);
}

对应download的操作,我们还有一个上传文件的操作。即inputtypefile的时候。同样的,文件上传可接受的文件类型也是不可见的,我们可以使用同样的方法来告诉你的用户:

<input type="file" accept="pdf,doc,docx">

[accept]:after {
    content: "可上传的文件格式: " attr(accept);
}

手风琴折叠效果

手风琴(Accordion)效果在Web中常常可见,最早通过JavaScript来完成,接着我们使用CSS的:target选择器也能实现,随着技术不断的革新,现在我们直接使用HTML5的detailssummary属性就可以实现。当点击summary标签时,detail标记就会展开,并且会添加一个open属性。这样一来,手风琴的展开和折叠的两种状态样式就可以轻易的使用open属性来帮我们实现:

details[open] {
    background-color: hotpink;
}

Tooltips组件

如上图这样简易的Tooltips组件,使用纯CSS就可以快速的完成:

[title] {
    position: relative;
    display: block;
}
[title]:hover:after {
    content: attr(title);
    color: hotpink;
    background-color: slateblue;
    display: block;
    padding: .225em .35em;
    position: absolute;
    right: -5px;
    bottom: -5px;
}

甚至你还可以通过组合属性来控制其三角形的方向,比如:

[title][data-direction="top"]:hover:after {
    top: -5px;
    left: 5px;
}

布局

布局用CSS属性选择器,估计有不少同学会感到困惑,其实我在不少的组件库中发现有很多人使用CSS属性选择器来做布局,特别是用来绘制网格布局,比如:

截图只示为了向大家展示一下CSS属性选择器的另一使用场景,图中的代码并没有对浏览器前缀做优化,不值得参考,但CSS属性选择器在布局上的运用还是值得我们参考一二。

诊断

接下来的示例,在线上的项目基本上是不能使用的,要是用了,搞不好就给你产出一个事故了。这些示例只是用来阐述CSS属性选择器另一种用法,而且这些用法可以帮助你在开发的过程中或者尝试修复bug的时候能更好的确定问题。这也就是文章开头提到的开发体验方面的运用。

没有controlsaudio

或许audio并不是大家常用的一个标签元素,但当使用它的时候,有很多同学都会忘记其controls属性。最终的结果是:什么都没有显示出来。如果你在Firefox中调试页面,那么下面这段代码可以帮助你第一时间确认是否隐藏了audio元素:

audio:not([controls]) {
    width: 100px;
    height: 20px;
    background-color: chartreuse;
    display: block;
}

没带altimg

如果你平时开发过程中喜欢使用Validate HTML来验证你的HTML是否符合W3C的规范的话:

如果你使用过这样的工具,你会发现,如果<img>标签中没有带alt属性是通不过验证的。甚至很多时候,你有的时候是加上了,有的时候没加上,如果不借用验证工具来排查,你要定位到未添加alt属性的img还是要花不少时间的,这个时候你的代码中只需要包含下面这段代码,你就很容易找出未加alt属性的img标签:

img:not([alt]) { /* 未加alt属性 */ 
    outline: 2em solid chartreuse; 
}
img[alt=""] { /* alt属性添加了,但未赋值 */ 
    outline: 2em solid cadetblue; 
}

异步加载JavaScript文件

平时在开发Web页面的时候,可能会加载很多JavaScript文件,如果你想弄清楚哪些是异步加载的,哪些不是异步加载的,那么可以通过下面的代码来帮助确定:

script[src]:not([async]) {
    display: block;
    width: 100%;
    height: 1em;
    background-color: red;
}
script:after {
    content: attr(src);
}

隐藏元素

如果你需要查看隐藏元素或者隐藏的input的位置,可以使用以下方式让其显示:

[hidden], [type="hidden"] {
    display: block;
}

总结

当你看到这里的时候,或许你已经认可了文章开头提到的:CSS属性选择器很强大,而且很神奇!是CSS选择器中先进的技术之一。文章中举的到一些示例只是日常工作中常见的一小部分而以。如果你有其他相关方面的使用经验,欢迎在下面的评论中与我们一起分享。

参考阅读