creeperyang / blog

前端博客,关注基础知识和性能优化。
MIT License
2.63k stars 211 forks source link

HTML与CSS相关问题 #12

Open creeperyang opened 9 years ago

creeperyang commented 9 years ago

今天遇到并讨论了一个CSS相关的问题,很有意思,并更正了我的某些错误概念。想以问答的形式记录下,发现只有 JavaScript问题集锦,于是干脆就新开了一个issue,以后所有的HTML与CSS相关问题都记录在这里。

creeperyang commented 9 years ago

1. position: absolute的 containing block 问题

position: absolute的元素的包含块(containing block) 总是最近定位的祖先元素(nearest positioned ancestor)所在的包含块。所谓定位,即positionrelative, absolute, fixed

如果没有最近定位的祖先,那么包含块就是 initial containing block 。问题的难点就在谁是 initial containing block

在我的(可能包括很多人)潜意识里,initial containing block显然就是<body>。可以看个例子:

<body>
<style>
    body{height: 200vh; background-color: green;}
    .demo{position: absolute; top: 0; left: 0; bottom: 0; right: 0; background-color: yellow;}
</style>
<div class="demo"></div>
</body>

可以看到,背景黄色的.demo占满整个窗口。调整body高度,.demo的高度始终不变,即跟<body>无关。所以<body>不是 initial containing block 。然后<html>是么?是(html所在的包含块):

http://www.w3.org/TR/CSS2/visudet.html#containing-block-details:

The containing block in which the root element lives is a rectangle called the initial containing block. For continuous media, it has the dimensions of the viewport and is anchored at the canvas origin; it is the page area for paged media. The 'direction' property of the initial containing block is the same as for the root element.

概念有点晦涩,就屏幕媒体而言,

initial containing block是一个长方形,尺寸和视口(窗口,window)一致,定位于画布(绘制整个文档的canvas,不要与<canvas>元素混淆)的起点。 可以看作(但不是)没有滚动时的窗口。

所以我们可以看到例子中.demo恰好铺满窗口,不论<html>/<body>怎么变动。


为什么通过position: absolute; top: 0; bottom: 0;来确认initial containing block

当同时指定绝对定位元素的topbottom,且height没有指定,或者是auto/100%,那么topbottom都会起作用,指定对包含块的上下距离。通过这个原理,我们在以上例子中,调整body高度而.demo的高度不变,确认body不是initial containing block

creeperyang commented 7 years ago

2. -webkit-overflow-scrolling

https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-overflow-scrolling

-webkit-overflow-scrolling: touch; 在触屏设备上采用基于动量(momentum-based)的滚动。直觉上就是手指离开屏幕后滚动不会立即停止,会根据你之前的滑动速度继续减速滚动一段距离,和一般的 natvie 应用上的滚动类似。

3. overflow: auto;在某些情况下总是出现滚动条(内容高度不大于容器高度)

div(class='container')
  canvas(class='content')

containercontent 的高度相同,而 container 设置 overflow: auto; ,为什么滚动条总是出现?

以下是 codepen 地址:

See the Pen mwqXPx by creeper (@creeper) on CodePen.

image

我们可以看到, 在容器和内容等高,当内容标签是 <canvas> 时,即使设定 overflow: auto; 也总是出现滚动条。

当我们设定 <canvas>display: block; 时,上述情况消失,可见,应该和 <canvas> 默认内联布局有关。

原因:

具体可查阅CSS深入理解vertical-align和line-height的基友关系

简单说,就是和默认的line-height/vertical-align值有关。默认情况下,vertical-align:baseline导致需要保留容纳descenders的距离,这就是图片下面多出的一段空白。

除了设定 <canvas>display: block; 来解决问题,也同样可以设置容器 font-size: 0; 等方式来解决。

creeperyang commented 7 years ago

4. flexbox 布局中剩余空间分配的问题

hybrid 开发中碰到的一个 flexbox 布局问题:https://codepen.io/creeper/pen/evVOQR

