0%

浏览器默认的 margin 和 padding 不同

解决方案:加一个全局的 * {margin: 0; padding: 0;} 来统一

双边距

在 IE6 下,如果对元素设置了浮动,同时又设置了 margin-left 或 margin-right,margin 值会加倍

1
2
3
4
5
#box {
float: left;
width: 10px;
margin: 0 0 0 10px;
}

这种情况之下 IE6 会产生 20px 的距离
解决方案:在float的标签样式控制中加入 _display:inline; 将其转化为行内属性(_这个符号只有 IE6 会识别)

event 对象

IE 下,event 对象有 x、y 属性,但是没有 pageX、pageY 属性;Firefox下,event 对象有 pageX、pageY属性,但是没有 x、y 属性

Chrome 文本大小

Chrome 中文界面下默认会将小于 12px 的文本强制按照 12px 显示
可以使用 -webkit-transform: scale(0.5);
注意 -webkit-transform: scale(0.75); 收缩的是整个 span 的大小,这时候,必须要将 span 转换成块元素,可以使用 display:block/inline-block/...

hover

超链接访问过后 hover 样式就不出现了,被点击访问过的超链接样式不再具有 hover 和 active 了
解决方法:改变 CSS 属性的排列顺序 L-V-H-A,即 link > visited > hover > active

透明度

IE8 以下,没有 opacity 属性
解决方法:

1
2
3
4
div {
opacity: 0.5;
filter: alpha(opacity=50);
}

水平居中

margin: 0 auto

1
子元素块级元素,设置 `margin: 0 auto`

text-align: center

子元素行内元素,父亲设置 text-align: center 即可

水平垂直居中

负数 margin

1
2
3
4
5
6
7
8
9
div {
position: absolute;
width: 100px;
height: 150px;
top: 50%;
left: 50%;
margin-left: -50px;
margin-top: -75px;
}

margin: auto

1
2
3
4
5
6
7
8
9
10
div {
position: absolute;
width: 100px;
height: 100px;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
}

transfrom

1
2
3
4
5
6
7
8
div {
position: absolute;
width: 100px;
height: 100px;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}

flex

1
2
3
4
5
div {
display: flex;
justify-content: center;
align-items: center;
}

参考资料:
https://www.jianshu.com/p/a7552ce07c88

CSS 选择器种类

  • id 选择器 #myid
  • 类选择器 .classname
  • 标签选择器 div
  • 后代选择器 div p
  • 子选择器 div > p
  • 兄弟选择器 div ~ p
  • 相邻兄弟选择器 div + p
  • 属性选择器 a[rel="external"]
  • 伪类选择器 a:hover,li:first-child
  • 伪元素选择器 a::before,a:after
  • 通配符选择器 *

伪元素和伪类的区别

  • 伪类用于当已有的元素处于某个状态时,为其添加对应的样式,这个状态是根据用户行为而动态变化的。比如说,当用户悬停在指定的元素时,我们可以通过 :hover 来描述这个元素的状态
  • 伪元素用于创建一些不在文档树中的元素,并为其添加样式。它们允许我们为元素的某些部分设置样式。比如说,我们可以通过 ::before 来在一个元素前增加一些文本,并为这些文本添加样式。虽然用户可以看到这些文本,但是这些文本实际上不在文档树中

权重

权重大小

  • !important,权重无穷大
  • 行内样式,权重 1000
  • id 选择器,权重 100
  • 类选择器、属性选择器和伪类选择器,权重 10
  • 标签选择器和伪元素选择器,权重 1
  • 通配符,权重 0

权重作用

  • 一般来说,权重大的样式生效
  • 相同的权重,后面的规则覆盖前面的规则
  • 拥有更高权重等级的 css 权重更高。例如 11 个 class 和一个 id,利用前述的计算方法 11 个 class 的权重为 110,id 权重为 100,class 权重值更大,但是这并不成立,这种情况下之前的计算方法不再适用。

盒子模型的分类

有 3 种盒子模型:IE 盒模型(border-box)、标准盒模型(content-box)、padding-box
盒模型一般都由四部分组成:内容(content)、填充(padding)、边界(margin)、边框(border)

盒子模型之间的区别

  • 标准盒模型:width,height 只包含内容 content,不包含 border 和 padding
  • IE 盒模型:width,height 包含 content、border 和 padding
  • padding-box:width,height 包含 content、padding,不包含 border

