CSS 技巧收录

【本文会持续更新!】

1、color 影响 border-color

当只设置元素的边框宽度和样式时,边框的颜色会取当前元素 color 属性的计算值。

<div style="color: blue;border: 1px solid;">这里设置了 border,但不指定 border-color</div>

瞧瞧该实例元素的 border-color 属性的计算值:

可以看到,border-color 属性未设置值时为 initial 关键字,代表取该属性的默认值。那么,为什么这里取的是 color 属性的值而非浏览器对 border-color 的默认值(如果有的话)呢?

因为 border-color 属性的默认值就是 currentColor 关键字(CSS 3),也就是当前元素的 color 属性的计算值,详见 border-color | MDN

color 前景色

MDN 中对 color 属性是这样介绍的:

The color property sets the foreground color of an element's text content, and its decorations. It doesn't affect any other characteristic of the element

翻译过来就是,color 属性设置元素文本内容的 前景色修饰

HTML 元素的前景色包括:

  • 字体颜色,也就是狭义上的 color
  • border-color,边框的颜色;
  • outline-color,轮廓的颜色;
  • box-shadow,阴影的颜色;
  • text-shadow,文本阴影的颜色;

以及文本修饰中的 text-decoration-color,下划线的颜色。

<head>
  <style>
    p {
      color: blue;
      max-width: 500px;
    }
  </style>
</head>
<body>
  <p>这里没有做任何处理</p>
  <p style="border: 1px solid;">这里设置了 border,但不指定 border-color</p>
  <p style="outline: 1px solid;">这里设置了 outline,但不指定 outline-color</p>
  <p style="text-decoration: underline;">这里设置了 text-decoration,但不指定 text-decoration-color</p>
  <p style="box-shadow: 0 1px 2px 0;">这里设置了 box-shadow,但不指定颜色</p>
  <p style="text-shadow: 10px 10px 2px;">这里设置了 text-shadow,但不指定颜色</p>
</body>

2、移动端 H5 禁止显示系统菜单

当在移动端 H5 上长按一个目标元素时,浏览器会弹出一个关于目标元素的菜单信息。请看下面针对文本元素的实例:

<span>这是一段文字,请在移动端长按</span>

在某些场景下,我们并不希望浏览器弹出这样的菜单,应该怎么做呢?我在网上搜罗了几个常见方案:

  • 通过 -webkit-touch-callout 属性禁用 callout【无效】

    -webkit-touch-callout: none;
    

    -webkit-touch-callout 属性的兼容性非常差,之前只有在 Safari 浏览器上可用。而 现在应该是被废弃了,亲测在 ios Safari 浏览器上不起效Can I use 上也已经搜索不到了。

  • JS 屏蔽 contextmenu 事件的默认行为【无效】

    $elm.addEventListener('contextmenu', (e) => {
      e.preventDefault();
    });
    

    当用户尝试打开上下文菜单(通常是鼠标右键单击)时,contextmenu 事件会被触发,我们可以通过 preventDefault() 方法来屏蔽菜单的显示。那么这种方法是否适用于移动端 h5 呢?

    答案是否定的。不妨来看看 contextmenu 事件的兼容性:

    so,网上的部分教程就不要再误人子弟了。

  • 通过 user-select 属性让元素不可选中【有效】

    -webkit-user-select: none;
    -moz-user-select: none; 
    -ms-user-select: none;
    -o-user-select: none;
    user-select: none;
    

    在移动端,若用户不可选中元素,自然也就不能通过长按调起默认菜单了。但需要注意的是,user-select: none 在部分浏览器(如 Safari)中会使 <input><textarea> 等表单元素失效

  • JS 屏蔽 touchstart 事件的默认行为【有效】

    $elm.addEventListener('touchend', (e) => {
      e.preventDefault();
    });
    

    此方案和使用 user-select 是一样的原理,但同样 需要注意禁止默认行为所带来的负面影响,譬如作用于可滚动元素时。

所有方案的测试情况见:https://codepen.io/JunreyCen/pen/rEBYPV

总结一下,移动端的兼容性一直是非常棘手的问题,各种手机操作系统、各种浏览器应用没有一套统一的 web 标准,都喜欢 “各抒己见”。也因此,上面提及的方案在某些型号手机的浏览器中(譬如 Oppo 自带的浏览器)依然是不起作用的,这种情况下开发者只能 “见招拆招”,无招可使时也只能择 “较优解” 了。


3、文本溢出显示省略号

