CSS教程

其他伪类与伪元素

CSS其他伪类与伪元素:否定、匹配等高级选择器详解

1 否定伪类 :not() 详解

1.1 基本语法与参数

否定伪类 :not() 是CSS3中引入的一个重要功能,它允许开发者选择不匹配特定选择器的元素。这种选择器被称为反选伪类(negation pseudo-class),可以显著优化CSS代码的编写方式,减少对重置样式的需求。

:not() 伪函数接受一个或多个选择器作为参数,然后匹配那些不匹配这些参数的元素。其基本语法如下:

:not(selector) {
  /* 样式声明 */
}

需要注意的是,传递给 :not() 的参数可以是标签选择器、类选择器、ID选择器、属性选择器或其他伪类选择器,但不能是伪元素选择器或另一个否定伪类选择器。例如,以下用法是无效的:

/* 无效的 :not() 用法 */
:not(::before) { /* 伪元素不允许 */ }
:not(:not(.same)) { /* 不允许嵌套 :not() */ }

1.2 实际应用示例

以下是一个完整的HTML页面示例,展示了 :not() 伪类的实际应用:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>:not() 伪类示例</title>
    <style>
        /* 选择所有不是<p>的元素 */
        body :not(p) {
            margin-left: 20px;
        }

        /* 选择既不包含.special类也不是<div>的元素 */
        body :not(.special):not(div) {
            font-style: italic;
        }

        /* 选择所有没有disabled属性的输入元素 */
        input:not([disabled]) {
            background-color: #f0f8ff;
            border: 2px solid #4CAF50;
        }

        /* 选择非第一个子元素的列表项 */
        li:not(:first-child) {
            border-top: 1px solid #ddd;
            padding-top: 10px;
        }
    </style>
</head>
<body>
    <h1>页面标题</h1>
    <p>这是一个普通段落。</p>
    <p class="special">这是一个特殊段落。</p>
    <div>这是一个div元素。</div>
    <ul>
        <li>第一个列表项</li>
        <li>第二个列表项</li>
        <li>第三个列表项</li>
    </ul>
    <input type="text" placeholder="可输入文本框">
    <input type="text" disabled placeholder="禁用文本框">
</body>
</html>

在这个示例中,我们使用了多种 :not() 表达式:

  • body :not(p) 选择body中所有不是段落元素的元素
  • body :not(.special):not(div) 选择既不包含special类也不是div的元素
  • input:not([disabled]) 选择所有未禁用的输入框
  • li:not(:first-child) 选择除第一个列表项外的所有列表项

通过合理使用 :not() 伪类,我们可以编写出更加简洁、易于维护的CSS代码,避免了频繁使用重置样式覆盖原有样式的情况。

2 任意匹配伪类 :is():where()

2.1 功能与语法对比

:is():where() 是两个功能相似但有着关键差异的CSS伪类,它们都用于将一组选择器作为一个整体进行匹配,从而简化复杂选择器的编写。这两个伪类都可以接受一个选择器列表作为参数,并匹配该列表中任意一个选择器可以选择的元素。

它们的基本语法如下:

/* :is() 语法 */
:is(selector1, selector2, selector3) {
  /* 样式声明 */
}

/* :where() 语法 */
:where(selector1, selector2, selector3) {
  /* 样式声明 */
}

虽然功能相似,但两者在优先级上有显著区别。:is() 伪类会采用其参数中优先级最高的选择器的优先级,而 :where() 伪类的优先级始终为0。这一差异使得 :where() 在需要低优先级覆盖样式时特别有用。

2.2 优先级差异与容错机制

为了更好地理解 :is():where() 的优先级差异,请参考以下对比表格:

特性:is():where()
优先级采用参数中最高优先级始终为0
容错性
浏览器支持现代浏览器广泛支持现代浏览器广泛支持
适用场景需要正常优先级的情况需要低优先级便于覆盖的情况

以下是一个完整的示例,展示了两者的区别:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>:is() 和 :where() 示例</title>
    <style>
        /* 使用 :is() - 优先级相当于 header p */
        :is(header, section, footer) p {
            color: blue;
            font-size: 16px;
        }

        /* 使用 :where() - 优先级为0,相当于 p */
        :where(header, section, footer) .special {
            color: red;
            font-style: italic;
        }

        /* 对比组:这个规则会覆盖上面的 :where() 规则 */
        .special {
            color: green !important;
            font-weight: bold;
        }

        /* 容错机制示例 - 即使 :unsupported 是无效选择器,整体仍有效 */
        :is(.valid, :unsupported) {
            background-color: #f0f0f0;
            padding: 10px;
        }

        /* 传统写法等效对比 */
        header p, section p, footer p {
            border-left: 3px solid blue;
            padding-left: 10px;
        }
    </style>