BFC

BFC,即 block-formatting-context,块级格式化上下文。通俗来说,BFC 是一个完全独立的区域,在区域内的子元素不会影响到外面的布局。

BFC 创建方法

  • 根元素(html)
  • 浮动元素
  • 定位为 absolute 或 fixed
  • 行内块元素(display: inline-block)
  • overflow 的值不为 visible
  • flex box(display: flex 或 inline-flex)

BFC 的特征

  • 属于同一个 BFC 的两个相邻元素的 margin 会发生重叠,不同的 BFC 之间 margin 不重合(解决 margin 重叠)
  • 计算 BFC 的高度时,浮动元素也参与计算(清除浮动)
  • BFC 的区域不会与 float 元素重叠(两栏布局)

BFC 可以解决的问题

防止 margin 重叠

使用 BFC 前

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<style>
p {
color: #f55;
background: #fcc;
width: 200px;
line-height: 100px;
text-align:center;
margin: 100px;
}
</style>

<body>
<p>A</p>
<p>B</p>
</body>


因为 margin 塌陷,实际两者之间只间隔了 100 px。

使用 BFC 后

1
2
3
4
5
6
7
8
9
10
11
12
<style>
.wrap {
overflow: hidden;
}
</style>

<body>
<p>A</p>
<div class="wrap">
<p>B</p>
</div>
</body>

水平方向和嵌套同理。

清除浮动

使用 BFC 前

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<style>
.parent {
border: 5px solid #fcc;
width: 300px;
}

.child {
border: 5px solid #f66;
width: 100px;
height: 100px;
float: left;
}
</style>

<body>
<div class="parent">
<div class="child"></div>
<div class="child"></div>
</div>
</body>

使用 BFC 后

1
2
3
.parent {
overflow: hidden;
}

自适应两栏布局

使用 BFC 前

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<style>
body {
width: 300px;
position: relative;
}

.aside {
width: 100px;
height: 150px;
float: left;
background: #f66;
}

.main {
height: 200px;
background: #fcc;
}
</style>

<body>
<div class="aside"></div>
<div class="main"></div>
</body>

使用 BFC 后

1
2
3
.main {
overflow: hidden;
}

参考资料:
https://github.com/zuopf769/notebook/blob/master/fe/BFC%E5%8E%9F%E7%90%86%E5%89%96%E6%9E%90/README.md

题目链接:
https://leetcode-cn.com/problems/lru-cache/
解法分析:LRU 经典模拟题,使用双链表 + 哈希组合的方法解决。

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
/** 
* @param {number} key
* @param {number} value
*/
var DoubleLinkedListNode = function(key, value) {
this.key = key;
this.value = value;
this.prev = null;
this.next = null;
}

/**
* @param {number} capacity
*/
var LRUCache = function(capacity) {
this.capacity = capacity;
this.map = new Map();
// 通过 dummy 节点减少边界处理
this.dummyHead = new DoubleLinkedListNode(null, null);
this.dummyTail = new DoubleLinkedListNode(null, null);
this.dummyHead.next = this.dummyTail;
this.dummyTail.prev = this.dummyHead;
};

/**
* @return {boolean}
*/
LRUCache.prototype._isFull = function() {
return this.map.size === this.capacity;
}

/**
* @param {DoubleLinkedListNode}
* @return {DoubleLinkedListNode}
*/
LRUCache.prototype._removeNode = function(node) {
node.prev.next = node.next;
node.next.prev = node.prev;
// 删除节点
node.prev = null;
node.next = null;
return node;
}

/**
* @param {DoubleLinkedListNode}
*/
LRUCache.prototype._addToHead = function(node) {
const head = this.dummyHead.next;
node.next = head;
head.prev = node;
node.prev = this.dummyHead;
this.dummyHead.next = node;
}

/**
* @param {number} key
* @return {number}
*/
LRUCache.prototype.get = function(key) {
if (this.map.has(key)) {
// get 的要放到链表头
const node = this.map.get(key);
this._addToHead(this._removeNode(node));
return node.value;
} else {
return -1;
}
};

