[译] 前端项目中常见的 CSS 问题

6,213 阅读13分钟

原文Common CSS Issues For Front-End Projects
作者Ahmad Shadeed 发表时间:december 27, 2018
译者:西楼听雨 发表时间: 2019/01/01 (转载请注明出处)

When implementing a user interface in a browser, it’s good to minimize those differences and issues wherever you can, so that the UI is predictable. Keeping track of all of those differences is hard, so I’ve put together a list of common issues, with their solutions, as a handy reference guide for when you’re working on a new project.

Let’s begin.

在浏览器中实现一个用户界面时,我们最好尽可能地把差异点和问题最小化,这样用户界面就会更加可预测。不过要记住所有的差异点是非常困难的,所以我将一些常见的问题及其解决方法做成了一个清单,以便您在开发一个新项目时有一个方便的参考。好,我们开始!

1. 重设 button、input 元素的背景

When adding a button, reset its background, or else it will look different across browsers. In the example below, the same button is shown in Chrome and in Safari. The latter adds a default gray background.

在添加一个按钮时,记得重设它的背景,不然在不同浏览器下它将呈现不同的效果。下面这个例子是同一个按钮在 Chrome 和 Safari 中所展示出的样子。后者多了一个默认的灰色背景。

img

Resetting the background will solve this issue:

重设它的背景可以解决这个问题:

button {
  appearance: none;
  background: transparent;
  /* Other styles */
}

演示地址:codepen.io/shadeed/pen…

2. Overflow: scroll vs. auto

To limit the height of an element and allow the user to scroll within it, add overflow: scroll-y. This will look good in Chrome on macOS. However, on Chrome Windows, the scroll bar is always there (even if the content is short). This is because scroll-y will show a scroll bar regardless of the content, whereas overflow: auto will show a scroll bar only when needed.

要固定一个元素的高度,并允许用户进行滚动查看,可添加overflow: scroll-y。这在 macOS 版的 Chrome 下是没有问题的。然而,如果是在 Windows 下的话,滚动栏就会一直存在(即便元素的内容很短)。这是因为 scroll-y 不管元素的内容如何都会显示出滚动栏,如果使用 overflow: auto 的话就只会在必要的时候才展示出滚动栏。

img

(左边:macOS 版的 Chrome;右边:Windows 下的 Chrome)

.element {
    height: 300px;
    overflow-y: auto;
}

演示地址:codepen.io/shadeed/pen…

3. 添加 flex-wrap

Make an element behave as a flex container simply by adding display: flex. However, when the screen size shrinks, the browser will display a horizontal scroll bar in case flex-wrap is not added.

要使一个元素变成 flex 容器,只需添加 display: flex 即可;但如果只是这样,而没有添加 flex-wrap 的话,当屏幕尺寸缩小时,浏览器就会展示出一个水平滚动栏。

<div class="wrapper">
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
</div>
.wrapper {
  display: flex;
}

.item {
  flex: 0 0 120px;
  height: 100px;
}

The example above will work great on big screens. On mobile, the browser will show a horizontal scroll bar.

上面这个例子在大屏幕下效果会不错;但在移动端的话,浏览器就会展示出一个水平滚动栏。

img

The solution is quite easy. The wrapper should know that when space is not available, it should wrap the items.

解决方法非常简单:让 wrapper 知道当空间不够用时,应该对元素进行换行排列。

.wrapper {
    display: flex;
    flex-wrap: wrap;
}

演示地址:codepen.io/shadeed/pen…

4. 如果元素的数量是动态的话,不要使用 justify-content: space-between

When justify-content: space-between is applied to a flex container, it will distribute the elements and leave an equal amount of space between them. Our example has eight card items, and they look good. What if, for some reason, the number of items was seven? The second row of elements would look different than the first one.

如果把 justify-content: space-between 应用在一个 flex 容器上,它将会把它的元素按照等距的间隔来排列。在我们的例子中有 8 张卡片元素,看上去效果不错。但如果因为某些原因,他们的数量变成了 7 会怎样?——第二行的元素显示效果就会与第一行不一样。

