lunasun

CSS vs. JavaScript: 相信 vs. 控制 | Christian Heilmann

原文链接: christianheilmann.com

当阿姆斯特丹的GotoConf要求我发言时,我以为这将是另一场机器学习或Progressive Web Apps的发言。但是,组织者要求我涉及CSS,这一在他们的“编程语言”中被低估的语言。说起来我从最开始就一直是CSS的粉丝。但我觉得一个硬核的开发会议参与者并不会对CSS那么有兴趣。他们不会去关心CSS的细节。然而,我认为这是他们必要的烦恼。所以我写了一篇演讲来讨论使用CSS的意义,以及我们为何没有发挥它的长处。

这是我演讲的笔记。

一场无聊的战争

Captain America vs. Iron Man

某天在再次观看了《美军队长:内战》后,我又一次感到了厌烦,并且依然不太清楚它究竟想表达些什么。超级英雄被迫对他带来的损害负责,这样的想法并不新鲜。要求控制他们也不新鲜了。在这点上,《超人总动员》做得很好。

所有这些优秀的超级英雄互相对抗的前提更让我感觉无聊。我们知道他们的能力,知道他们在无数场合里救了彼此的性命,是挚交好友。我们知道他们的力量匹配。在这些遭遇里,没有暴力、没有真正的理由、也没有愤怒。感觉就像Marvel创造了太多很酷的人物,现在就试图让观众站边,借此出售更多的玩具,人为创造戏剧性。

当我们谈论使用CSS还是JavaScript进行布局时,会让我产生同样的感觉。这两者都有自己的优点,都有自己的能力,有它们自己的粉丝群,时刻准备挖掘最详细的信息来呼吁使用其中的一个而不是另一个。但我觉得这挺无聊的。事实上,正是将这两者结合起来使用使得网络得以向前发展。而壁垒分明的两个阵地则让我们止步不前。有的人将CSS看作过去的事情,认为在我们这个模块驱动的世界里,应该在脚本空间来完成一切。有的人则认为CSS和它的预处理器以及构建脚本足以做任何事情。还记得在DHTML的时代我们用JavaScript做所有事吗?还记得“CSS是唯一解决方案”的反击吗?还有我们(ab)用复选框来做复杂的互动以避免使用任何JavaScript的时候吗?

Giana Blantin说得好:

这两波人:

“CSS太简单了,它甚至不是一种代码”

“CSS太难用了,我们需要用JS代替它!”

能不能和对方谈一谈?

很多对CSS的误解是因为开发人员不了解它与编程的区别。相反,我们瞎搞一气然后把东西都改了,并在搞砸了某样东西后,得出结论,它不够好,我们需要替换它。

I know Open GL - I can do gradients

常见的问题是过度使用标记。就像使用OpenGL创建简单渐变 我们不需要用牛刀来杀鸡。CSS里有一些我们无法与客户端脚本匹配的袖里玄机。与语法或语言功能无关,关键是如何分配任务。

谁在犯错,谁应当宽容?

CSS,很像HTML,是_容错_的。这可能不太好理解。它意味着终端用户不应该因为开发人员的错误而受到影响。当开发人员出错时,使用CSS构建的产品仍然会显示出来。他们看起来不完美,但仍然工作着。当CSS解析器遇到无法解读的属性时,就会跳过它。遇到不能处理的值或不支持的属性时 - 也会跳过它。我们保持这样向后兼容。

具有背景颜色和渐变的按钮会在较老的环境显示背景颜色。在由于性能问题不支持渐变的环境下也会显示背景颜色。而在更快、更高保真和支持渐变的环境下则显示渐变色。

你不需要了解环境,也不需要做出这个决定。相关操作系统、浏览器和代理可代你决定。

JavaScript_不是容错_的。这可能产生许多问题。在使用JavaScript时,你获得了更多控制权,但也相应需要承担更多责任。

客户端上的JavaScript可能由于几十种原因中断。浏览器的不支持、片状的连接,你终端用户的移动提供商还可能会把减少和打包下线的脚本当作自己的工作。当JavaScript遇到无法解读的东西 - 它会中断,停止运行并且不显示任何东西,从而因为你的错误惩罚你的产品使用者。或是让引入的其他人员和脚本的错误代码影响你的终端客户。