</head>
<body>
    <header>
        <p>头部段落(蓝色)</p>
        <p class="special">头部特殊段落(绿色粗体,:where()被覆盖)</p>
    </header>
    <section>
        <p>内容区段落(蓝色)</p>
        <div class="valid">有效元素(灰色背景)</div>
    </section>
    <footer>
        <p>底部段落(蓝色)</p>
    </footer>
</body>
</html>

在这个示例中,我们可以观察到以下几个关键点:

  1. :is() 的优先级行为:is(header, section, footer) p 的优先级与 header p 相同,因此会正常应用蓝色文本样式。
  2. :where() 的低优先级:尽管 :where(header, section, footer) .special 包含了类选择器,但由于 :where() 的优先级为0,该规则被简单的 .special 选择器覆盖。
  3. 容错机制:在 :is(.valid, :unsupported) 中,即使 :unsupported 是一个无效的选择器,整个规则也不会失效,仍然会应用到有效的 .valid 元素上。
  4. 代码简化效果:使用 :is() 可以将多个逗号分隔的选择器组合成一个简洁的表达,提高代码可读性。

值得注意的是,:is():where() 都支持可容错选择器解析,这意味着当其中一个选择器无效时,不会使整个规则失效,其他有效的选择器仍然会被应用。这一特性使它们在处理浏览器兼容性或未知选择器时更加稳健。

3 关联伪类 :has() 的强大应用

3.1 语法与浏览器支持

:has() 伪类是CSS选择器Level 4规范中引入的一个革命性功能,它允许开发者根据元素的后代元素来选择该元素,实现了类似”父选择器”和”前面兄弟选择器”的效果。这一功能对CSS的开发有着颠覆性的影响,因为它首次使开发者能够根据子元素的状态来选择父元素。

:has() 伪类的基本语法如下:

parent:has(child) {
  /* 样式声明 */
}

element:has(+ sibling) {
  /* 样式声明 */
}

尽管 :has() 伪类规范制定得很早,但浏览器的支持却相对较慢。不过,目前大多数现代浏览器的最新版本已经支持了这一功能。在实际使用前,建议检查目标浏览器的支持情况。

3.2 实际应用场景

以下是展示 :has() 伪类多种应用场景的完整示例:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>:has() 伪类示例</title>
    <style>
        /* 选择包含<svg>元素的<a>元素 */
        a:has(> svg) {
            background-color: #f8f9fa;
            padding: 8px 12px;
            border-radius: 4px;
            display: inline-flex;
            align-items: center;
        }

        /* 选择后面跟随<p>元素的<h1>元素 */
        h1:has(+ p) {
            border-bottom: 2px solid #4CAF50;
            padding-bottom: 10px;
            margin-bottom: 5px;
        }

        /* 选择包含.selected类的列表项的无序列表 */
        ul:has(li.selected) {
            border: 2px solid #ff9800;
            background-color: #fff3e0;
        }

        /* 选择包含空白的输入字段的表单组 */
        .form-group:has(input:placeholder-shown) {
            opacity: 0.7;
        }

        /* 选择包含至少一个图片的article元素 */
        article:has(img) {
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }

        /* 选择包含聚焦输入框的表单 */
        form:has(input:focus) {
            background-color: #f0f8ff;
        }

        /* 卡片布局增强 */
        .card:has(.featured) {
            transform: scale(1.05);
            z-index: 1;
        }
    </style>
</head>
<body>
    <h1>带段落的标题</h1>
    <p>这个标题有下边框,因为它后面跟着段落。</p>

    <h1>独立标题</h1>

    <a href="#">
        普通链接
    </a>
    <a href="#">
        带图标的链接
        <svg width="16" height="16" viewBox="0 0 16 16" style="margin-left:5px;">
            <circle cx="8" cy="8" r="7" fill="none" stroke="currentColor" stroke-width="2"/>
        </svg>
    </a>

    <ul>
        <li>普通列表项</li>
        <li class="selected">选中的列表项</li>
        <li>另一个普通列表项</li>
    </ul>

    <div class="form-group">
        <input type="text" placeholder="请输入内容">
    </div>
    <div class="form-group">
        <input type="text" value="已填写内容">
    </div>

    <article>
        <h2>带图片的文章</h2>
        <img src="https://via.placeholder.com/150" alt="示例图片">
        <p>这篇文章有阴影效果,因为它包含图片。</p>
    </article>

    <article>
        <h2>无图片的文章</h2>
        <p>这篇文章没有阴影效果。</p>
    </article>

    <form>
        <div class="form-group">
            <label>姓名:</label>
            <input type="text" placeholder="点击我会使表单变背景色">
        </div>
    </form>

    <div class="card">
        <h3>普通卡片</h3>
        <p>这是一个普通卡片。</p>
    </div>
    <div class="card">
        <h3>特色卡片</h3>
        <p class="featured">这是一个特色卡片,它会放大。</p>
    </div>