我们经常遇到单行文本溢出时显示省略号的场景,那多行文本的类似处理该如何实现?

单行文本

overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;      /* 文本不换行 */

多行文本

多行文本溢出时,最后一行截断并显示省略号。这里提供两种实现方式:

  • 使用 line-clamp 属性

    line-clamp 是一个不规范的属性,没有出现在 CSS 规范草案中。而且,它必须结合旧版的 flexbox(伸缩盒)模型才起作用。

    overflow: hidden;
    display: -webkit-box;           /* 旧版伸缩盒模型 */
    display: -moz-box;
    -webkit-box-orient: vertical;   /* 子元素垂直排列 */
    -moz-box-orient: vertical;
    -webkit-line-clamp: 2;          /* 块元素显示的文本行数 */
    -moz-line-clamp: 2;
    

    如下图所示,line-clamp 在兼容性方面还是有点缺陷的。

    除此之外还需要注意的是,autoprefixer 等预处理插件会自动删除一些旧的样式属性(包括 -webkit-box-orient 属性),所以使用这种方式时要对 autoprefixer 插件进行配置,详见这个 issue

  • JS + CSS 手动加省略号

    处理流程:

    1. 元素的 height 值设置为 line-height 值的整数倍n
    2. JS 获取元素的文本节点高度(scrollHeight),若高度\geq n+1line-height 值则视为文本溢出;
    3. 文本溢出时,于元素右下方显示省略号,否则不显示;
    <style>
    #ellipsis {
      position: relative;
      display: block;
      overflow: hidden;
      line-height: 20px;
      height: 40px;           /* height 为整数倍 line-height */
      word-break: break-all;  /* 使文本填充满容器,利于省略号和文本的衔接 */
    }
    .show-ellipsis {
      padding-right: 12px;    /* 给省略号腾出空间 */
    }
    .show-ellipsis:after {
      content: '...';
      position: absolute;
      right: 0;
      bottom: 0;
    } 
    </style>
    
    <p id="ellipsis">JS 和 CSS 设置多行文本字数超出时最后一行显示省略号,JS 和 CSS 设置多行文本字数超出时最后一行显示省略号</p>
    
    <script>
      const $elm = document.getElementById('js-ellipsis');
      const style = window.getComputedStyle($elm);
      const limitHeight = +style.height.replace('px', '') + +style.lineHeight.replace('px', '');
      if ($elm.scrollHeight >= limitHeight) {
        // 文本溢出则显示省略号
        $elm.className = 'show-ellipsis';
      }
    </script>
    

    这种实现方式基本不存在兼容性问题(除了低版本 IE 浏览器),但效果肯定比不上 line-clamp 的方式,只能静待 W3C 制订一套规范的处理方案了。


4、图像自适应

我们写页面的时候经常会遇到,获取的图片尺寸与我们所期待的渲染尺寸不符。譬如在渲染用户头像的场景下,我们往往希望用户上传的图片都是统一的正方形尺寸,然而用户给的可能是这样的:

常见的处理方式是 适当的剪裁使图片填满容器而不被拉伸,我们可以利用背景图(background-size / background-position)来实现:

<style>
.avatar {
  width: 200px;
  height: 200px;
  border: 2px solid #07C160;
  background: url('./images/captain.jpeg') no-repeat;
  background-size: cover;
  background-position: center; 
}
</style>
<div class="avatar"></div>

CSS 3 中提供了新的属性:object-fit,可以对 <img> 标签作宽高自适应处理,效果类似于 background-size 属性。

object-fit 提供了5个取值:

  • none:内容保持原有尺寸;
  • contain:内容保持宽高比地缩放,内容和容器的宽高比不匹配时会 留下白边
  • cover:内容保持宽高比地填满容器,内容和容器的宽高比不匹配时会 被裁剪
  • fill:内容刚好填满容器,内容和容器的宽高比不匹配时会 被拉伸
  • scale-down:内容尺寸和 nonecontain 中的一个相同,最终会显示尺寸较小的那个;

background-size 和 object-fit 的效果对比

同样的,background-positionobject-position 的作用类似,比较明显的区别在于:

background-position 的默认值是 0% 0%,而 object-position 的默认值是 50% 50%

object-fit 的意义

  • 解放了 background-image 的能力;
    譬如可以同时利用 background-image<img> 标签完成图片的堆叠等。
  • object-fit 对所有 可替换元素 都有效;
    常见的可替换元素包括:<iframe><video><embed><img><input type="image">

兼容性

额,如果要兼容 IE 浏览器的话,还是乖乖用背景图的方式把。