换句话说:

  • CSS - 你使用样式并期望它可行。

  • JavaScript - 你控制样式,能够并且应当验证它的可行。

CSS意味着拥抱网页的“杂乱无章”,就像布拉德·弗罗斯特(Brad Frost)所说的那样。网页不是可以摆放像素的固定画布。很多事情会脱离你的掌控:

  • 你目标用户的浏览器

  • 其设备的分辨率,像素密度和颜色设置

  • 他们的连接可靠性和速度

  • 他们的连接限制 - 资源封锁是一个问题

  • 他们的字体大小和网页缩放

  • 你的的产品在他们机器上的资源可用性(CPU是不是已经烧起来了?)

  • 你的产品中的文本内容和图像大小的数量 - CMS任何人?

这可能会让人心生畏惧,通常我们想要控制产品运行的环境 - 为了不陷入疯狂。这意味着我们把很多潜在用户拒之门外。

在这个未知的环境中,我们必须决定谁负责处理其性能问题:

  • CSS - 重点需要浏览器表现良好,使用GPU资源和跳过功能。

  • JavaScript - 测试支持是你的任务。并确保渲染,快速绘画和回流。并保持动画同步。

CSS是非常棒的,而且浏览器厂商投入大量精力来调整界面性能。

那么为什么我们低估了CSS的价值并且高估了JavaScript的优势呢?我想最应该责怪的典型是 - Internet Explorer。

CSS和它的曲折历史

CSS的成长过于快速,以至于没有得到浏览器的支持而无法成为可靠的工具。

CSS一开始有着很大的局限性,它意味着对可视化的HTML和属性的替代。意味着放弃所有这些fonts,bgcolor,align,center, HR 和 friends。乱七八糟的浏览器支持和没有调试选项的奇怪的错误对它毫无助益。我们知道事情是错的,但是我们束手无策。甚至没人可以询问,因为浏览器制造商无法提供反馈。

当iPhone出来的时候,CSS已经开始崭露头角。 “HTML5是未来”的故事需要很多额外的功能。虽然有苹果的摇旗呐喊,但标准化需要太长时间,而且“仅限Webkit”。

这带来了CSS中的前缀,并再次该死的针对了不同的渲染引擎。浏览器制造商开始革新,并以其他前缀功能显示出优势。对开发人员来说,这就意味着重复劳动,并且必须为每个浏览器选择一个支持计划。当然,还得有一个计划支持旧的、过时的浏览器。这些围绕前缀的新浏览器战争引起了很多争议和混乱。

最后,但同样重要的是,直到最近CSS中才出现布局模型。此前,我们想办法使用定位和浮动。定位,特别是以像素为单位的绝对定位在网页上是不明智的。一旦人们调整字体大小,内容就会重叠。浮动定位则需要清算元素。

如果你不是“网页老手”,这些对你来说就不是一个可靠的底线,或者能够轻易理解。

我们要让CSS无视浏览器限制并发挥作用

过去的解决方案是用JavaScript打补丁。我们可以读出条件,然后做出反应,创建HTML和应用样式。由于JavaScript是一种编程语言,我们可以完全控制正在发生的事情。我们有conditional,loops以及comparisons — 所有程序员在CSS中没有的东西。这在某种程度上是对CSS概念的误解。匹配多个元素的选择器本质上是一个循环。我们甚至可以使用:nth-child()来定位集合中的元素。

总的说来,因为我们必须使用JavaScript来修补它,所以CSS一直在跳跃式发展。与此相比,特别令人失望的浏览器支持只是是一个小得多的问题。

  • Evergreen浏览器是一处 - 所有浏览器都在不断的升级路径。我们甚至从浏览器制造商那里或者会有什么新东西出现。

  • 浏览器工具可以详细了解CSS适用于什么。我们甚至有了像动画编辑器和拾色器这样的可视化工具。 Bezier editor in Firefox Devtools 火狐(Firefox)浏览器开发工具里有贝塞尔曲线动画的可视化编辑器。

  • 跨浏览器的CSS支持很好地被记录着:caniuse.com是一个不可思议的资源。它不仅显示哪个浏览器和哪个环境支持什么。它还解析了应用中的bugs,提供了说明和错误报告的链接,甚至还有 一个 API将这一信息镶嵌到开发文档和工具中!“我可以在Visual Studio代码编辑器页脚中使用信息吗”通过在 Visual Studio Code 中使用“Can I use” 扩展,可以直接在编辑器中显示浏览器支持信息。这样你在写代码的时候就知道你屏蔽了哪些人。

  • 几乎所有浏览器都有支持渠道和错误跟踪。有些甚至允许你 在Twitter上提交bug。浏览器制造商的团队都在社交媒体上非常活跃,而且可以联系上。

  • SassLess这样的预处理器已经加快了对CSS规范的创新速度。好比jQuery启发了今天的JavaScript,这些带来了人们想要的功能。

  • 社区花费了大量时间使CSS更易维护。Nicole Sullivan写作的《面向对象的CSS》Brad Frost《原子设计》已经存在多年了,并应该有助于降低复杂性。