/**
* @param {number} key
* @param {number} value
* @return {void}
*/
LRUCache.prototype.put = function(key, value) {
if (this.map.has(key)) {
// 若已存在,修改值,然后将其放到链表头
const node = this.map.get(key);
node.value = value;
this._addToHead(this._removeNode(node));
} else {
if (this._isFull()) {
// 容量已满,去除尾节点,同时删除哈希表中的对应 key
const node = this.dummyTail.prev;
this.map.delete(node.key);
this._removeNode(node);
}
// 添加头节点
const node = new DoubleLinkedListNode(key, value);
this.map.set(key, node);
this._addToHead(node);
}
};

/**
* Your LRUCache object will be instantiated and called as such:
* var obj = new LRUCache(capacity)
* var param_1 = obj.get(key)
* obj.put(key,value)
*/

偶然在 b 站看到的视频,然后用 react 还原了一下,并部署到了 vercel。

仓库地址:
https://github.com/Flower-F/merry-christmas
在线体验:
https://merry-christmas-taupe.vercel.app/

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
45
46
47
48
49
50
51
52
53
54
// App.js
import './App.css';

function App() {
return (
<div className="App">
<div className="text">
Merry Christmas
</div>
<div className="tree">
<div className="iconfont icon-iconcollect-copy"></div>

<div className="top">
<span style={{ transform: 'rotateX(30deg) translateZ(86px)' }}></span>
<span style={{ transform: 'rotateY(90deg) rotateX(30deg) translateZ(86px)' }}></span>
<span style={{ transform: 'rotateY(180deg) rotateX(30deg) translateZ(86px)' }}></span>
<span style={{ transform: 'rotateY(270deg) rotateX(30deg) translateZ(86px)' }}></span>
</div>

<div className="top" style={{ transform: 'translateY(78px)' }}>
<span style={{ transform: 'rotateX(30deg) translateZ(86px)' }}></span>
<span style={{ transform: 'rotateY(90deg) rotateX(30deg) translateZ(86px)' }}></span>
<span style={{ transform: 'rotateY(180deg) rotateX(30deg) translateZ(86px)' }}></span>
<span style={{ transform: 'rotateY(270deg) rotateX(30deg) translateZ(86px)' }}></span>
</div>

<div className="top" style={{ transform: 'translateY(156px)' }}>
<span style={{ transform: 'rotateX(30deg) translateZ(86px)' }}></span>
<span style={{ transform: 'rotateY(90deg) rotateX(30deg) translateZ(86px)' }}></span>
<span style={{ transform: 'rotateY(180deg) rotateX(30deg) translateZ(86px)' }}></span>
<span style={{ transform: 'rotateY(270deg) rotateX(30deg) translateZ(86px)' }}></span>
</div>

<div className="top" style={{ transform: 'translateY(234px)' }}>
<span style={{ transform: 'rotateX(30deg) translateZ(86px)' }}></span>
<span style={{ transform: 'rotateY(90deg) rotateX(30deg) translateZ(86px)' }}></span>
<span style={{ transform: 'rotateY(180deg) rotateX(30deg) translateZ(86px)' }}></span>
<span style={{ transform: 'rotateY(270deg) rotateX(30deg) translateZ(86px)' }}></span>
</div>

<div className="bottom">
<span style={{ transform: 'translateZ(30px)' }}></span>
<span style={{ transform: 'rotateY(90deg) translateZ(30px)' }}></span>
<span style={{ transform: 'rotateY(180deg) translateZ(30px)' }}></span>
<span style={{ transform: 'rotateY(270deg) translateZ(30px)' }}></span>
</div>

<span className="shadow"></span>
</div>
</div>
);
}

export default App;
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
/* App.css */

* {
margin: 0;
padding: 0;
box-sizing: border-box;
}

.App, body {
position: relative;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
width: 100vw;
background-color: #e8ffe8;
}

.tree {
position: relative;
width: 150px;
height: 150px;
transform-style: preserve-3d;
transform: rotateX(-20deg) rotateY(30deg);
animation: movie 7s linear infinite;
}

@keyframes movie {
0% {
transform: rotateX(-20deg) rotateY(360deg);
}

100% {
transform: rotateX(-20deg) rotateY(0deg);
}
}

.text {
position: absolute;
top: 20px;
font-size: 2rem;
font-weight: bold;
color: #222;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
}

.tree .icon-iconcollect-copy {
position: absolute;
left: calc(50% - 1rem) !important;
top: -10rem !important;
color: yellow !important;
font-size: 2rem !important;
}

.tree div {
position: absolute;
top: -100px;
left: 0;
width: 100%;
height: 100%;
transform-style: preserve-3d;
}