上中下三栏布局中,上下固定高,中间flex-grow: 1(中间块 overflow: auto,其内容较多时可滚动),结果导致上下被挤压。

原因就是 flexbox 布局中剩余空间分配的默认规则跟我们小的不太一样,我们要正确设置 flex-shrink/flex-grow/flex-basis

http://zhoon.github.io/css3/2014/08/23/flex.html 这篇文章可以一看,对剩余空间分配讲解的比较清楚:

什么是剩余空间呢?

具备flex环境的父容器,通常是有一条主轴和一条侧轴,默认情况下主轴就是水平从左向右的,侧轴是垂直从上到下的(类似书写模式)。 剩余空间就是父容器在主轴的方向上还有多少可用的空间。

<div class="container">
  <span class="B1"></span>
  <span class="B2"></span>
  <span class="B3"></span>
</div>

假设 container 高度 500px ,那么剩余高度就是 500px - B1.width - B2.width - B3.width

flex-grow

flex-grow 就是索取父容器的剩余空间,默认值是0 ,就是不索取剩余空间。

接上面的例子,假设 B1/B2/B3 的 width 都是 100px 。因为默认值是 0,那么剩余的 200px 不会被占用。

  1. 设置 B2 的 flex-grow: 1 ,那么剩余 200px 都被 B2 占用,B2 变成 300px。
  2. 设置 B1 的 flex-grow: 1 ,B2 的 flex-grow: 2 ,那么 B1 和 B2 都参与剩余空间瓜分,B1 瓜分 200px / 3 * 1,B2 瓜分 200px / 3 * 2

flex-basis

flex-basis 用于和父容器预约空间(预约剩下的归入剩余空间),默认值是 auto,即由自身内容决定空间占用(长度)。

  1. 属于 width 的替代品。如果同时设置 flex-basis 和 width ,则 width 被覆盖,即 flex-basis 的优先级更高。有一点需要注意,如果 flex-basis 和 width 其中有一个是 auto ,那么另外一个非 auto 的属性优先级会更高。

flex-shrink

讲到现在,都是容器长度超过子元素长度和(flex 默认不换行,这里先不考虑 flex-wrap),如果小于会怎么样?小于的话怎么计算剩余空间?

这里就要说到 flex-shrink 了。

flex-shrink 属性用于设置 flexbox 的收缩比率, 默认值是 1 ,即子元素长度和超过容器时默认压缩。

接上面的例子:

设置各自的长度为:
container: 500px;
B1: 300px;
B2: 160px;
B3: 120px;

则

剩余空间是  500 - 300 - 160 - 120 = -80px; 要进行压缩(假设没有设置 flex-wrap)。
因为 flex-shrink 默认 1,
则最终:
B1: 258.63px; 缩小 41.37 = 80 * (300 / 580)
B2: 137.94px; 缩小 22.06 = 80 * (160 / 580)
B3: 103.45px; 缩小 16.55 = 80 * (120 / 580)

如果我们把 B1 的 flex-shrink 设为 2,则最终:
B1: 245.45px; 缩小 54.55 = 80 * ((300 + 300) / (580 + 300))
B2: 145.45px; 缩小 22.06 = 80 * (160 / (580 + 300))
B3: 109.09px; 缩小 16.55 = 80 * (120 / (580 + 300))
creeperyang commented 7 years ago

5. CSS 规则不起效?

同事碰到这样一个问题:如下代码,chrome 查看开发工具,发现 .btn 里的样式起效了,.btn.disabled 却完全不起作用。

<button class="btn disabled"/>
.btn {
  rules
}
.btn.disabled {
  rules
}

试过各种办法,包括重写 rules 都没用。然后同事删掉所有 css 代码重写,终于没这个问题了。

所以到底什么原因?

结合以前碰到过的问题,我觉得应该是字符显示/编码问题。让同事恢复到有问题的版本,复制选择符测试:

`.btn {`.charCodeAt(4)
// 32 
`.btn.disabled {`.charCodeAt(13)
// 132

