CSS中外边距塌陷问题及其解决方法

外边距塌陷(又叫外边距合并、外边距折叠),是指当两个块元素彼此接触时,它们的间距将取两个相邻外边界的最大值,而非两者的总和。

会发生外边距折叠的三种基本情况:
1.相邻元素之间
2.父元素与其第一个或最后一个子元素之间
3.空的块级元素

关于外边距塌陷,MDN上有更详细的说明,可供参考
MDN-外边距合并


这里我们用一个相邻元素做示例:

Example

1
2
3
4
5
// HTML代码
<div class="back">
<div class="one">1</div>
<div class="two">2</div>
</div>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// CSS代码
.back {
height: 400px;
width: 400px;
background: #fedeae;
}
.one {
height: 100px;
width: 100px;
background: pink;
margin-bottom: 50px;
}
.two {
height: 100px;
width: 100px;
background: skyblue;
margin-top: 50px;
}

可以看到,块元素1和2之间原本应该有100px的外间距,但实际结果是只有50px(由于两个margin间距值均为50px,所以取最大值就是50px了,如果一个为50px,一个为100px,则取最大值100px。)


知道问题后,就需要解决问题了,相邻元素之间margin塌陷问题的解决方法:

方法一:给靠后的快元素添加float:left/right

(或给靠后的快元素添加position:absolute/fixed)

float:定义元素在哪个方向浮动。
absolute:生成绝对定位的元素,相对于 static 定位以外的第一个父元素进行定位。
fixed:生成绝对定位的元素,相对于浏览器窗口进行定位。

方法二:给靠后的块元素添加display:inline-block

(IE6和IE7下不完全支持display:inline-block,所以如果要兼容IE6、IE7要加上*display:inline;zoom:1)
(注:以上两种方法同样适用于解决父子块元素之间的margin塌陷问题)

另外,如果是父子元素之间的margin塌陷,还可以尝试使用以下方式解决:

方法一:给父元素设置一个border

方法二:给父元素添加overflow:hidden

方法三:给父元素设定padding值


至于,空块级元素的外边距塌陷,情况要更复杂一些,而且实际应用的地方很少。以一个有背景色的块元素为参照,可以看出空块元素的外边距塌陷也是取top与bottom的最大值,但空块元素内的空间分布要更复杂一些。感兴趣的小伙伴可以自己再实践一下~

(END)

算法题-螺旋数组

题目:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
foo(1) = [[1]];
foo(2) = [ [1,2]
[4,3] ];
foo(3) = [ [7,8,9]
[6,1,2]
[5,4,3] ];
foo(4) = [ [7, 8, 9, 10]
[6, 1, 2, 11]
[5, 4, 3, 12]
[16,15,14,13] ];
foo(5) = [ [21,22,23,24,25]
[20, 7, 8, 9,10]
[19, 6, 1, 2,11]
[18, 5, 4, 3,12]
[17,16,15,14,13] ];

思路:
算法题-螺旋数组-思路

从图中foo(5)上的几个方框可以看出,foo(5)可以拆成五个部分,这五个部分代表了从foo(1)foo(5)foo(n)n每大1,外面就多一层,单数加在左上,双数加在右下。

因此我们把答案(下面的解法)分为两部分,一个函数是用来循环boo(1)(实际上是boo(2))到boo(n),另一个函数用来添加实际的数组项。

解法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
function foo(n) {
let arr = [[1]]
if (n > 1) {
// n > 1
for(let k = 2; k <= n; k++) {
arr = boo(arr, k)
}
return arr
} else {
return arr
}
}

function boo(array, n) {
if (n % 2 === 0) {
// 双数
let arr1 = []
for (let i = n ** 2;i >= n ** 2 - n + 1; i --) {
arr1.push(i)
}
array.push(arr1)
for (let j = 0; j < n - 1; j ++) {
array[j].push(n ** 2 + j - 2 * n + 2)
}
return array
} else {
// 单数
let arr1 = []
for (let i = n ** 2 - n + 1;i <= n ** 2; i++) {
arr1.push(i)
}
array.unshift(arr1)
for (let j = 1; j < n; j++) {
array[j].unshift(n ** 2 - n - j + 1)
}
return array
}
}

console.log(foo(4))
// 0: (4) [7, 8, 9, 10]
// 1: (4) [6, 1, 2, 11]
// 2: (4) [5, 4, 3, 12]
// 3: (4) [16, 15, 14, 13]


(END)

JS-判断用户当前网络状态

方法一:web的online offline事件

Online_and_offline_events | MDN

navigator.onLine | MDN
navigator.online会返回浏览器的联网状态。在线返回true,离线返回false
一旦浏览器的联网状态发生改变,该属性值也会随之变化。
当用户点击链接或者脚本进行网络请求时,如果发现浏览器连接不上互联网,则该属性会被赋值为false
(各浏览器对该属性的实现略有不同)
语法:online = window.navigator.online;

1
2
3
4
5
6
// 🌰栗子
if (navigator.onLine) {
alert('online')
} else {
alert('offline');
}