.tree .top span {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(90deg, #69c069, #77dd77);
clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
transform-origin: bottom;
border-bottom: 10px solid #00000019;
}

.tree .bottom span {
position: absolute;
top: 350px;
left: calc(50% - 30px);
width: 60px;
height: 40%;
background: linear-gradient(90deg, #bb4622, #df7214);
/* clip-path: polygon(50% 0%, 0% 100%, 100% 100%); */
transform-origin: bottom;
border-bottom: 10px solid #00000055;
}

.tree .shadow {
position: absolute;
top: 0;
left: 0;
width: 150px;
height: 150px;
background-color: #0002;
transform-style: preserve-3d;
transform: rotateX(90deg) translateZ(-280px);
filter: blur(10px);
}

参考资料:
https://www.youtube.com/watch?v=hrv2XAY27gU

题目链接:
https://leetcode-cn.com/problems/rotate-list/
解法分析:
解法一:直接法。假设链表长度为 n。首先显然需要 k % n,然后只需要将链表的后 k 个节点移动到链表的最前面,然后将链表的后 k 个节点和前 n - k 个节点连接到一块即可。问题就转变成了找到链表的尾节点、头节点(已知)、第 n - k 个节点。

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
/**
* Definition for singly-linked list.
* class ListNode {
* val: number
* next: ListNode | null
* constructor(val?: number, next?: ListNode | null) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
* }
*/

function rotateRight(head: ListNode | null, k: number): ListNode | null {
if (head === null || k === 0) {
// 这里要返回 head,不能返回 null
// return null
return head;
}
let n = 0; // 链表长度
let tail = null; // 存储尾节点
for (let p = head; p !== null; p = p.next) {
n++;
tail = p;
}
k %= n;
let nk = head; // 第 n - k 个节点
for (let i = 0; i < n - k - 1; i++) {
nk = nk.next;
}
tail.next = head;
head = nk.next;
nk.next = null;

return head;
};

解法二:
首先思考一下如何返回链表倒数第 k 个节点。

  • 采用快慢指针
  • 快指针与慢指针都以每步一个节点的速度向后遍历
  • 快指针比慢指针先走 k 步
  • 当快指针到达终点时,慢指针正好是倒数第 k 个节点
1
2
3
4
5
6
7
let slow = (fast = head);
while (fast.next) {
if (k-- <= 0) {
slow = slow.next;
}
fast = fast.next;
}

所以解法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var rotateRight = function (head, k) {
if (!head || !head.next) return head;
let count = 0,
now = head;
while (now) {
now = now.next;
count++;
}
k = k % count;
let slow = (fast = head);
while (fast.next) {
if (k-- <= 0) {
slow = slow.next;
}
fast = fast.next;
}
fast.next = head;
let res = slow.next;
slow.next = null;
return res;
};

参考题解:

  1. https://leetcode-cn.com/problems/rotate-list/solution/xuan-zhuan-lian-biao-tu-jie-lian-biao-zu-ku33/
  2. 公众号力扣加加

螺旋矩阵 I

题目链接:
https://leetcode-cn.com/problems/spiral-matrix/

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
/**
* @param {number[][]} matrix
* @return {number[]}
*/
var spiralOrder = function(matrix) {
const m = matrix.length, n = matrix[0].length;
const visited = new Array(m).fill(false).map(() => new Array(n).fill(false));
const directions = [[0, 1], [1, 0], [0, -1], [-1, 0]];
let row = 0, col = 0;
let directionIndex = 0;
const res = [];

for (let i = 1; i <= m * n; i++) {
res.push(matrix[row][col]);
visited[row][col] = true;
const newRow = row + directions[directionIndex][0],
newCol = col + directions[directionIndex][1];
if (newRow >= m || newRow < 0 || newCol >= n || newCol < 0 || visited[newRow][newCol]) {
directionIndex = (directionIndex + 1) % 4;
}
row = row + directions[directionIndex][0];
col = col + directions[directionIndex][1];
}

return res;
};

螺旋矩阵 II

题目链接:
https://leetcode-cn.com/problems/spiral-matrix-ii/
解法分析:几乎同螺旋矩阵 I

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
function generateMatrix(n: number): number[][] {
const maxNum = n * n;
let curNum = 1;
const direction = [[0, 1], [1, 0], [0, -1], [-1, 0]]; // 顺时针
let col = 0, row = 0;
let directionIndex = 0;
const res = new Array(n).fill(0).map(() => new Array(n).fill(0));

while (curNum <= maxNum) {
res[row][col] = curNum;
curNum++;

const nextRow = row + direction[directionIndex][0];
const nextCol = col + direction[directionIndex][1];

if (!(nextRow >= 0 && nextRow < n && nextCol >= 0 && nextCol < n &&
res[nextRow][nextCol] === 0)) {
directionIndex = (directionIndex + 1) % 4; // 顺时针旋转
}

row = row + direction[directionIndex][0];
col = col + direction[directionIndex][1];
}

return res;
};

本文总结 js 遍历对象的方法。
下面是本文所用的测试对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const parentObj = {
a: 'aaaaa',
b: Symbol('bbbbb'),
c: 'ccccc'
};
const obj = Object.create(parentObj, {
d: {
value: 'ddddd',
enumerable: true
},
e: {
value: 'eeeee',
enumerable: false
},
[Symbol('f')]: {
value: 'fffff',
enumerable: true
}
});

9 种遍历方法

Object.keys(obj)

Object.keys 返回一个所有元素为字符串的数组,其元素来自于从给定的 object 上面可直接枚举的属性(不含 Symbol 属性)。

1
2
console.log('Object.keys(): ', Object.keys(obj)); 
// Object.keys(): [ 'd' ]

Object.values(obj)

Object.values() 返回一个数组,其元素是在对象上找到的可枚举属性值。

1
2
console.log('Object.values():', Object.values(obj)); 
// Object.values(): [ 'ddddd' ]

Object.entries(obj)

Object.entries() 返回一个数组,其元素是与直接在 object 上找到的可枚举属性键值对相对应的数组。

1
2
console.log('Object.entries():', Object.entries(obj)); 
// Object.entries(): [ [ 'd', 'ddddd' ] ]

Object.getOwnPropertyNames(obj)

Object.getOwnPropertyNames() 方法返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括 Symbol 值作为名称的属性)组成的数组。

1
2
console.log('Object.getOwnPropertyNames():', Object.getOwnPropertyNames(obj)); 
// Object.getOwnPropertyNames(): [ 'd', 'e' ]

Object.getOwnPropertySymbols(obj)

Object.getOwnPropertySymbols() 方法返回一个给定对象自身的所有 Symbol 属性的数组。

1
2
console.log('Object.getOwnPropertySymbols()', Object.getOwnPropertySymbols(obj));
// Object.getOwnPropertySymbols() [ Symbol(f) ]

for…in…

遍历所有可枚举的属性(包括原型上的),然后可利用 hasOwnProperty 判断对象是否包含特定的自身(非继承)属性。
注意事项:

  1. index 索引为字符串型数字,不能直接进行几何运算
  2. 遍历顺序有可能不是按照实际数组的内部顺序
  3. 会遍历数组的所有可枚举属性,包括原型
  4. for…in 更适合便利对象,不要使用 for…in 遍历数组
1
2
3
4
5
6
7
for (let key in obj) {
console.log('for in:', key);
}
// for in: d
// for in: a
// for in: b
// for in: c

for…of…

必须部署了 Iterator 接口后才能使用。使用范围:数组、Set 和 Map 结构、类数组对象(arguments、DOMNodeList对象……)、Generator 对象以及字符串。

1
2
3
4
for (let value of Object.values(obj)) {
console.log('for of:', value);
}
// for of: ddddd

forEach

使用 break 不能中断循环使用。

1
2
3
4
Object.values(obj).forEach(value => {
console.log('forEach:', value);
});
// forEach: ddddd

Reflect.ownKeys(obj)

返回一个数组,包含对象自身的所有属性,不管属性名是 Symbol 还是字符串,也不管是否可枚举。

1
2
console.log('Reflect.ownKeys():', Reflect.ownKeys(obj));
// Reflect.ownKeys(): [ 'd', 'e', Symbol(f) ]

特点总结

遍历顺序

上述遍历对象的属性时都遵循同样的属性遍历次序规则:

  1. 首先遍历所有属性名为数值的属性,按照数字排序
  2. 其次遍历所有属性名为字符串的属性,按照生成时间排序
  3. 最后遍历所有属性名为 Symbol 值的属性,按照生成时间排序

参考资料:公众号前端点线面