CSS能为你做什么

这里有一些CSS的超赞功能,你应该考虑用上它们。

计算CSS值(Calculated CSS values)

CSS中似乎一直缺失计算值的方法。典型的例子是宽度为100%,但需要padding的绝对定位元素。在过去,我们需要通过嵌套另一个元素并它加上padding来应用填充。现在我们可以使用 CSS calc() 并添加宽度calc(100% – 1em)。

Calculations 在浏览器中支持良好,可以放心使用。

媒体查询(Media Queries)

CSS媒体查询 允许你对文档视口的更改做出反应。实质上,它们意味着当视口满足某些标准时,你将应用部分样式表。这可以是至少一定宽度或至多一定高度的视口。如果文档是打印件,你还可以查看屏幕是纵向或横向。

CSS Media Queries在matchMedia里有同等的JavaScript。这让你能够按需加载内容。媒体查询的一个问题是,无论合适与否,浏览器都会以块的形式加载图像。

内容生成(Generated content)

使用::before::after这类CSS中的伪选择器允许你创建纯视觉的内容。CSS中的伪选择器允许您创建纯视觉的内容。这个方法非常好,因为它能确保只是出于美观而做的东西不需要一个自己的,空的DIV、SPAN、B或I元素。这个方法在样式表中保留所有视觉信息而不是脚本或HTML文档。你可以将其与阴影,渐变和其他创建视觉效果的CSS功能配对。一个令人印象深刻的例子是“一个单个DIV“。该网站展示了单个DIV元素创建的数十个视觉效果。

Sully of Monsters, inc. as a single DIV

这张图片就是一个用生成内容创建的单个DIV

动画和过渡(Animations and transitions)

CSS里的动画过渡是iPhone出现后的一个重大突破。过渡让你能够创建从一个状态到另一个状态的平滑变化。你不需要知道应该发生什么变化。只需要告诉浏览器转换时间的长度和使用什么平缓功能。动画则给你更精细的控制。你能够定义关键帧,以及动画的表现方式。动画和过渡能够发生在事件前、中间和之后。这允许你以可预测的方式与JavaScript进行交互。使用CSS的好处是浏览器确保动画的性能。这通过在GPU上运行它们并在需要时调节帧速率来实现。这也是确保用户手机电池寿命良好的关键。如果在JavaScript中动画,可能很容易发生错误。

视口单位(Viewport Units)

当你想要详细定义体验时,媒体查询很有用。但是,你还能用视口单位根据可用空间对元素进行大小调节。视口宽度(vw)是完整视口宽度的百分比。所以在480px宽屏幕上,10vw是10%或48px。这与%单位不同,%单位是父元素的百分比,而不是视口。嵌套的百分比会变小,vw则不会。视口高度(vh)是完整视口高度的百分比。你也可以使用和我不一样的vmin和vmax的设置,采用更小或是更大的vw和vh。支持视口单元的唯一的麻烦是,到目前为止,Edge仍然不支持vmin和vmax。

CSS Tricks有一篇很好的文章《视口单位能有多强大》。从分辨率独立嵌入到依赖视口的排版,你可以使用视口单元创建高度灵活的界面。

弹性盒(Flexbox)

弹性盒 是一种在CSS中创建元素布局的方法。实质上,所有声称布局表格更容易的人都会在CSS中错过这点——及更多的功能。你可以将元素的子元素对齐在右侧、左侧、顶部或底部。你可以定义它们来填充可用空间,每个元素使用相同的或大于其它元素的空间。你还可以定义它们使用彼此之间或其周围的可用空间。它与tin上说的一样灵活。如果你想要一个可视化的编辑器来看看这是什么意思,可以玩玩这个:用React生成的一个好用的flexbox编辑器