方法二:ajax请求

如果offline,我们可以catch到一个error对象。

1
2
3
4
5
6
7
8
9
10
this.$axios({
method: 'xxx',
url: 'xxx/xxx'
...
}).then((res) => {
...
}).catch((error) => {
console.log(error)
})
// Network Error: ...

CSS-span中的数字和字母不能正确折行

前段时间遇到的一个bug,当时的情况大概是这样:

图示
(代码结构很简单,我就不贴了,就是div标签里面有一个span标签。)
很明显,数字和字母都没有正确折行,整个布局都被打乱了。

解决这个问题还蛮简单的,只需要用到css的word-break属性。CSS 属性 word-break 指定了怎样在单词内断行,详情见MDN文档——word-break属性 | MDN
word-break主要有四个取值(非继承取值):

  • normal(默认)
  • break-all
  • keep-all
  • break-word
    normal是默认,就是上面图片的样子。

break-all

对于non-CJK (CJK 指中文/日文/韩文) 文本,可在任意字符间断行。

也就是说自由断行,只要该换行,不管前后直接断。

🌰栗子
图示
如果span标签中的英文/数字内容不需要考虑连接问题,那使用break-all属性就OK了。
但是,如果span里面是一些英文语句,那这么做恐怕不妥。

假如我把内容换成《I have a dream》演讲稿的其中一段,使用break-all值就会出现这种情况:

图示
很明显,左右两边边缘的单词也被断开了,这样断行显然是不规范的。

keep-all

CJK 文本不断行。 Non-CJK 文本表现同 normal

keep-all的断行规则基本上是按照空格或者回车符断行,说总是起来很抽象,让我们直接看示例:
🌰栗子
图示
上图第一个框里面的内容是连着写的,所以根本就没换行,下面的框中是演讲稿中的一段,可以看出来,是按照单词前后换行。

break-word

顾名思义,该属性从单词前后断行。
🌰栗子
图示
英文文章断的好像和keep-all没什么区别,而第一张图的断行j就看起来十分奇怪了。

(END)

CSS-两个inline-block元素出现上下错位

在布局的过程中,有时候会出现这样一种情况。被同一父级(block)包裹的几个行内块(inline-block)元素并没有水平对齐,而是上下错位,类似于:
Example
遇到这种情况,第一反应一般就是很懵逼,“WTF???我啥也没干啊?!”
其实是因为对齐的基线不同的原因,非常好解决,只需要一行代码,使用vertical-align,具体的vertical-align取什么值,由具体需要而定。
比如上面的例子:
加一条vertical-align:bottom;就OK了
Example



(END)

CSS-文字描边-webkit-text-stroke属性

之前工作中遇到需要文字描边的情况,学习到了text-stroke属性,记录总结一下。

-webkit-text-stroke CSS属性为文本字符指定了颜色。它是-webkit-text-stroke-width-webkit-text-stroke-color属性的缩写。

🌰栗子

1
2
3
4
/* Width and color values */
-webkit-text-stroke: 4px navy;
/* Global values */
-webkit-text-stroke: inherit / initial / unset;

描边过后大概酱紫

示例

如果想要实现多重描边效果,伪元素(:before + :after)多层叠加模拟。

🌰栗子

1
<p id="example" data-text="The stroke of this text is pink.">The stroke of this text is pink.</p>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<style>
#example {
font-size: 48px;
margin: 0;
-webkit-text-stroke: 2px pink;
} #example::before, #example::after {
content: attr(data-text);
position: absolute;
left: 8px;
z-index: -1;
} #example::before {
-webkit-text-stroke: 20px yellow;
} #example::after {
-webkit-text-stroke: 10px red;
}

效果是这样:
示例

解析一下上面的代码:

  • :before CSS中,::before 创建一个伪元素,其将成为匹配选中的元素的第一个子元素。常通过 content 属性来为一个元素添加修饰性的内容,默认为行内元素。 :after
  • CSS伪元素::after用来创建一个伪元素,作为已选中元素的最后一个子元素。通常会配合content属性来为该元素添加装饰内容,默认是行内元素。

所以加上伪元素后的html结构大致是这样的:

1
2
3
4
5
<p>标签
:before 文本节点
p标签的文本节点
:after 文本节点
</p>标签

设置z-index属性让伪元素置于p标签的文本节点下方(z-indexposition: absolute配合才能生效)。

其次要注意的是,由于伪元素:after在:before上层,所以:before的宽度要大于:after,不然会被覆盖。(至于为什么:after在:before上册我也没有找到靠谱的资料,我只能猜想这和css的渲染规则有关。)

另外,代码中还涉及到了data-*content的相关内容,这部分可以参阅以下资料学习。
HTML data-* Attributes | w3c
CSS content and attr

另外,还可以配合使用-webkit-background-clip-webkit-text-fill-color属性,让文字变得更有设计感。

(END)


相关资料:
-webkit-text-stroke文字描边CSS属性及展开
-webkit-text-stroke | MDN

::before | MDN
::after | MDN

CSS-将stylus样式代码转成css