5、块元素等比缩放

现在的网页开发都讲究响应式设计,以使在各种尺寸的设备上也能保持良好的 UI 呈现。在响应式布局中,我们经常需要实现随网页视窗宽度动态变化的元素,尤其是支持等比缩放的元素。

比较传统的做法,是通过 JS 监听 resize 事件,动态获取容器的宽度然后调整元素的宽高。这里提供一种纯 CSS 实现的方式,主要利用的是 padding 属性的百分比取值

padding 属性的百分比取值是相对于其包含块的宽度。

所以,当包含块的宽度发生变化时,子元素的 padding 属性的计算值会随之发生变化,也就是说子元素的总宽高都会被改变,且与包含块宽度成正比。

<style>
.wrapper {
  width: 200px;
  height: 300px;
  border: 2px solid #07C160;
}
.content {
  padding: 50%;
  width: 0;
  height: 0;
  background-color: #FA5151;
}
</style>

<div class="wrapper">
  <div class="content"></div>
</div>

接下来的工作,只需要让目标元素的宽高参考于 padding 子元素的宽高,就完成了元素的等比缩放。我们可以利用 绝对定位 来实现。

这里实现一个宽高比为 1:2、宽度保持为容器\frac{1}{2}的等比缩放元素:

<style>
.wrapper {
  width: 20%;
  height: 40%;
  border: 2px solid #07C160;
}
.container {
  position: relative;
  padding: 50%;
  width: 0;
  height: 0;
}
.content {
  position: absolute;
  top: 0;
  left: 0;
  width: 50%;
  height: 100%;
  background-color: #FA5151;
}
</style>

<div class="wrapper">
  <div class="container">
    <div class="content"></div>
  </div>
</div>

为了突出演示效果,稍微 “修饰” 了一下:

需要体验 Demo 的请跳转:https://codepen.io/JunreyCen/pen/agzoyG


6、垂直外边距合并

当两个垂直外边距邻接时,会合并成一个外边距。只有普通文档流中块元素的垂直外边距才会发生合并,不在同一个 BFC 内(譬如行内元素、浮动元素、绝对定位等)的垂直外边距不会合并,水平方向的外边距也不会合并

通常发生外边距合并的场景有:

  • 相邻的两个块元素,邻接的上/下外边距发生合并

    图片摘自 http://www.w3school.com.cn/css/css_margin_collapsing.asp

    发生合并时外边距的计算规则:

    • 两个外边距都是正数时,取两者中的较大值;
    • 两个外边距一正一负时,取两者之和;
    • 两个外边距都是负数时,比较两者的绝对值大小,谁大取谁;
  • 无内边距(padding)和边框(border)的父元素的垂直外边距会与子元素的垂直外边距合并

    图片摘自 http://www.w3school.com.cn/css/css_margin_collapsing.asp
  • 无内边距(padding)、边框(border)和内容(content)的空元素的上/下外边距会合并

    图片摘自 http://www.w3school.com.cn/css/css_margin_collapsing.asp

这里提供一个完整 Demo:https://codepen.io/JunreyCen/pen/zVxPWX


7、行内(块)元素空隙

试试执行这段代码:

<img src="./images/captain.jpeg" width="200">
<img src="./images/captain.jpeg" width="200">

奇怪,两张图片之间出现了一道缝隙……

这是因为 img 元素默认会被渲染成行内元素(display: inline;),而上述代码中两个 <img> 之间其实是存在一个 换行符 (以及若干个 制表符)的,这些都会被渲染成一个空白格,也就导致了缝隙的产生。

解决办法有俩:

  • 写 HTML 代码的时候避免行内元素间的空格、换行符等特殊字符;
    <img src="./images/captain.jpeg" width="200"><img src="./images/captain.jpeg" width="200">
    
  • 把行内元素所在行的字体大小设为 font-size: 0;
    <style>
    body { font-size: 0; }
    </style>
    
    <img src="./images/captain.jpeg" width="200">
    <img src="./images/captain.jpeg" width="200">
    

PS:行内块元素(display: inline-block)也会存在同样的问题。


8、pointer-events: none;

pointer-events 属性可以指定元素是否可以成为鼠标事件的 target ,通俗点讲就是该元素是否可以接收鼠标事件。

pointer-events 属性有多种取值,详见 MDN。这里着重介绍取值 none

pointer-events: none; 指定元素及其后代元素不会成为鼠标事件的 target,父元素不受影响。

实验 Demo

