CSS 样式覆盖问题的一些总结

1,736 阅读5分钟

我正在参加「掘金·启航计划」

一、背景

这次的项目迭代中,遇到了一些样式覆盖的问题,就是基础组件内置的样式,被项目中一些作用范围比较大的选择器覆盖了,问题定位和解决方法导致不算难,就是最后的方案因为通用性,影响点等等因素改了好几次。这里主要是重新梳理一下CSS样式优先级的相关知识和在选择具体方案的一些思考。

二、CSS选择器优先级

CSS的样式优先级,准确说指的是选择器的优先级。

1. 什么是选择器优先级

先看看MDN上对于优先级(Specificity)的描述

Specificity

Specificity is the algorithm used by browsers to determine the CSS declaration that is the most relevant to an element, which in turn, determines the property value to apply to the element. The specificity algorithm calculates the weight of a CSS selector to determine which rule from competing CSS declarations gets applied to an element.

其中的 the most relevant to an element 非常重要,意思是最为相关/相关性最高。所以总体思想是,和元素相关性越高的选择器,优先级也越高,越能决定元素最终应用的样式。

三、优先级比较

1. 一般情况

  • 内联 > ID选择器 > 类选择器 > 标签选择器。

算法1:

从0开始,一个行内样式+1000,一个id选择器+100,一个属性选择器、class或者伪类+10,一个元素选择器,或者伪元素+1,通配符+0。

有些文章中采用上面的算法,然后依据最终累加出来的权重值来计算优先级,这个在实践经验中基本是没有问题的,但是没有找到具体的依据,有大佬知道的可以告知一下。

算法2: 在 《CSS REFACTORING》一书中有提到更加具体的算法

image.png

image.png

这里的特指度在英文原版中是 Specificity ,和常说的优先级是一个意思。本书中的观点都是从选择器识别元素的精确性来比较优先级的,所以翻译成特指度也挺贴切。

其他: 在MDN上也有给出一些和优先级相关的参考资料

您可以访问 "Specificity" in "Cascade and inheritance" 或者 specifishity.com 来了解更多关于优先级的详细信息。

2. 特殊情况

  • !important
    当在一个样式声明中使用一个 !important 规则时,此声明将覆盖任何其他声明。

3. 注意点

  • 内联样式总会覆盖外部样式表的任何样式。
  • 可以用 !important 覆盖内联样式
  • 都有 !important 的选择器,按照上述的规则计算选择器优先级
  • 优先级相同的选择器,在样式表中出现顺序更后的优先级更高(可以理解为后出现的把先出现的覆盖了),是样式表中选择器出现的顺序,不是class属性中类名出现的顺序
  • 内联样式 + !important 具有最高的优先级,这时候内联样式已经强大到不管外部样式怎么写都无法覆盖它了 。
  • 内联样式 + !important 依然有可能被影响。在选择器优先级中,内联样式 + !important无法被超越了,但是从属性生效的角度,依然有可能被影响。比如 max-width 和 width 是两个不同的属性,即便在行内样式中设置 width + !important,依然会被 max-width 控制。

四、如何解决样式覆盖问题

样式优先级是解决样式覆盖问题的关键,具体怎么解决也还有其他问题要考虑。

从一个具体的场景来看。

image.png

这里的问题很简单,就是项目中引用了一个input组件,然后项目中有一个选择器对input元素的border做了修改,input组件内部的样式优先级比外部的更低,所以被外部样式覆盖。

解法一: 新建一个选择器,在选择器中添加一个类选择器,将组件内部的input样式修改回来

// project中
.classA {
    // 加入input组件的类名,限制作用范围
    .componentClass{
        input{
            // 修改回组件内部的样式
            border: 'xxxxxx'
        }
    }
}

结果:不可行
原因:组件内部input在hover,hover,error等状态border的属性也不一致,也被外部样式覆盖了,这样只修改了默认状态的问题。

解法二: 提升组件内部的样式优先级

// 组件中
.componentClass {
    // 在原有组件样式外加一层组件class,提高内部样式的优先级
    .component__input {
        border: 'xxxxxx'
    }

    .component__input--hover {
        border: 'xxxxxx'
    }

    .component__input--error {
        border: 'xxxxxx'
    }

}

结果:不可行
原因:外部可能存在自定义的对组件样式的覆盖,如果提升组件内部样式的优先级,可能会导致外部自定义的样式覆盖失效。影响点比较不可控。

解法三: 修改外部样式的作用范围。 project中样式生效的所有input添加一个类名 project_input_class 。 project中样式表选择器中对input增加限制,只有具有类名 project_input_class 的input 元素才应用原有的样式。

// project中
.classA input.project_input_class {
    border: 'xxxxxx'
}

结果:可行

五、一些可能需要考虑的点

  1. 从优先级考虑,提高被覆盖样式的优先级,降低覆盖样式的优先级
  2. 从作用范围考虑,降低覆盖样式的作用范围
  3. 在提高基础组件的样式时,要考虑原有主动覆盖的样式可能会失效