层叠样式表,即css,是网页开发者编写的,可以应用到html标签从而使页面元素呈现各种宽度、高度、背景、颜色等丰富的样式。
解析函数
网页应用样式的其中一个方式是在标签上使用style属性。比如:
<div color: rgb(221, 17, 68);">"background-color:lightblue"></div>
通常,style属性的值由多个键值对组成,并用分号分割。
为了把style应用于html标签,浏览器要递归调用解析函数以对这些键值对进行解析。解析的过程比较复杂。浏览器将为不同类型的数据提供不同的函数,并将它们组织在CSSParser类中,该类存储了被解析的文本以及解析器当前位置:
class CSSParser: def __init__(self, s): self.s = s self.i = 0
空格的解析函数:
def whitespace(self): while self.i < len(self.s) and self.s[self.i].isspace(): self.i += 1
style样式键值对的解析函数:
def word(self): start = self.i while self.i < len(self.s): if self.s[self.i].isalnum() or self.s[self.i] in"#-.%": self.i += 1 else: break assert self.i > start return self.s[start:self.i]
检查style中的冒号:
def literal(self, literal): assert self.i < len(self.s) and self.s[self.i] == literal self.i += 1
pair函数对一对键值对进行解析。包含属性、冒号和值,中间有空格:
def pair(self): prop = self.word() self.whitespace() self.literal(":") self.whitespace() val = self.word() return prop.lower(), val
对style中所有键值对循环调用解析函数:
def body(self): pairs = {} while self.i < len(self.s): prop, val = self.pair() pairs[prop.lower()] = val self.whitespace() self.literal(";") self.whitespace() return pairs
浏览器还有处理style错误的能力。比如网页开发者style写错或者对于浏览器不支持的功能,应该跳过不解析的键值对。
可以使用ignore_until跳过一些事情:
def ignore_until(self, chars): while self.i < len(self.s): if self.s[self.i] in chars: return self.s[self.i] else: self.i += 1
当浏览器无法解析样式的键值对时,要么跳到下一个分号,要么跳到字符串的末尾:
def body(self): # ... while self.i < len(self.s): try: # ... except AssertionError: why = self.ignore_until([";"]) if why == ";": self.literal(";") self.whitespace() else: break # ...
web是一个由许多浏览器组成的生态系统,在一个浏览器中解析的CSS可能不会在另一个浏览器中解析。对于无声的解析错误,浏览器只会忽略他们不理解的东西,而网页大部分都能在所有这些错误中工作。
样式属性
解析style属性后,就可以在浏览器的其余部分使用解析后的信息了。浏览器将在每个节点的样式字段中存储解析的信息:
def style(node): node.style = {} # ... for child in node.children: style(child)
在解析HTML之后,但在进行布局之前,在浏览器的load方法中调用style。
def style(node): # ... if isinstance(node, Element) and"style"in node.attributes: pairs = CSSParser(node.attributes["style"]).body() for property, value in pairs.items(): node.style[property] = value # ...
使用存储在每个元素上的style,浏览器可以查阅样式信息:
class InlineLayout: def paint(self, display_list): bgcolor = self.node.style.get("background-color", "transparent") if bgcolor != "transparent": x2, y2 = self.x + self.width,self.y + self.height rect = DrawRect(self.x, self.y, x2,y2, bgcolor) display_list.append(rect) # ...
在网络早期,通过给元素设置style属性是改变元素样式的唯一的办法。但是,你需要在每个元素上设置一个样式属性,如果你改变了样式,那么就需要编辑很多属性。CSS的发明就是为了改善这种状况:
一个CSS文件可以同时为多个网页提供一致的样式
一行CSS可以同时为多个元素设置一致的样式
CSS经得起未来考验,支持具有不同功能的浏览器
为了实现这些目标,CSS用两个相关的概念扩展了style属性:选择器和级联。选择器描述属性/值对列表适用于哪些HTML元素:
selector { property-1: value-1;property-2: value-2; }
由于这些规则中的任何一个都可以应用于许多元素,因此可能有多个规则应用于同一个元素。所以浏览器有一个级联机制来解决冲突,以支持最具体的规则。
接下来介绍浏览器中如何添加对CSS的支持。我们需要将CSS文件解析为选择器和属性/值对;找出页面上与每个选择器匹配的元素;然后将这些属性值复制到元素的样式字段中。
选择器
选择器有很多种类型,本文介绍两种:标记选择器和后代选择器。
对于每种类型的选择器,都有一个类来存储选择器的内容,比如:
class TagSelector: def __init__(self, tag): self.tag = tag
每个选择器类还将测试选择器是否与元素匹配:
def matches(self, node): return isinstance(node, Element) and self.tag == node.tag
后代选择器的工作原理类似。
class DescendantSelector: def __init__(self, ancestor, descendant): self.ancestor = ancestor self.descendant = descendant
match方法是递归的:
class DescendantSelector: def matches(self, node): ifnot self.descendant.matches(node): return False while node.parent: if self.ancestor.matches(node.parent): return True node = node.parent return False
定义selector函数,用于创建selector对象
def selector(self): out = TagSelector(self.word().lower()) self.whitespace() while self.i < len(self.s) and self.s[self.i] != "{": tag = self.word() descendant = TagSelector(tag.lower()) out = DescendantSelector(out, descendant) self.whitespace() return out
A CSS file is just a sequence of selectors and blocks:
CSS文件只是一系列选择器和块:
def parse(self): rules = [] while self.i < len(self.s): self.whitespace() selector = self.selector() self.literal("{") self.whitespace() body = self.body() self.literal("}") rules.append((selector, body)) return rules
解析css时调用body函数,在body函数中,直到遇到 } 时停止:
def body(self): # ... while self.i < len(self.s) and self.s[self.i] != "}": try: # ... except AssertionError: why = self.ignore_until([";", "}"]) if why == ";": self.literal(";") self.whitespace() else: break # ...
在解析选择器时可能也会出现解析错误。在这种情况下,跳过整个规则:
应用样式表
由于每个CSS规则都可以为页面上的许多元素设置样式,因此需要对所有元素和所有规则进行循环。应用规则时,其属性/值对将复制到元素的样式信息中:
def style(node, rules): # ... for selector, body in rules: ifnot selector.matches(node): continue for property, value in body.items(): node.style[property] = value
此循环应置于解析style属性的循环之前:因为style属性应覆盖样式表中的属性。
将以下css样式保存在browser.css中:
pre { background-color: gray; }
在浏览器启动时读取:
class Browser: def __init__(self): # ... with open("browser.css") as f: self.default_style_sheet =CSSParser(f.read()).parse()
当浏览器加载网页时,它可以应用默认样式表为每个元素设置默认样式:
def load(self, url): # ... rules = self.default_style_sheet.copy() style(self.nodes, rules) # ...
def load(self, url): # ... rules = self.default_style_sheet.copy() style(self.nodes, rules) # ...
<link rel="stylesheet"href="/main.css">
强制的rel属性将此链接标识为样式表,href属性指向样式表的URL地址。浏览器需要找到所有这些链接,下载它们的样式表,然后应用它们。
样式表的URL通常不是完整的URL;它们被称为相对URL,例如:
普通URL,指定方案、主机、路径等;
主机相对URL,以斜线开头,但重复使用现有方案和主机;或
路径相对URL,不以斜杠开头,解析方式与文件名类似。
要下载样式表,需要将每个相对URL转换为完整URL:
浏览器请求每个链接的样式表,并将其规则添加到规则列表中:
层叠
一个网页可以应用任意数量的样式表。由于两条规则可以应用于同一个元素,规则顺序很重要:它决定哪些规则优先,以及何时一条规则覆盖另一条规则。
在CSS中,正确的顺序被称为级联顺序。
规则的级联顺序用priority计算:
def cascade_priority(rule): selector, body = rule return selector.priority
调用style时,需要对规则进行排序,如下所示:
def load(self, url): # ... style(self.nodes, sorted(rules, key=cascade_priority)) # ...
继承
CSS中文本样式的工作方式称为继承。继承意味着,如果某个节点没有某个属性的值,它将使用其父节点的值。其中包括文本节点。有些属性是继承的,有些则不是;背景色不是继承的,但文本颜色和其他字体属性是继承的。
将实现继承的代码添加到style函数中。它必须位于其他循环之前,因为显式规则应该覆盖继承:
网页可以使用百分比作为字体大小:
h1 {font size:150%} 使标题比周围文本大50%。
但是,如果h1标记中有一个子元素,它会继承字体大小的150%值吗?它会比标题文本的其余部分再大50%吗?其实不会。
浏览器在将百分比存储到样式中并在继承这些值之前,会将百分比解析为绝对像素单位。
字体大小设置为百分比还必须处理一个棘手的边缘情况:根html元素的百分比大小。在这种情况下,百分比与默认字体大小有关:
现在,style函数可以在任何时候调用从样式表中读取属性值的computed_style函数:
样式化不仅可以让网页作者为自己的网页设置样式,它还将浏览器代码移动到一个简单的样式表中。这是一个很大的改进:样式表更简单,而且更容易编辑。
小程序名字修改的技巧规则
我们都知道名字的意义,名称作为陌生人最先的认知,在物质喧嚣的时代,如何从众多名称中脱颖而出给陌人生留下一个良好且深刻的印象,这至关重要。随着小程序开发越来越多,运营者在给小程序...
小程序商城怎么运营?
小程序商城在当今电商领域日益受到瞩目,成功运营这样一个平台对于每个经营者而言都至关重要。那么,我们该如何着手呢?一、确立品牌方向首先,我们要清晰地定义自己的品牌在市场中的位置。...
自建商城运营秘籍,吸引顾客有妙招!
新建网站的运营与维护之道一、明确核心产品的市场定位要让新建的商城网站在竞争激烈的市场中脱颖而出,关键在于精准地定位核心产品。选择具有市场潜力的热销产品,并突出其独特之处,是吸引...
小程序商城推广完全指南
随着小程序商城的日益兴起,如何在竞争激烈的市场中脱颖而出成为了关键。小程序商城的推广方式多种多样,以下是一些有效的策略:1.公众号与小程序的结合:商家可以将小程序与公众号绑定,...
推广引流方法有哪些,裂变营销什么意思
推广引流方法有哪些,裂变营销什么意思除了各公域平台,另一个比较重要的引流场景,就是在微信中。一方面做信社交性强,对于身边用友的链接更紧密,微信上也会以群、公众号的形式聚集一群有...
延伸阅读
本文来自投稿,不代表本人立场,如若转载,请注明出处:http://www.lnbdc.com/article/8594.html