img

img

演示地址:codepen.io/shadeed/pen…

In this case, using CSS grid would be more suitable.

其实在这种场景下,使用 grid 布局会更加合适。

5. 长词和长链

When an article is being viewed on a mobile screen, a long word or inline link might cause a horizontal scroll bar to appear. Using CSS’ word-break will prevent that from happening.

在移动设备屏幕上查看一篇文章时,长词或长链可能会导致水平滚动栏的出现;使用 word-break 可以防止这个问题发生。

img

.article-content p {
    word-break: break-all;
}   

img

Check out CSS-Tricks for the details.

具体细节请查看 CSS-Tricks

6. 渐变中的透明色

When adding gradient with a transparent start and end point, it will look black-ish in Safari. That's because Safari doesn’t recognize the keyword transparent. By substituting it with rgba(0, 0, 0, 0), it will work as expected. Note the below screenshot:

当添加用 transparent 作为渐变的开始或结束点的渐变时,在 Safari 中会显示成乌黑的效果。这是因为 Safari 并不能识别 transparent 关键字。把它替换成 rgba(0, 0, 0, 0) 就好了。仔细看下面的截图:

img

.section-hero {
  background: linear-gradient(transparent, #d7e0ef), #527ee0;
  /*Other styles*/
}

This should instead be:

应该这样写:

.section-hero {
  background: linear-gradient(rgba(0, 0, 0,0), #d7e0ef), #527ee0;
  /*Other styles*/
}

7. 关于Grid布局中 auto-fitauto-fill 之间的区别的误解

In CSS grid, the repeat function can create a responsive column layout without requiring the use of media queries. To achieve that, use either auto-fill or auto-fit.

在 CSS 的 Grid 布局中,repeat 函数可以用来创建响应式的分栏布局,而不需要借助媒体查询来实现。要实现这种效果,可以使用 auto-fillauto-fit

.wrapper {
    grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
}

img

In short, auto-fill will arrange the columns without expanding their widths, whereas auto-fit will collapse them to zero width but only if you have empty columns. Sara Soueidan has written an excellent article on the topic.

简单点讲就是,auto-fill 在排列每一栏时不会扩展他们的宽度,而 auto-fit 则会把他们收缩成 0 宽度——如果那一栏是空元素的话。关于这个话题,Sara Soueidan 写了一篇非常不错的文章

8. 在浏览器视窗高度不够时将元素固定在顶部

If you fix an element to the top of the screen, what happens if the viewport is not tall enough? Simple: It will take up screen space, and, as a result, the vertical area available for the user to browse the website will be small and uncomfortable, which will detract from the experience.

如果你把一个元素固定在屏幕顶部,当视窗的高度不足时会发生什么?很简单:它会占用屏幕空间,而且会造成用户浏览你网站的可用垂直方向的空间变小,造成用户不适,这样就会导致体验退化。

@media (min-height: 500px) {
    .site-header {
        position: sticky;
        top: 0;
        /*other styles*/
    }
}

In the snippet above, we’re telling the browser to fix the header to the top only if the viewport’s height is equal to or greater than 500 pixels.

上面这段代码中,我们告诉浏览器只在视窗的高度大于等于 500 个像素时才将头部固定显示。

Also important: When you use position: sticky, it won’t work unless you specify the top property.

另外要注意一点:在你使用 position: sticky 的时候,如果不指定 top 属性是不会有效果的。

img

演示地址:codepen.io/shadeed/pen…

9. 设置图片的 max-width

When adding an image, define max-width: 100%, so that the image resizes when the screen is small. Otherwise, the browser will show a horizontal scroll bar.

在添加图片时,将其 max-width 设置为 100%,这样在屏幕较小时,图片的尺寸就会收缩,否则就会出现一个水平滚动栏。

img {
    max-width: 100%;
}

10. 使用 CSS Grid 布局来定义 main、aside 元素

CSS grid can be used to define the main and aside sections of a layout, which is a perfect use for grid. As a result, the aside section’s height will be equal to that of the main element, even if it’s empty.

CSS Grid 布局可以用来定义一个布局中的 main 和 aside 区域,这非常适用于 grid。不过这样的结果是,aside 区域的高度会和 main 元素保持一致,即便 aside 是空元素。

To fix this, align the aside element to the start of its parent, so that its height doesn’t expand.

要解决这个问题,将 aside 元素保持与其父元素的开始位置对齐即可,这样他的高度就不会自动扩展了。

.wrapper {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  grid-gap: 20px;
}

// align-self will tell the aside element to align itself with the start of its parent.
// align-self 可以告诉 aside 元素保持与其父元素对对齐。
aside {
  grid-column: 1 / 4;
  grid-row: 1;
  align-self: start;
}

main {
  grid-column: 4 / 13;
}

img

演示地址:codepen.io/shadeed/pen…

11. 为 SVG 添加 fill

Sometimes, while working with SVGs, fill won’t work as expected if the fill attribute has been added inline in the SVG. To solve this, either to remove the fill attribute from the SVG itself or override fill: color.

在使用 SVG 时,如果 fill 属性是内联在其上的的话,有个时候会得不到想要的效果。要解决这个问题,要么将 fill 属性从 SVG 上移出,要么用 fill: <颜色值> 来覆盖。

Take this example:

看下下面这个例子:

.some-icon {
    fill: #137cbf;
}

This won’t work if the SVG has an inline fill. It should be this instead:

如果这个 SVG 上有内联的 fill 的话,这样是没用的,应该这样写:

.some-icon path {
    fill: #137cbf;
}

12. 使用伪元素

I love to use pseudo-elements whenever I can. They provide us with a way to create fake elements, mostly for decorative purposes, without adding them to the HTML.

我喜欢尽可能地使用伪元素。他们可以用来创建虚假元素——大多数时候作为装饰效果用,且不用添加东西到 HTML 中。

When working with them, the author might forget to do one of the following:

在使用他们的时候,编写者可能经常会忘记做下面这些事:

  • add the content: "" property,

    添加 content: "" 属性。

  • set the width and height without defining the display property for it.

    设置了 widthheight ,却没有定义其 display 属性。

In the example below, we have a title with a badge as a pseudo-element. The content: "" property should be added. Also, the element should have display: inline-block set in order for the width and height to work as expected.

下面这个例子中,有一个标题,它前面的标记就是一个伪元素。需要添加 content: "" 才会有作用;另外,如要要让他的 widthheight 生效,还需要设置 display: inline-block

img

13. 当使用 display: inline-block 时,元素间隔的怪异现象

Setting two or more elements to display: inline-block or display: inline will create a tiny space between each one. The space is added because the browser is interpreting the elements as words, and so it’s adding a character space between each one.

将两个或多个元素设置为 display: inline-blockdisplay: inline 时,会在他们之间制造出一个小的空间。这个空间的出现,是因为浏览器把这些元素解释成了单词,所以会在他们之间添加一个字符的间距。

In the example below, each item has a space of 8px on the right side, but the tiny space caused by using display: inline-block is making it 12px, which is not the desired result.

下面这个例子中,每个元素的右边都拥有 8px 的空间,但如果使用的是 display: inline-block 的话,这个小空间就会变成 12px ,这并不是所期望的结果。

li:not(:last-child) {
  margin-right: 8px;
}

img

A simple fix for this is to set font-size: 0 on the parent element.

要修复这个问题,一个简单的方法就是将它父元素的 fotn-size 设置为 0

ul {
    font-size: 0;
}

li {
    font-size: 16px; /*The font size should be reassigned here because it will inherit `font-size: 0` from its parent.*/
    /* 这里之所以重新设置了字体大小,是它会集成父元素的字体大小 */
}

img

演示地址:codepen.io/shadeed/pen…

14. 为 Label 元素添加 for="ID" 属性

When working with form elements, make sure that all labelelements have an ID assigned to them. This will make them more accessible, and when they’re clicked, the associated input will get focus.

在使用表单元素时,确保所有的 label 元素都设置了指向目标表单元素 ID 的 for 属性。这可以使得表单元素更具可访问性,在点击这些 label 的时候,对应的表单元素就可以获得聚焦。

<label for="emailAddress">Email address:</label>
<input type="email" id="emailAddress">

img

15. 字体属性不会影响到交互式元素

When assigning fonts to the whole document, they won’t be applied to elements such as input, button, select and textarea. They don’t inherit by default because the browser applies the default system font to them.

如果把字体设置到整个文档上,input、button、select 及 textarea 元素并不会受影响,他们默认不会继承字体,因为浏览器对他们应用了默认的系统字体。

To fix this, assign the font property manually:

要解决这个问题,需要手动地为他们设置字体属性:

input, button, select, textarea {
  font-family: your-awesome-font-name;
}

16. 水平滚动栏

Some elements will cause a horizontal scroll bar to appear, due to the width of those elements.

某些元素会由于自身的宽度问题导致水平滚动栏的出现。

The easiest way to find the cause of this issue is to use CSS outline. Addy Osmani has shared a very handy script that can be added to the browser console to outline every element on the page.

要找出这这个问题原因,最简单的方法就是使用 CSS 的 outline 属性。Addy OSmani 分享了一段脚本,它可以直接放到浏览器的 console 中执行,以此 outline 出页面的每个元素。

[].forEach.call(?("*"), function(a) {
  a.style.outline =
    "1px solid #" + (~~(Math.random() * (1 << 24))).toString(16);
});

img

17. 图片的挤压和拉伸

When you resize an image in CSS, it could be compressed or stretched if the aspect ratio is not consistent with the width and height of the image.

在使用 CSS 来重定义图片的尺寸时,如果其长宽比与设定的不一致,就会导致其出现挤压或拉伸的效果。

The solution is simple: Use CSS’ object-fit. Its functionality is similar to that of background-size: cover for background images.

解决方法非常简单:使用 CSS 的 object-fit 就可以了。它的作用类似于针对背景图片的 background-size: cover

img {
    object-fit: cover;
}

img

Using object-fit won’t be the perfect solution in all cases. Some images need to appear without cropping or resizing, and some platforms force the user to upload or crop an image at a defined size. For example, Dribbble accepts thumbnails uploads at 800 by 600 pixels.

使用 object-fit 并不是在所有情况下都合适。有些地方可能要求图片不经裁切或缩减尺寸,另外有些平台也会强制用户上传符合规定尺寸的图片,例如,Dribble 就只接受 800 x 600 的缩略图。

18. 为 input 元素添加正确的 type 属性

Use the correct type for an input field. This will enhance the user experience in mobile browsers and make it more accessible to users.

为 input 元素添加正确的 type 属性可以增强移动端的用户体验,让用户获得更多的可访问性。

Here is some HTML:

下面是一段 HTML:

<form action="">
  <p>
    <label for="name">Full name</label>
    <input type="text" id="name">
  </p>
  <p>
    <label for="email">Email</label>
    <input type="email" id="email">
  </p>
  <p>
    <label for="phone">Phone</label>
    <input type="tel" id="phone">
  </p>
</form>

This is how each input will look once it’s focused:

下面显示的就是每个元素聚焦后的效果:

img

19. RTL 布局中的电话号码

When adding a phone number like + 972-123555777 in a right-to-left layout, the plus symbol will be positioned at the end of the number. To fix that, reassign the direction of the phone number.

当在右到左布局中添加像+ 972-123555777这样的手机号码时,加号会被置于号码的末尾。要解决这个问题,需要重新设置号码的 direction 属性。

p {
    direction: ltr; // left-to-right,左到右 ——译注
}

img