</body>
</html>

在这个示例中,我们展示了 :has() 伪类的多种强大应用:

  1. 父选择器效果:通过 a:has(> svg) 选择包含SVG图标的所有链接元素,并为其添加特殊样式。
  2. 前面兄弟选择器效果:使用 h1:has(+ p) 选择后面紧跟着段落的所有一级标题。
  3. 条件性容器样式:通过 ul:has(li.selected)article:has(img) 选择包含特定子元素的容器。
  4. 表单状态管理:利用 :has(input:placeholder-shown):has(input:focus) 根据表单字段的状态为整个表单或表单组添加样式。
  5. 交互式组件增强:使用 .card:has(.featured) 为包含特定元素的卡片添加突出显示效果。

需要注意的是,虽然 :has() 伪类功能强大,但由于其性能特性(浏览器需要检查多个元素的关系),在大型文档中应谨慎使用,避免复杂的 :has() 表达式影响页面渲染性能。

4 伪元素的扩展应用

4.1 常见伪元素详解

伪元素是CSS提供的强大功能,它们允许开发者选择和样式化元素的特定部分,而无需添加额外的HTML元素。与伪类选择元素的状态不同,伪元素选择的是元素的某个特定部分。

最常见的伪元素包括:

  • ::before::after:在元素内容的前面或后面插入生成的内容
  • ::first-letter:选择块级元素的第一行的第一个字母
  • ::first-line:选择块级元素的第一行
  • ::selection:选择用户选中或高亮的部分
  • ::marker:选择列表项的标记框(如无序列表的点或有序列表的数字)

4.2 内容生成与文本装饰

以下是一个展示多种伪元素应用的完整示例:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>伪元素应用示例</title>
    <style>
        /* ::before 和 ::after 示例 */
        blockquote::before {
            content: "“";
            font-size: 3em;
            color: #4CAF50;
            line-height: 0.1em;
            vertical-align: -0.4em;
        }

        blockquote::after {
            content: "”";
            font-size: 3em;
            color: #4CAF50;
            line-height: 0.1em;
            vertical-align: -0.4em;
        }

        .feature::before {
            content: "✓";
            color: #4CAF50;
            font-weight: bold;
            margin-right: 8px;
        }

        /* ::first-letter 示例 */
        p::first-letter {
            font-size: 2em;
            color: #d32f2f;
            font-weight: bold;
            float: left;
            line-height: 1;
            margin-right: 5px;
        }

        /* ::first-line 示例 */
        p::first-line {
            font-weight: bold;
            color: #1976d2;
            text-transform: uppercase;
        }

        /* ::selection 示例 */
        ::selection {
            background-color: #ffeb3b;
            color: #000;
        }

        /* ::marker 示例 */
        li::marker {
            color: #d32f2f;
            font-weight: bold;
        }

        /* 自定义列表样式 */
        .custom-list li::marker {
            content: "➤ ";
            color: #4CAF50;
        }

        /* 工具提示效果 */
        .tooltip {
            position: relative;
            border-bottom: 1px dotted #000;
            cursor: help;
        }

        .tooltip::after {
            content: attr(data-tooltip);
            position: absolute;
            bottom: 125%;
            left: 50%;
            transform: translateX(-50%);
            background-color: #333;
            color: #fff;
            padding: 5px 10px;
            border-radius: 4px;
            white-space: nowrap;
            opacity: 0;
            transition: opacity 0.3s;
            pointer-events: none;
            font-size: 14px;
            font-weight: normal;
            text-transform: none;
        }

        .tooltip:hover::after {
            opacity: 1;
        }
    </style>