所以一切清晰了,.btn.disabled后面不是空格,虽然它视觉上是个空格。

最后一句话,有时不要相信你看到的。

creeperyang commented 6 years ago

6. <a> 标签不能嵌套

<a> 标签嵌套是非法的,详见 Are you allowed to nest a link inside of a link?

如果嵌套,浏览器将不会“正确”解析元素层级关系:外部的 <a> 会在内部 <a> 前提前闭合。 参见https://codepen.io/creeperyang/pen/LmQwzz

creeperyang commented 5 years ago

7. table 布局

等宽/等高布局是常常碰到的需求,flexbox 也可以很容易地满足需求;但在需要兼容IE9等不能使用 flexbox 的情况下,table 布局是个不错的选择(兼容 IE8)。

table-cell 天然可以等高,但等宽时需注意给 container 加一个 table-layout: fixed;

demo: https://gist.github.com/creeperyang/dcf0ad40624dec5e61441957cb061f2a

link: https://stackoverflow.com/questions/10525744/css-table-cell-equal-width

creeperyang commented 5 years ago

8. 永不过时的问题:垂直居中

各个时期垂直居中的方法不尽相同,在 flexbox 之后,我以为不会再重提这个问题,毕竟在 flexbox 下居中实在太容易了。

但没想到,很快旧事重提...

在 Android 中,

.box {
    display: flex;
    align-items: center;
    justify-content: center;
}

以上代码并不能使box内的文字垂直居中。解决方案:https://www.zhihu.com/question/39516424/answer/274374076?hb_wx_block=0

.box {
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: PingFangSC-Regular, STHeiti STXihei, Microsoft YaHei, Microsoft JhengHei, miui;
}

目前来看,不居中的原因和安卓计算字体行高的规则有关,暂时设置字体为中文字体可解决。

creeperyang commented 5 years ago

补充 line-height 相关链接:

creeperyang commented 5 years ago

9. <button> 使用 flexbox 时在 iOS 9 文字不居中

问题出在某些版本的 safari 不支持 button 作为 flexbox 布局的 container。

详情可见 https://stackoverflow.com/questions/33084810/how-is-possible-that-display-flex-and-align-items-center-do-not-work-anymo

修复方法:

display: -webkit-box;
-webkit-box-orient: horizontal;
creeperyang commented 4 years ago

10. iOS input 的 placeholder 不垂直居中

参考 https://blog.csdn.net/u012076852/article/details/52062553

可以通过使用padding,或者 line-height: 1px 来 hack。

input {
  height: 40px;
  line-height: 1px;
}
creeperyang commented 4 years ago

11. 微信或系统字体放大缩小,页面布局错乱问题

目前一个比较主流的响应式布局方案就是使用 rem,在微信中查看页面时发现布局错乱。查找原因时发现 html 的 font-size 实际值比设置的值要大,最后定位到是微信设置中放大了字体。

解决方法:

html {
    -webkit-text-size-adjust: 100% !important;
    text-size-adjust: 100%!important;
 }
creeperyang commented 4 years ago

12. 安卓中broder-radius: 50% 非完美圆(正圆)的问题

问题依然与使用 rem 有关,使用 rem 后某些尺寸下必然产生元素实际宽高的小数点问题,不同浏览器对小于1px的处理方式不同(四舍五入或直接丢弃等等),这就造成无法正圆。

解决方案,采用 transform 缩放来避免/减轻非正圆的情况:

.circle {
       width: 2rem; // 放大10倍
       height: 2rem;
       border-radius: 50%;
       transform scale(.1); // 再缩小回来
       transform-origin: center;
}
creeperyang commented 4 years ago

13. 输入框<input>使用伪元素无效

结论:replaced element 不支持伪元素。

像 img/input 这些替换元素,本身就不支持伪元素。参考:

creeperyang commented 4 years ago

14. <textarea> 随文字输入自动适应高度

https://codepen.io/vsync/pen/czgrf

原理是根据 scrollHeight 来设置 height;缺点是删除文字后不太好处理高度自动缩减的问题。