我非常喜欢将 box-sizing 重置为 border-box,以至于我们在今年专门为它设立了 一个特殊的日子。但是,在设置它时有一点调整似乎是个好主意。
这是调整后的版本
html {
box-sizing: border-box;
}
*, *:before, *:after {
box-sizing: inherit;
}
关于继承想法的功劳归于 Jon Neal 此处,他说
这将为您提供相同的结果,并且使在利用其他行为的插件或其他组件中更改盒模型变得更容易。
进一步解释,假设您有一个组件,它只是被设计为与默认的 content-box
box-sizing
一起使用。您只想使用它,而不是弄乱它。
.component {
/* designed to work in default box-sizing */
/* in your page, you could reset it to normal */
box-sizing: content-box;
}
问题是,这实际上并没有重置整个组件。也许该组件中有一个 <header>
期待处于 content-box
世界中。如果此选择器在您的 CSS 中,以“旧方式”进行 box-sizing
重置……
/* This selector is in most "old way" box-sizing resets */
* {
box-sizing: border-box;
}
那么那个标题就不是您期望的 content-box
,而是 border-box
。就像
<div class="component"> <!-- I'm content-box -->
<header> <!-- I'm border-box still -->
</header>
</div>
为了使该重置更容易和更直观,您可以使用上面顶部的继承片段,并且将保留继承。
它有效
查看 CodePen 上 Chris Coyier 的 Easy to Reset Box-Sizing(@chriscoyier)。
这不是一件大事。您可能已经在使用“旧方式”的 box-sizing
重置,并且从未被它咬到过。对我来说就是这样。但是,只要我们在推广“最佳实践”风格的代码片段,我们不妨将其磨练成最好的状态。
在我看来,有一种情况是默认情况下不欢迎 border-box:img。
尝试使用“box-sizing: border-box”为 img 添加边框。图像被调整大小。
所以,我通常会为图像添加伪类 :not()
您可以直接使用 box-sizing: content-box 来定位
img
,而不是使用可能很昂贵的:not
选择器。@sanson,我很好奇。如果边框是在外部创建的,图像如何调整大小?
在这个 CodePen 中,您可以清楚地看到两个图像即使其中一个有 10 像素的边框也具有相同的尺寸:http://codepen.io/ricardozea/pen/anCyq
那么我是不是遗漏了什么?
您介意做一个演示,让我们看看您所说的吗?
提前感谢!
@sanson,当使用响应式图像时,您只想将其设置为其容器的
width:100%
,您很可能需要border-box
……请参阅 Ricardo 的 codepen 的修改版本,http://codepen.io/anon/pen/sijtD – 如果没有border-box
,带有边框的图像会“超出”其容器元素,这很可能不是您在这种情况下想要的结果。@sanson 是对的。
图像被调整大小:http://codepen.io/ricardozea/pen/KtFlH(我增加了边框宽度以更好地看到这个问题)。
在新的演示中,我将
html { box-sizing: border-box; }
更改为* { box-sizing: border-box; }
),然后 bam!图像被调整大小。但是,在我看来,这是您想要的结果。
如果您将
:not(img)
添加到通用选择器 [*:not(img) { box-sizing: border-box; }
] 中,那么您将看到图像保持相同大小,并且“溢出”其容器。这拯救了生命。真的很严肃。
我喜欢使用
:not
选择器的想法。如此之多,以至于我构建的每个组件都使用:not(.exclude)
进行样式化。这使得覆盖任何功能变得非常容易,而不必追溯您的步骤。
我最近在一个网站上做到了这一点,该网站中的所有按钮都在单击时进行动画处理。当在按钮组中使用相同的组件时,它会导致问题。
这就是 box-shadow 胜过边框的地方。
事实上,我为此创建了一个 Sass 混合器:http://codepen.io/ricardozea/pen/5d729855e63d404f0dfa46bda3bb56d1/
说实话,这个混合器使用的是
inset
值,因此如果您想让它适用于图像,请从box-shadow
声明中删除inset
值,然后瞧,边框现在在外部。在文章结尾,额外加分,因为使用了“gotten”而不是“got”,但应该写成“never been bitten”。
您不能只使用
当然可以。您只需要记住这样做,因为它不是特别直观(至少对我来说)。这相当于“做一些事只是为了撤销它”。不是什么大问题,但就像我在文章底部所说的那样,如果我们正在分享这种“最佳实践”类型的代码片段,不妨将其做得尽善尽美。
我同意,以这种方式做似乎一样容易,而且更简洁。因此,除非继承在性能方面比设置每个元素有优势(我无法想象它会这样),否则这似乎更直观和明显。
尽管在他特定的例子中,为了完整性,CSS 必须是
这篇文章让重置 HTML 的特定子集为 content-box 似乎很难,但实际上很简单。
没错,为什么还要费心去考虑所有这些,当您可以简单地重置组件,就完成了呢?所有相同的好处,更简单的重置,不会撤销自己的操作,略微更好的最佳实践。
在意识到 * 选择器不包括 :before 和 :after 之后,我同意您的观点,Chris,因为现在代码必须是
以确保您涵盖了组件中的所有元素。如果您有任何特殊的规则(如 Sanson 提到的),您可能还需要根据当前的标准方法重置这些规则。
感谢您提供此信息,它听起来像是应该在我工作中实施的东西。
Christian Longe,您也忘记了
.component
伪元素。一个略微偏离主题的问题
@Christian Longe(嗯,其他人也是):为什么
*
选择器不包括:before
和:after
?* {}
就像html {}
一样,它是一个针对元素名称的通用选择器。就像存在*:hover
(伪类)一样,也存在*::before
(伪元素)。我认为
.component *
在选择器性能方面很糟糕,而*
和.component
则要好得多。@PhistucK,我不得不不同意,
html {}
不是您所指的通用选择器。正如您所读到的,没有任何关于html
是“通用”的参考:https://mdn.org.cn/en-US/docs/Web/CSS/Universal_selectors*
和html
是两个完全不同的选择器。但这并不是我问题的重点。那么,让我重新表述一下我的问题。
通用选择器
*
难道不也包括:before
和:after
伪元素吗?您要么误解了我的意思,要么就是我的表达不够清楚。我的意思是,它与
html
相似,因为它们都匹配元素名称。*
是一个用于元素名称的通用选择器(表示“任何”),而不是针对 CSS 中的任何事物和一切事物。实际上,“通用”这个词很奇怪。它实际上意味着“任何元素名称”。
这里有一个问题——
为什么
*, *:*, *::* { ... }
不匹配任何元素、伪元素和伪类?(技术答案可能是因为这在性能和内存方面会很糟糕,概念答案可能是因为这不太有用,因为浏览器可以添加新的伪元素和伪类,这些元素和伪类可能会突然出现在不应该出现的地方,而标准答案是规范中没有这样规定。但没有真正的答案)
@Ricardo
*
选择器只匹配元素。::before
和::after
伪元素不是真正的元素(它们在 DOM 中不存在作为独立元素)。而其他伪元素(::first-line
、::first-letter
)不需要由*
匹配,因为它们已经通过它们的父元素匹配。一个例外是::selection
,但现在我们只是在规范中遇到了奇怪的边缘情况。(而且,只有适用于选择、首行和首字母的属性是与字体相关的属性。)好主意!
html { box-sizing: border-box; }
声明不应该放在通配符*
选择器之后吗?这样box-sizing
值才能正确级联,或者html
不包含在*
选择器中?就像这样html
包含在*
中,但顺序在这里并不重要,两种方式都可行。顺序并不重要。通配符选择器(
*
)的特异性为 0-0-0,而元素选择器html
的特异性更高,为 0-0-1。因此,无论源代码顺序如何,元素选择器都会获胜。如果
box-sizing
是继承的,为什么我们还需要声明inherit
呢?html { box-sizing: border-box; }
不应该就足够了吗?它不是自然继承的,它只支持一个内在值,可以强制它被继承。
哈。我本来打算建议修改文章,不要说
box-sizing
是一个继承属性。但看来你已经做到了。;-)这澄清了我的问题,因为
inherit
可以用于任何 CSS 属性,无论它是否被继承。既然我们说到这里,那些
*:before
和after
选择器怎么样?它们有多必要?如果你使用
:before
和:after
并想使用这种方法应用box-sizing: border-box
,你必须像这样选择它们。如果你不这样做,就不要管它们。老兄,说实话,自从我学会了
border-box
后,我再也没有回到box-sizing: content-box;
了。这看起来是个好建议。也许可以更新所有关于这个问题的文章(例如边框框日文章,以及任何其他使用类似代码片段的文章)?
嗨,感谢你的提示。继承框大小将是一种更好地、更方便的尺寸方式。
我不认为我同意这个想法。
如果你创建了一个特定的默认框大小方法,你就会确切地知道在未来的道路上会发生什么。它是可预测的,它并不会阻止你创建一个面向不同于默认框大小方法的组件,如果你需要的话。
拥有一个在任何地方都相同的默认框大小方法可能会迫使你明确地声明如果你想使用另一个属性,但这是可以的:明确地声明是件好事!
如果你默认继承框大小方法,这意味着,只要你改变了一个元素的行为,所有嵌套的元素都会突然表现得不同,即使它们本身没有改变。这是不可预测的,这使得在(当)出现问题时更难理解发生了什么。
当你在多个地方开始嵌套组件,或者当你在未来开始更新或添加组件,或者当在团队中工作时,多个人必须挖掘相同的 CSS 代码,等等等等,不可预测的框大小带来的问题只会变得更糟。
如果你的插件只适用于一种框大小方法,你只有两种选择:修复插件或修复 CSS。选择最合理的那个,然后完成它。
我必须 +1
Chris 的(我还不确定这是否已经是行业最佳实践)建议当然很聪明,在非常特定的用例下是有意义的,但我越是思考它,就越是觉得它可能存在类似于处理
em
的级联问题。嗯,我不确定这是否更好。仅仅因为你想让一个元素使用
content-box
,并不意味着你想让里面的所有东西都使用它。我不认为我会使用content-box
,因为我希望所有元素都生活在一个“content-box
世界”中。我认为我会为了那个元素的特定布局原因使用它。但话说回来,我不能说我有很多理由回到content-box
,所以我也不能确定。完全疯狂。不错,Chris/Jon
这是一个面向对象的解决方案,一种更好的实践。酷!
这个想法不会破坏预期所有元素都使用标准盒子模型的内容(前提是它被包裹在一个
box-sizing
属性显式设置为content-box
的元素中),但它仍然会破坏任何使用任何非默认box-sizing
的内容的布局,同时期望其内部元素具有box-sizing
设置为content-box
(CSS3 中的默认值为content-box
,而不是inherit
)。我见过一些布局只对外部布局 div 使用
border-box
,而不对内部元素使用,使它们保留默认的盒子模型——所以现在必须将内部元素显式地设置为通常是 CSS 标准默认值的值。我说的是包含你无法控制的内容,而这些内容有时是不可避免的。此外,图像带有边框等也有一些问题,因此也需要一些例外情况。
我写了一个提案,希望在没有任何通配符选择器 hack、伪类,或对元素添加任何类,或强制属性在 CSS 标准中不被继承的情况下,解决这些问题和类似的问题。这是我的提案,只是一个草案
更好的盒子模型 == 没有盒子模型
https://gist.github.com/rsp/9f47e0105e105dae6869
我期待您的评论。
恕我直言,这个提案听起来有点荒谬……
你建议将
width
和height
属性拆分为八个新属性——width-content
、width-padding
、width-border
、width-margin
,以及height
的类似属性……你真的认为这会让在现实生活中指定布局尺寸变得更容易吗?我对此表示怀疑——很多怀疑。(此外,
width-margin
在我的理解中意义最小——将边距包含在宽度或高度中,在任何盒子模型中都从未做过,这样做究竟有什么好处?我无法想到这会产生实际意义的实际用例。请记住,边距允许负值——所以根据你的提案,width-margin: 500px
与margin: -100px
相结合,会导致一个宽度为 700 像素的元素,或者……?)你希望让事情变得更容易是合理的——但我认为你并没有通过这个提案实现这一点;相反,它甚至增加了复杂性。
CBroe,感谢你的评论。重点是你不需要使用所有值,而且
width
和height
并没有被拆分成其他值——它们仍然可用,但你也有浏览器已经知道但现在你可以轻松访问的不同值——如果你需要它们的话。例如,而不是编写.x { box-sizing: border-box; width: 100px; }
你可以使用
.x { width-border: 100px; }
而不是编写
.y { box-sizing: content-box; width: 100px; }
你可以使用
.y { width-content: 100px; }
你也可以使用
.z { width-content: 200px; height-border: 100px; }
这在使用 'width' 和
box-sizing
时是不可能的。添加 *-margin 值是为了完整性,它们可能最没用,但有它们也没什么坏处 - 你不需要使用它们。是的,对于负边距值,宽度边距会大于宽度边框。在你问之前 - 是的,某些值如果会导致负内容宽度,就没有意义。
我不想在这里偏离主题,但我乐意在 GitHub 和推特上回答所有评论。
https://gist.github.com/rsp/9f47e0105e105dae6869
如果这是一个最佳实践片段,我认为我应该能够在片段部分轻松找到它。也许我只是没有在正确的地方寻找,但我找不到它。只是说说 :)
仅供参考,我想你仍然需要 -moz-box-sizing (FF < 29 包括 e.g. 17 和 24 这些是 ESR) 和 -webkit-box-sizing (Android <= 3)。即使 caniuse 也可能在默认过滤器视图中具有误导性。我想只有那些仍然用手输入 CSS 的老派类型的人才会感兴趣 ;)
性能如何?
* 匹配器是 CSS 中存在的性能最差的匹配器,对吧?
也许你应该在你的文章中提到这一点 - 至少作为旁注。
通配符选择器 (
*
) 与简单的h1
或div
选择器一样快。只有当你嵌套选择器时,比如.something *
,它的性能才会变差。对 Paul Irish 版本进行的“小调整”似乎对 HTML5 元素不起作用(对我来说)。该元素没有继承 box-sizing 布局,但它具有 box-sizing: inherit;。你知道为什么吗?
所以,与其在 html 上使用 box-sizing 并让所有其他元素继承,我仍然更喜欢
*,
*:before,
*:after {
.box-sizing(border-box);
}
你必须做一个演示来展示问题。这些元素应该具有 box-sizing: inherit;,以便它们从父元素继承,从而更轻松地设置 box-sizing 上下文。这绝对与这些元素是否为“HTML5”无关。
我认为这是一个非常棘手的问题。它会帮助很多人,但像我这样的博主会发现它很难实现。无论如何,这是一种很棒的方法。
我是否不知道一些特殊情况,在这些情况下,
*:before, *:after
比仅仅:before, :after
更好?如果某些旧浏览器版本对此表示反对,我不会感到惊讶。@Tigt –
如果两者都有效,我想
*:before
更好,因为它更明确、更有意且更易读。我正要将此建议添加到我自己的样板中,但我们是否已经达成共识?
如果你使用 normalize,请确保添加 input type=”search” 的 border-box 值,因为 put “inherit” normalize 从 content-box 的默认值继承。
例如
不需要在你的 CSS 中“添加”它,只需在 normalize.css 中更改它。
这样在以后更容易维护。如果你遇到问题,你就知道只有一个地方需要查看。此外,normalize.css 是为了让我们能够根据自己的特定需求自定义它而创建的,而不是一定要“照搬”。
我更喜欢不改变使用第三方工具,比如 normalize、jQuery 等…
因为如果出于某种原因需要使用某个 CDN,这样就不会影响我的工作。
好吧,你在这里涉及了两个不同的领域,加上 CDN 部分。
“不同领域”是指你通常从不,至少我从未听说过第一个这样做的或知道这样做的人,编辑核心 jQuery 库。或者任何主要的 JavaScript 库。同样,我从未听说过有人这样做。至少现在还没有。
但就编辑 CSS 文件而言,尤其是“重置”(是的,我知道 normalize.css 不是重置,所以请耐心等待),这是许多 Web 设计师/前端/后端开发人员经常做的事情。
我理解你不编辑第三方资产的观点,但这完全取决于你正在处理的资产类型。
另一方面,你提到使用 CDN 托管这些第三方资产。这很好。
但是,如果你使用 CDN 是因为你或你的团队确实可以访问它。如果你无法访问它,你可能首先就不会在其中托管你的 CSS 文件,因此,编辑 normalize.css 仍然是可行的。
“只需在 normalize.css 中更改它”
是的,我的意思是这个。