</head>
<body>
    <h1>伪元素演示</h1>

    <blockquote>
        这是一个使用::before和::after添加引号的块引用。注意开头和结尾的大型引号。
    </blockquote>

    <div class="feature">特色功能一</div>
    <div class="feature">特色功能二</div>
    <div class="feature">特色功能三</div>

    <p>这个段落演示了::first-letter和::first-line伪元素的效果。注意首字母的下沉和变色,以及第一行的大写和蓝色。段落的其余部分保持正常样式。伪元素使我们能够创建视觉上吸引人的排版效果,而无需修改HTML结构。</p>

    <p>尝试选中一些文本,可以看到::selection伪元素的效果,它改变了文本选中时的背景和文字颜色。</p>

    <ul>
        <li>默认列表项</li>
        <li>另一个列表项</li>
        <li>注意列表标记的颜色</li>
    </ul>

    <ul class="custom-list">
        <li>自定义标记列表</li>
        <li>使用自定义箭头作为标记</li>
    </ul>

    <div class="tooltip" data-tooltip="这是一个提示信息">
        鼠标悬停在这里查看工具提示
    </div>
</body>
</html>

在这个示例中,我们展示了多种伪元素的应用场景:

  1. 内容生成:使用 ::before::afterblockquote 元素添加装饰性引号,为特色功能列表添加勾选标记。
  2. 文本装饰:通过 ::first-letter 创建首字母下沉效果,使用 ::first-line 为段落第一行添加特殊样式。
  3. 交互反馈:利用 ::selection 改变文本选中样式,增强用户体验。
  4. 列表定制:通过 ::marker 自定义列表标记的样式,创建独特的列表外观。
  5. 工具提示:结合 ::aftercontent: attr() 函数,实现纯CSS工具提示效果。

需要注意的是,使用 ::before::after 伪元素时必须指定 content 属性,即使将其设置为空字符串(content: "")。此外,虽然伪元素在选择器中的优先级与普通伪类相同,但它们可以实现仅通过修改HTML结构难以达到的效果。

5 高级选择器综合应用与性能优化

5.1 综合应用实例

在实际项目中,高级选择器往往不是单独使用的,而是结合多种选择器创建精确且强大的样式规则。以下是一个综合应用多种高级选择器的完整示例:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>高级选择器综合应用</title>
    <style>
        /* 1. 使用 :is() 简化选择器组 */
        :is(header, main, footer) :is(h1, h2, h3) {
            font-family: 'Arial', sans-serif;
            margin-bottom: 0.5em;
        }

        /* 2. 使用 :where() 添加低优先级样式 */
        :where(article, section) p {
            line-height: 1.6;
            margin-bottom: 1em;
        }

        /* 3. 使用 :not() 排除特定元素 */
        article :not(.exclude) {
            opacity: 0.95;
        }

        /* 4. 使用 :has() 创建条件布局 */
        .grid:has(.featured) {
            grid-template-columns: 2fr 1fr 1fr;
            gap: 15px;
        }

        /* 5. 结合伪元素增强设计 */
        article:has(h2) > p:first-of-type::first-line {
            font-variant: small-caps;
            letter-spacing: 0.5px;
        }

        /* 6. 表单状态的高级选择 */
        form:has(input:invalid) {
            border-left: 3px solid #f44336;
        }

        form:has(input:valid) {
            border-left: 3px solid #4CAF50;
        }

        /* 7. 使用属性选择器增强交互 */
        a[href^="https"]:not([target="_blank"])::after {
            content: " 🔒";
            font-size: 0.8em;
        }

        /* 8. 卡片系统的高级选择器 */
        .card:not(:has(img)) {
            background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
        }

        .card:has(.urgent) {
            border: 2px solid #ff5252;
            animation: pulse 2s infinite;
        }

        /* 动画定义 */
        @keyframes pulse {
            0% { box-shadow: 0 0 0 0 rgba(255, 82, 82, 0.7); }
            70% { box-shadow: 0 0 0 10px rgba(255, 82, 82, 0); }
            100% { box-shadow: 0 0 0 0 rgba(255, 82, 82, 0); }
        }

        /* 布局样式 */
        .grid {
            display: grid;
            grid-template-columns: 1fr 1fr 1fr;
            gap: 20px;
            margin: 20px 0;
        }

        .card {
            background: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }

        form {
            padding: 20px;
            background: #f9f9f9;
            margin: 20px 0;
            transition: all 0.3s ease;
        }

        input {
            display: block;
            width: 100%;
            padding: 8px;
            margin: 10px 0;
            border: 1px solid #ddd;
            border-radius: 4px;
        }

        /* 内容样式 */
        .exclude {
            background-color: #fff3e0;
            padding: 10px;
            border-radius: 4px;
        }

        .urgent {
            color: #f44336;
            font-weight: bold;
        }
    </style>