实验内容:三层 DOM 元素都监听了鼠标点击事件,其中 target 节点设置了 pointer-events: none,点击 child 元素,看看有哪层元素可以响应点击事件。

<div class="parent" onclick="alert('parent')">
  <div class="target" onclick="alert('target')">
    <div class="child" onclick="alert('child')"></div>
  </div>
</div>

左边不作处理的实例会 alert 三次;而右边的实例只会 alert 一次,内容为 parent

应用场景

  • 同层元素点击穿透

    我们在写 UI 基础组件时,多多少少会接触到 Field 输入框组件。比如下面的实例,需求是满足 0.5px 边框 + input 输入框

    <style>
    .field {
      position: relative;
      width: 250px;
      height: 50px;
      text-align: center;
    }
    .field:after {
      position: absolute;
      top: -50%;
      left: -50%;
      right: -50%;
      bottom: -50%;
      content: '';
      border: 1px solid #ccc;
      transform: scale(0.5);
    }
    </style>
    
    <div class="field">
      <input type="text" placeholder="请输入">
    </div>
    

    你会发现,左边的输入框无法聚焦。这其实跟 0.5px 边框的实现方式有关,实例中利用伪元素 :after 制造了一个2倍尺寸的子元素,然后通过 transform: scale(0.5) 缩放,从而实现 0.5px 边框。然而,**这个伪元素和 input 输入框属于同层元素,根据渲染的先后顺序伪元素是层叠于输入框之上的,所以鼠标点击事件是无法被输入框捕获的。

    我们只需要给伪元素设置 pointer-events: none;,使其无法成为鼠标事件的 target,input 输入框就可以成功被聚焦。

  • 阻止 :hover、:active 等鼠标行为状态的触发

    设置了 pointer-events: none; 的元素无法响应鼠标事件,自然也就无法触发相关的状态了。

想体验实例的请访问:https://codepen.io/JunreyCen/pen/NZqKOx


9、:first-child 和 :first-of-type 的区别

  • :first-child
    匹配其父元素的符合特定类型的首个子元素。条件更为苛刻,需要满足 首个子元素 + 符合特定类型

  • :first-of-type
    匹配其父元素的符合特定类型的第一个元素。条件较为宽松,在满足 符合特定类型 的范畴下寻找第一个元素即可。

<style>
  .group-1 h2:first-child,
  .group-1 h3:first-child,
  .group-2 h2:first-of-type,
  .group-2 h3:first-of-type {
    color: #FA5151;
  }
</style>

<div class="group-1">
  <h2>父元素的第一个元素,第一个 h2 元素</h2>
  <h3>父元素的第二个元素,第一个 h3 元素</h3>
</div>
<div class="group-2">
  <h2>父元素的第一个元素,第一个 h2 元素</h2>
  <h3>父元素的第二个元素,第一个 h3 元素</h3>
</div>

10、当心分号 ;

我们都知道,在样式表的声明块({...})内,样式声明之间会用分号隔开,这是因为引擎在解析时,每条声明之间的空格(包括换行符等)会被忽略:

<style>
div {
  color: red;
  font-size: 20px;
}
</style>
<div>content</div>

那如果,分号写在声明块之外呢?

这相当于告诉引擎,样式表解析到这里就结束了,后面的东西就不用管了。来看看这个 demo:

<style>
div {color: red};
.ctn-1 {color: blue}
.ctn-2 {font-weight: 600}
</style>
<div id="ctn-1">蓝色字体</div>
<div id="ctn-2">加粗字体</div>

遇到类似的面试题时,就要注意仔细看别被 “坑” 了~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容

  • 问答题47 /72 常见浏览器兼容性问题与解决方案? 参考答案 (1)浏览器兼容问题一:不同浏览器的标签默认的外补...
    _Yfling阅读 13,737评论 1 92
  • 学习CSS的最佳网站没有之一 http://www.w3school.com.cn/tags/index.asp ...
    Amyyy_阅读 1,033评论 0 1
  • 1.块级元素和行内元素 块级(block-level)元素;行内(内联、inline-level)元素。 块元素的...
    饥人谷_小侯阅读 1,991评论 1 4
  • 第一部分 HTML&CSS整理答案 1. 什么是HTML5? 答:HTML5是最新的HTML标准。 注意:讲述HT...
    kismetajun阅读 27,450评论 1 45
  • CSS 指层叠样式表(Cascading Style Sheets),是一种用来为结构化文档(如 HTML 文档或...
    神齐阅读 2,087评论 0 14