Playing with the different settings of Flexbox

还有一个名为Flexbox Froggy的游戏的游戏。它用好玩并且方便的形式教导这些概念,对于孩子们来说从CSS起步非常不错。

Flexbox Froggie

Zoe Gillenwater在许多活动中做过一个关于弹性盒的优秀演讲。讲演中我最喜欢部分是Zoe展示他们如何在产品中使用弹性盒。范例来自booking.com,同时还展示了不支持这个的浏览器上的备用(fallback)形式。

CSS 网格(CSS Grid)

如果弹性盒解决了一行或一列中布局元素的问题,CSS网格则进入了下一个阶段。它可以让你在两个维度(行和列)定义的网格里摆放元素。网格已经存在了一段时间,现在终于得到了(浏览器的)全面支持。 A simple grid example

一些设置将一系列元素转换为灵活的网格

网格可能会不太好看,因为它的灵活性意味着有很多选项可供选择。到目前为止,最简单的方法是Rachel Andrew的 “Grid by Example” 资源。这里给出了一个网格布局的复制+粘贴的示例。他们中的许多人对于不支持的浏览器都有备选项。解释它们的内在和外部的培训视频使它成为一个了不起的资源。

如果你在有挑战的环境下学得更好,你可以通过玩CSS Grid Garden来学习CSS网格。

CSS Grid Garden

学着用CSS网格来给胡萝卜浇水。

有一些“必看”的关于CSS网格的演讲。第一个是“CSS网格布局“,同样来自Rachel Andrew,

Jen Simmons 采取了不同的做法。在她“网页真实艺术指导” 演讲中,她说明了网格的多功能性如何帮助我们突破“盒子布局”思维。

另外还可以混合和配合使用网格和弹性盒子。在单元格中可以并且应当使用弹性格子。一起使用这些工具可以帮助你创建灵活的布局,允许可变的内容和适应可用空间发生的改变的布局,Web的布局。

CSS 自定义属性 (变量)(CSS Custom properties (variables))

一个在类似Sass和Less这样的预处理器已经有了的,人们期待已久的CSS功能之一就是变量。现在我们有了CSS自定义属性,这是让我非常振奋的CSS消息。你可以在文档中定义一次可重用的设置,并将其应用于整个文档。最常见的使用场景是自定义颜色和大小。但是你可以进一步定义字体和其他排版。你还可以使用它们来嵌套CSS中的计算。这在之前是完全不可能的。此外,让人惊讶的是,自定义属性也可以使用JavaScript动态设置。

Example of reading and setting CSS Custom properties in JavaScript

如何使用JavaScript阅读和编写自定义CSS属性 - (摘自Lea Verou的演讲)

如果你想了解所有关于CSS自定义属性的惊人能力,这里有一个你不应该错过的演讲:Lea Verou’s “CSS Variables: var(—subtitle)”,这就是一个信息的宝库。

CSS 特性查询 (CSS Feature Queries)

另外一个非常受欢迎的CSS功能是特性查询(Feature Queries)。这非常像媒体查询。通过使用@supports,你可以检查当前用户代理是否支持某个功能。接着可以定义一个CSS块,仅在功能支持时才应用。你可能会觉得奇怪,因为CSS的容错性质应该已经照顾到了这点。但是,它的作用是提供更精细的控制。当部分用了“not”关键字的特定功能不被支持的时候,还可以定义回退。

CSS和JavaScript?

同时使用CSS和JavaScript能够作出强大的、正确的事情。就CSS而言,它仍然无法做到所有事情。在一些场合,CSS的天然特性与我们想要实现的效果是相反的。

就像Cristiano Rastelli在他的 “让CSS充满和平” 的演讲中所说,“分离问题”(Separation of Concerns)的珍贵特征不适用于模块世界。

Separation of Concerns in a component world

当CSS成为一种趋势,我们将所有的外观、感觉和行为从HTML转移到CSS和JavaScript中。我们可以在文档上甚至是项目范围下定义。我们庆祝CSS从父元素继承的事实。但当我们构建可以重新使用的组件时,又不希望如此。我们希望它们带有自己的外表、感觉和行为,并且不会渗透到邻近的元素上或从父母继承。