</head>
<body>
    <header>
        <h1>网站标题</h1>
        <h2>子标题</h2>
    </header>

    <main>
        <article>
            <h2>带特色内容的主要文章</h2>
            <p>这篇文章的第一行会使用小型大写字母显示,这是通过组合:has()和::first-line伪元素实现的。其余内容保持正常样式。</p>
            <p>普通段落,没有特殊样式。</p>
            <div class="exclude">这个元素被排除在不透明度调整之外。</div>
        </article>

        <section>
            <h3>卡片网格系统</h3>
            <div class="grid">
                <div class="card">
                    <h4>普通卡片</h4>
                    <p>没有图片的卡片,具有渐变背景。</p>
                </div>
                <div class="card">
                    <h4>带图片的卡片</h4>
                    <img src="https://via.placeholder.com/150" alt="示例">
                    <p>这个卡片包含图片,没有渐变背景。</p>
                </div>
                <div class="card">
                    <h4>紧急卡片</h4>
                    <p class="urgent">这个卡片有紧急内容,带有脉冲动画边框。</p>
                </div>
            </div>
        </section>

        <section>
            <h3>表单验证状态</h3>
            <form>
                <label>必填字段:</label>
                <input type="text" required placeholder="请输入内容">

                <label>电子邮件:</label>
                <input type="email" required placeholder="输入有效邮箱">
            </form>
        </section>

        <section>
            <h3>链接安全指示器</h3>
            <p>
                <a href="https://example.com">安全链接</a> | 
                <a href="http://insecure.example.com">不安全链接</a>
            </p>
        </section>
    </main>

    <footer>
        <h3>页脚信息</h3>
        <p>版权所有 &copy; 2023</p>
    </footer>
</body>
</html>

5.2 优先级计算与性能优化

优先级计算规则

当使用高级选择器时,理解CSS优先级计算规则至关重要。以下是优先级计算的关键点:

  1. :is():has() 的优先级:采用参数中选择器的最高优先级
  2. :where() 的优先级:始终为0,不影响优先级计算
  3. :not() 的优先级:由括号内的选择器决定

以下表格展示了不同选择器的优先级权重:

选择器示例优先级计算说明
#header .link100 + 10 = 110ID + 类
:is(#header, .nav) .link100 + 10 = 110取最高优先级参数
:where(#header, .nav) .link0 + 10 = 10:where()优先级为0
a:not(.external)1 + 10 = 11标签 + 类

性能优化建议

虽然高级选择器功能强大,但不当使用可能影响页面性能。以下是一些优化建议:

  1. 避免过度复杂的选择器:特别是包含大量回溯或复杂参数的选择器
  2. 优先使用类选择器:对于频繁使用的样式,类选择器通常性能更好
  3. 合理使用 :has():由于 :has() 需要浏览器进行更多计算,应谨慎使用
  4. 利用浏览器的优化:现代浏览器已对常用选择器进行了优化,但仍需注意选择器复杂度
/* 不推荐 - 过于复杂的选择器 */
body > div:first-of-type:has(nav > ul > li:first-child > a[href^="#"]) ~ main article:not(.draft) p:first-of-type::first-letter {
    font-size: 3em;
}

/* 推荐 - 简化选择器,使用类替代 */
.article-lead::first-letter {
    font-size: 3em;
}

通过合理组合使用高级选择器,并遵循性能优化最佳实践,开发者可以创建既强大又高效的CSS规则,提升用户体验和代码维护性。

总结

CSS高级伪类与伪元素大大扩展了CSS选择器的能力,使开发者能够更精确地选择和样式化文档中的元素。以下是本教程的主要知识点总结:

知识点详细内容
否定伪类 :not()选择不匹配指定选择器的元素,可接受简单选择器作为参数,但不能嵌套使用
任意匹配伪类 :is()将选择器列表作为参数,选择匹配列表中任一选择器的元素,优先级采用参数中最高优先级
任意匹配伪类 :where()功能与:is()相同,但优先级始终为0,适合创建易于覆盖的样式
关联伪类 :has()根据元素的后代元素来选择该元素,实现父选择器和前面兄弟选择器效果
伪元素 ::before/::after在元素内容前/后插入生成内容,必须指定content属性
伪元素 `::first-letter选择块级元素第一行的第一个字母,常用于首字母下沉效果
伪元素 `::first-line选择块级元素的第一行,可应用有限的样式属性
伪元素 `::selection选择用户选中或高亮的部分,可定义背景色和文字颜色
伪元素 `::marker选择列表项的标记框,可自定义列表标记样式
优先级计算:is():has()采用参数中最高优先级,:where()优先级为0,:not()优先级由括号内选择器决定
性能优化避免过度复杂的选择器,优先使用类选择器,谨慎使用:has()

通过掌握这些高级选择器,开发者可以编写出更简洁、更灵活且更易维护的CSS代码,创建出视觉丰富、交互性强的现代网页设计。