一般情况下,做一个项目之前都会选一个样式预处理器,目前最常用的就是:less、sass、stylus三种,它们有助于写出更精简的样式代码。
而虽然很少出现,但出现就很糟糕的情况是,假如项目突然要舍弃预处理器,手动将stylus改成css简直是折磨,所以就要转换。
(印象中stylus的文档里有转换的方法,但是要用的时候怎么都找不到,最后在网上找到了一篇教程式的文章,里面提到了stylus转换css)

首先,我们假设有一个名为 style 的文件夹,用来放样式文件,style 里面有一个 stylus 文件夹,里面放的是 stylus 的样式文件,里面有个文件名为 test.styl

在style文件夹位置打开终端窗口,执行

1
2
$ stylus --compress stylus/
compiled stylus\test.css

带上 --compiled表示生成压缩的css文件

(END)

Js-删除数组中的某个元素

删除数组中某个元素的方法一般有两种:

  • splice
  • delete

下面来详细介绍这两种方法:

splice

  • splice()方法通过删除替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。此方法会改变原数组。
  • 语法:
    array.splice(start[, deleteCount[, item1[, item2[, _...]]]]_)
    • start:修改开始的位置(从0计数)。
      若值超出了数组的长度(length-1),则从数组末尾开始添加内容。如果是负值,则表示从数组末位开始(最后一位是-1)。如果负数的绝对值大于数组的长度,则表示从第一位开始。
    • deleteCount:整数,表示要移除的数组元素的个数。(含start位) 可选
      如果 deleteCount是 0 或者负数,则不移除元素。
    • item:要添加进数组的元素,从start 位置开始。
  • 返回值:
    由被删除的元素组成的一个数组。如果没有删除元素,则返回空数组。
    ⚠️ splice会改变原有数组

下面来看🌰栗子:

栗子

首先声明并赋值了一个test1数组,之后通过splice从第三项(index=2)开始(含第三项),删除3个元素,然后加入三个字符串。
返回的值是被删去的元素组成的数组,test1的数组更新,数组里面的元素已经发生了改变。

delete

  • delete 操作符用于删除对象的某个属性。
  • 语法:
    delete expression =>
    • delete object.property
    • delete object['property']
  • 返回值:Boolean值。
    ⚠️delete操作符会改变原有的数组或对象
    关于delete操作符的更多知识可以查看MDN | delete文档

下面来看🌰栗子:

操作对象:

对象示例1
对象示例2
首先声明一个对象a,对象a有三个键值对,删除对象aname,返回值为true,打印a发现对象已经变了。
然后删除一个不存在的atest属性,返回值依旧是true,打印a发现没有变化。

操作数组:

数组示例
首先声明并赋值一个数组test1,然后delete test1[0]删去test的第一个值,打印test1,发现index = 0empty,打印test1[0],值为undefined。

这是数组使用splice方法和delete操作符的一个区别,使用splice后数组会重排,数组内元素的index改变;而使用delete之后,数组内元素的index不变,删去的元素的位置变成了empty,打印值为undefined

(END)


关于操作数组还有以下方法:

  • push => push() 方法将一个或多个元素添加到数组的末尾,并返回该数组的新长度。
  • pop => pop()方法从数组中删除最后一个元素,并返回该元素的值。此方法更改数组的长度。
  • shift => shift() 方法从数组中删除第一个元素,并返回该元素的值。此方法更改数组的长度。
  • unshift => unshift() 方法将一个或多个元素添加到数组的开头,并返回该数组的新长度。
  • concat => concat() 方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。

Git-清理工作目录

写在前面:你需要谨慎地使用git clean这个命令,因为它被设计为从工作目录中移除未被追踪的文件。 如果你改变主意了,你也不一定能找回来那些文件的内容。相较之下,使用stash储藏或者在清理之前使用-n模拟清除是较为稳妥的方法。

下面是git clean的后缀及说明:

1
2
3
4
5
6
7
8
9
-q, --quiet           do not print names of files removed
-n, --dry-run dry run
-f, --force force
-i, --interactive interactive cleaning
-d remove whole directories(删除整个目录)
-e, --exclude <pattern>
add <pattern> to ignore rules
-x remove ignored files, too
-X remove only ignored files

  • 移除所有没有忽略的未跟踪文件:
    $ git clean -d

  • 移除所有未追踪的文件以及空的子目录:
    $ git clean -f -d
    (-f 意味着 _强制_ 或 “确定移除”)

  • 做一次移除演示:
    $ git clean -d -n
    (使用 -n 选项运行命令,这意味着 “做一次演习——将要移除什么”。)

    默认情况下,git clean 命令只会移除没有忽略的未跟踪文件。

如果想要移除.gitiignore 或其他忽略文件中的模式匹配的文件,则需要在clean后加 -x

  • 使用-i(interactive cleaning交互式清理)会以交互模式运行 clean 命令,这种方式下可以分别地检查每一个文件或者交互地指定删除的模式。也是一种比较稳妥的清理方式。

以上内容参考自:官方文档-7.3 Git 工具-储藏与清理

(END)