“只需一分钟就能学会,却要用一辈子的时间去精通”,就像黑白棋一样,学会 CSS 的规则并不难。你只需要写一个选择器尝试匹配元素,然后写一些键/值对给元素添加样式即可。即使是新手也能轻松理解该基础语法。但要精通 CSS,难在需要知道在何时做何事。精通CSS,需要学习CSS的所有功能。了解得越多,对CSS的感受就越自然。练习得越多,就越能轻松地想到完美的布局和定位方法。读得越多,就越能从容地应对任何设计。原因在于,CSS没有任何硬性规定,但正因为全靠自己发挥,没有衡量标准告诉你做得好不好,所以你需要格外小心。一点改动就可能产生巨大的影响。一个样式表也许会像滚雪球一样增长到难以控制的程度。最后,你可能会被自己写的样式吓到!
CSS在1994年被提出,1996年由Internet Explorer 3首次(部分地)实现。每个经验丰富的Web开发者在见识到CSS的神奇效果后,大部分会问:“我该如何学习CSS? ”这个问题没有简单直接的答案,因为学习 CSS 并不是学习一两个小技巧,而是要理解这门语言的方方面面,并知道它们如何搭配使用。与此同时,CSS正在加速变化。响应式设计如今已经是事实标准了。Web字体也早已普及。2016年,我们见证了Flexbox的崛起;2017年,网格布局开始兴起,混合模式、盒阴影、变换、过渡、动画也刚刚普及。随着越来越多的浏览器变得“长青”,具备自动升级新版本的功能,新的CSS特性还会源源不断地出现。我们需要关注的知识点太多了。
在软件开发中,CSS是很特别的存在。严格来讲,它不是编程语言,却要求抽象思维。它不是纯粹的设计工具,却要求创造力。它提供了看似简单的声明式语法,但是在大型项目中写过CSS的人都知道它可能会变得极其复杂。在学习传统编程中遇到问题时,你通常知道该搜索什么(比如,“如何找到一个数组里类型为x的元素”)。在CSS中,却很难将问题提炼成一句话。即使可以,答案一般也是“这得看情况”。最好的解决办法通常取决于具体场景,以及你希望以多大力度处理各种边缘情况。
在讲解层叠之前,先让我们先一段代码,在html中创建一个div,代码如下:
<div id="menus" class="layui-menus"></div>
接下来,我们对这个div声明规则,代码如下:
/** 标签选择器 */div { font-size: 12px; color: #fff;}/** ID选择器 */#menus { font-size: 14px; color:#000; }/** 类选择器 */.layui-menus{ font-size: 16px; color:#fff;}
三个规则集尝试给菜单设置不同的字体和颜色,哪一个会生效呢?浏览器为了解决这个问题会遵循一系列规则,因此最终的效果可以预测。在上面的代码里,规则决定了第二个声明(即 ID选择器)生效。
层叠指的就是这一系列规则。它决定了如何解决冲突,是CSS语言的基础。虽然有经验的开发人员对层叠有大体的了解,但是层叠里有些规则还是容易让人误解。 下面来分析层叠的规则。当声明冲突时,层叠会依据三种条件解决冲突。 (1) 样式表的来源: 样式是从哪里来的,包括你的样式和浏览器默认样式等。 (2) 选择器优先级: 哪些选择器比另一些选择器更重要。 (3) 源码顺序: 样式在样式表里的声明顺序。
这些规则让浏览器以可预测的方式解决CSS样式规则的冲突。我们来一个一个地分析。
你添加到网页里的样式表并不是浏览器唯一使用的样式表,还有其他类型或来源的样式表。你的样式表属于作者样式表,除此之外还有用户代理样式表,即浏览器默认样式。用户代理样式表优先级低,你的样式会覆盖它们。用户代理样式表因为设置了用户普遍需要的样式,所以不会做一些完全超出预期的事情。当你不喜欢默认样式时,可以在自己的样式表里设置别的值。例如:ul li 标签前面的小黑点,链接为蓝色且有下划线等。
样式来源规则有一个例外:标记为重要(important)的声明。如下所示,在声明的后面、分号的前面加上!important,该声明就会被标记为重要的声明。
#menus { font-size: 14px; color:#000 !important; }
标记了!important的声明会被当作更高优先级的来源,因此总体的优先级按照由高到低排列如下所示: (1) 作者的!important (2) 作者 (3) 用户代理 层叠独立地解决了网页中每个元素的样式属性的冲突。例如,如果给段落设置加粗的字体,用户代理的上下外边距样式仍然会生效(除非被明确覆盖)。处理过渡和动画时,还会再提到样式来源的概念,因为它们会引入更多的来源。! important 注释是 CSS的一个有趣而怪异的特性,稍后会再解释。
如果无法用来源解决冲突声明,浏览器会尝试检查它们的优先级。理解优先级很重要。不理解样式的来源照样可以写CSS,因为99%的网站样式是来自同样的源。但是如果不理解优先级,就会被坑得很惨。浏览器将优先级分为两部分:HTML的行内样式和选择器的样式。
如果用HTML的style属性写样式,这个声明只会作用于当前元素。实际上行内元素属于“带作用域的”声明,它会覆盖任何来自样式表或者<style>标签的样式。行内样式没有选择器,因为它们直接作用于所在的元素。如下代码所示:
<div id="menus" class="layui-menus" style="font-size: 14px;color:#000;"></div>
为了在样式表里覆盖行内声明,需要为声明添加!important,这样能将它提升到一个更高优先级的来源。但如果行内样式也被标记为
!important,就无法覆盖它了。最好是只在样式表内用!important。将以上修改撤销,我们来看看更好的方式。
优先级的第二部分由选择器决定。比如,有两个类名的选择器比只有一个类名的选择器优先级更高。如果一个声明将字体颜色设置为白色,但另一个更高优先级的声明将其设置为黑色,浏览器就会将黑色应用到元素上。
/** 更高优先级的选择器 */#menus div{ color:#000; }.layui-menus{ color: #fff;}
不同类型的选择器有不同的优先级。比如,ID 选择器比类选择器优先级更高。实际上,ID选择器的优先级比拥有任意多个类的选择器都高。同理,类选择器的优先级比标签选择器(也称类型选择器)更高。 优先级的准确规则如下。 ❑ 如果选择器的ID数量更多,则它会胜出(即它更明确)。 ❑ 如果ID数量一致,那么拥有最多类的选择器胜出。 ❑ 如果以上两次比较都一致,那么拥有最多标签名的选择器胜出。
说明:伪类选择器(如:hover)和属性选择器(如[type="input"])与一个类选择器的优先级相同。通用选择器(*)和组合器(>、+、~)对优先级没有影响。
如果你在 CSS 里写了一个声明,但是没有生效,一般是因为被更高优先级的规则覆盖了。很多时候开发人员使用 ID选择器,却不知道它会创建更高的优先级,之后就很难覆盖它。如果要覆盖一个ID选择器的样式,就必须要用另一个ID选择器。这个概念很简单,但是如果你不理解优先级,就无法弄清楚为什么一个规则能生效,另一个却不能。
如果两个声明的来源和优先级相同,其中一个声明在样式表中出现较晚,或者位于页面较晚引入的样式表中,则该声明胜出。也就是说,可以通过控制源码顺序,来给特殊链接添加样式。如果两个冲突选择器的优先级相同,则出现得较晚的那个胜出。
div .layui-menus { color: #fff;}.layui-menus div { color: #000;}
在这个方法里,选择器优先级相同。源码顺序决定了哪个声明作用于特殊链接,最终的字体颜色为黑色。
浏览器遵循三个步骤,即来源、优先级、源码顺序,来解析网页上每个元素的每个属性。如果一个声明在层叠中“胜出”,它就被称作一个层叠值。元素的每个属性最多只有一个层叠值。网页上一个特定的段落(<p>)可以有一个上外边距和一个下外边距,但是不能有两个不同的上外边距或两个不同的下外边距。如果 CSS 为同一个属性指定了不同的值,层叠最终会选择一个值来渲染元素,这就是层叠值。
正如之前所说,在CSS中最好的答案通常是“这得看情况”。实现相同的效果有很多途径。多想些实现方法,并思考每一种方法的利弊,这是很有价值的。面对一个样式问题时,我经常分两个步骤来解决它。首先确定哪些声明可以实现效果。其次,思考可以用哪些选择器结构,然后选择最符合需求的那个。
为初学者提供学习指南,为从业者提供参考价值。我坚信码农也具有产生洞见的能力。关注【码农洞见】,一起学习和交流吧!