CSS和JavaScript在非模块化的世界中合作

在构建基于文档的解决方案时,完全有理由去挖掘CSS的力量。你可以并且应该使用JavaScript来为CSS提供它不能读取信息。尽管以最小的影响来这样做需要慎之又慎。

在这种情况下,用CSS和JS搭配工作的层次结构如下:

  • 使用CSS,当你可以 – 使用你在这篇文章里看到的东西

  • 如果需要与CSS通信,考虑 更改自定义属性(Custom Properties)

  • 如果该选项不成立,通过classList在父级元素上应用类(classes)

  • 作为最后手段,你可以 直接更改样式

Example of getting the mouse position into your CSS

一个绝佳的例子,展示了如何在JavaScript中读取鼠标位置并将其存储在CSS自定义属性中 – (摘自Lea Verou的演讲)

每当你动态更改样式时,请记住你正在对付浏览器。每种风格的变化都会在回流、渲染和绘画方面产生后果。Paul LewisDas Surma维护着一个名为《CSSTriggers》的便捷指南。在里面详细描述了哪个CSS更改导致了浏览器的哪个问题。

CSS Triggers

CSS 触发器提供不同样式变化的效果信息。

总结

CSS比过去可靠地多,剩下的会与现在不同的东西也不多了。 需要牢记的最主要的事情是,CSS并不意味着与JavaScript做同样的事情。即使布局语言也不能像CSS那样工作,满足同样的需求。它的任务艰巨,但表现出色。 当你使用CSS时,无论终端用户的设置是怎么样的,浏览器都能够帮助你满足他们的需求。这是网页的核心原则,并在 W3C HTML设计原则中被定义为:

用户高于页面作者高于实施者(或者浏览器)高于规范制定者(W3C/WHATWG)高于理论的纯粹性

我们的用户应该获得平滑的、可靠的、省电的界面。 所以,再考虑一下CSS把。你还可以通过借鉴社区的成果来偷懒。

以下是一些灵感来源和活跃的CSS使用者

在准备这个话题时,我会时不时回看网上一些特别优秀的人撰写和维护的资源。 如果你想要跟上CSS的发展,这里有一个简短的没有特定排序的人员列表供你关注。我要感谢他们每个人。他们正在为我们所有人提供更易使用的的网页。

  • Ire Aderinokun (@ireaderinokun) 在博客bitsofco.de.上写了很多易于掌握的并且切中要害的CSS信息点。

  • Ana Tudor (@anatudor)是一名开发人员,她在CSS中创建极其复杂而美丽的动画。她的Codepen 是最常见的一个,并对CSS引擎所做工作为浏览器制造商测试浏览器性能提供了很大的帮助。

  • Jen Simmons (@jensimmons) 是Mozilla(火狐浏览器的公司)的CSS布局和设计专家。

  • Rachel Andrew (@rachelandrew) 对我来说是 #1 的CSS网格专家。

  • Chris Coyier (@chriscoyier) 是超赞的CSS资源共享网站CSS Tricks 与交互开发社区 Codepen的创始人。

  • Sarah Drasner (@sarah_edo) 是一位专注于构建可维护产品的动画和设计专家

  • Zoe M. Gillenwater (@zomigi)是一位在产品中使用最前沿CSS技术的开发主管。

  • Brad Frost (@brad_frost)是《原子设计》(Atomic Design)的作者,讲述了在大型项目中如何使用和重用CSS的可扩展方式。

  • Rachel Nabors (@rachelnabors)是一位漫画家和动画专家,会撰写关于Web动画和不同技术的优点的文章。

  • Una Kravets (@una)是专精CSS及其新功能的开发人员。 她也是一个播客主,她总能把握住CSS和其他视觉技术的最新脉动。

  • Lea Verou (@leaverou) 是《CSS揭秘》(CSS Secrets)的作者、麻省理工学院研究员、W3C的CSS工作组的特邀专家。 她在研究中一丝不苟,严于律己,并能在短时间内交付大量丰富的信息。

  • Sara Soueidan (@sarasoueidan)是一位响应式设计及将新技术实际运用的专家。

我每天都能在这些人(还有其他人)那里得到灵感,希望你也能拥有这样的体验。