css看这篇就够了

概述

这篇文章里面我不想讲一些概念性的东西,这些概念性的知识是必要的,但是也是可以很容易在往上搜索到的,需要的同学自己可以在往上查找,我希望讲述一些我自己内化的知识,这些知识在实际工作中用的比较多,也可以作为一个手册,有需要可以随时查找。

范围

这篇文档讲述的内容暂时不会包括移动端的 css 技术,说实话移动端的我了解的也比较少,主要平时用的比较少。另外,老版本 IE 的兼容性问题也不会被提及,因为这些陈旧的知识,慢慢会被舍弃掉,平时用的也不多。

基础部分

盒模型

盒模型

1
box-sizing: content-box|border-box|inherit;
属性类型 可选值 说明
box-sizing content-box width 仅仅包含内容,不包含 borderpadding
box-sizing border-box width 包含 borderpadding

选择器

Demo

选择器类型 举例 权级 权重
元素选择器 p / span 1 1
id 选择器 #id 3 100
类选择器 .class 2 10
通用选择器 * {} 0 0
组合选择器 1. 后代;2. 子带;3. 兄弟选择器;4. 交集选择器;5. 并集选择器 - -
伪类选择器 span:hover{} 2 -
属性选择器 span[class=”aa”] / a[href] 2
!important - 5 10000
内联样式 - 4 1000

需要补充的是:

  1. 权值可以相加,权级不可跃迁,比如 11 个 class 选择器 也小于 1 个 id 选择器
  2. 组合选择器可能是元素选择器,类选择器,id 选择器,因此要具体情况具体分析
  3. 权值相同时,以最后面的生效
  4. 权值不同时,权重高的生效

块元素和行内元素

块元素

  1. 默认情况下,块元素是独占一行的。
  2. 常见的块元素:div, p, h1-h6, table, hr, ul, ol, form
  3. 高度,行高,内外边距均可设置
  4. 宽度缺省是容器宽度的 100%
  5. 可以容纳内联元素和其他块元素

行内元素

也叫内嵌元素或者内联元素。

  1. 默认情况下,现在在一行,一行显示不下,才会换行显示。
  2. 任何不是块级元素的都是行内元素,比如:a, span, label, strong
  3. 宽高,行高,内外边距均不可设置;
  4. 宽度就是它容纳的文本或者图片的宽度,不可改变;
  5. 行内元素只能容纳其他行内元素或者文本;

当然如果把行内元素转化为块元素(display: inline-block or block),则该行内元素具有块元素的一切特征。

标准文档流

  1. 将窗体自上而下分成一行一行,并在每行中按从左至右的顺序排放元素,即为标准文档流。
  2. 每个非浮动块级元素都独占一行,浮动元素则按规定浮在行的一端。若当前行放不下,则再另起一行浮动。
  3. 内联元素不会独占一行,除非一行摆不下,才会另起一行
  4. 浮动,绝对定位,固定定位,这三种情况将使元素脱离标准文档流
  5. 浮动元素不占任何正常标准文档流空间,而浮动元素的定位还是基于正常的文档流,然后从文档流中抽出并尽可能远的移动至左侧或者右侧。文字内容会围绕在浮动元素周围。当一个元素从正常文档流中抽出后,仍然在文档流中的其他元素将忽略该元素并填补他原先的空间
  6. 相对定位(position: relative)并不会脱离文档流,只是相对于原位置进行偏移,但仍保留原位置。

浮动和定位

css 布局有三种机制 – 普通流,浮动,定位,下面讲一下浮动和定位。

浮动(float)

浮动例子[demo](float - JSRUN.NET)

1
2
float = left | right | none | inherit
clear = left | right | none | inherit

浮动的特点:

  1. 脱离标准流,不占位置(自己原来的位子漏给后面的标准流)
  2. 浮动只能往左或者往右移动,不能上下浮动,直到它的边缘碰到包含框或者另一个浮动元素为止,如果父亲元素装不下了,默认另起一行显示,浮动元素之间默认无缝隙。
  3. 任何元素都可以添加浮动,无论它是块元素还是行内元素,浮动之后可以直接设置宽高,默认行为类似于行内块特性,不需要 display 转换。

浮动的使用技巧:一般一个标准流的父亲,里面一个孩子浮动了,其他的孩子也都浮动了。

浮动元素于父元素的关系:

  1. 浮动的元素压不住父元素的边框;
  2. 浮动的元素压不住父元素的 padding;
  3. 定位可以随便压;

清除浮动的理解:

  1. 清除浮动不是真的把浮动清除,而是清除加了浮动带给后面的标准流的影响;
  2. 清除浮动是针对标准流元素的,清除的是浮动元素对自身的影响;
  3. 清除浮动意味着,清除那一侧的元素不再具有浮动效果(实际还是有浮动的);

什么时候清除浮动?

  1. 父亲没有高度;
  2. 子元素有浮动;
  3. 因为浮动影响了后面的布局;

定位

Demo例子

1
position = relative | absolute | fixed | static
  1. 相对定位(position: relative): 不会脱离文档流,不影响其他元素的布局,只是基于原位置偏移了一定的位置,并保留原来占的位置。
  2. 绝对定位(position: absolute): 会脱离文档流,会影响其他元素的布局,元素原先在标准文档流中所占的空间会关闭,就好像该元素原本不存在一样, 相对于离它最近的父元素带有相对定位或者绝对定位的元素做偏移。
  3. 固定定位(position: fixed): 会脱离文档流,以浏览器的可视窗口为基准进行偏移,跟父元素没有任何关系,不随滚动条滚动。

浮动和定位的使用的场景:

  1. 浮动会脱离文档流,但是也会影响其后面的元素排布,因此如果是需要方便的布局,选择绝对定位比较好,浮动一般用于导航条等横式布局中。

  2. 父元素内部是浮动元素的,如果需要撑满父元素,有几种方式:

    1. 父元素设置伪元素清除浮动
    2. 父元素内部加入一个清除浮动的元素
    3. 父元素设置 overflow
  3. 父元素内部是绝对定位元素的,父元素的高度是撑不开的,如果需要撑开,需要通过 javascript 计算父元素的高度

弹性盒子布局(flex)

Flex 布局是 css3 新加入的布局方案,可以简便,完整,响应式地实现各种页面布局。目前,它已经得到了所有浏览器的支持。Flex 布局将成为未来布局的首选方案。

1
display: flex | (inline - flex)

注意:设置 Flex 布局后,子元素的float,clear,vertical-align属性将失效。

对着上图解释一下 flex 布局的大致情况:

  1. 采用 flex 布局的元素,称为容器,它的所有子元素自动成为容器的成员,称为项目。
  2. 容器默认存在两个轴,主轴(main axis)和侧轴(cross axis),两个轴互相垂直。主轴的开始位置(与边框的交叉点,左侧或者上侧)叫做main start ,结束位置叫做main end; 侧轴的开始位置叫做cross start,结束位置叫做cross end
  3. 项目默认沿主轴排列,单个项目占据的主轴空间叫做main size, 占据的侧轴空间叫做cross size
容器的属性
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
// 定义了主轴的方向:
// 水平(起点在左端)| 水平(起点在右端)| 垂直(起点上端)| 垂直(起点下沿)
flex-direction: row(默认) | row-reverse | column | column-reverse;
// 定义了项目在主轴上排不下,应该如何显示
// 不换行 | 换行 | 换行(行序置换)
flex-wrap: nowrap(默认) | wrap | wrap-reverse;
// flex-direction 和 flex-wrap的简写,比如 row nowrap
flex-flow: <flex-direction> <flex-wrap>;
// 定义了项目在主轴上的对齐方式。
// flex-start: 左对齐(第一个项目紧靠左边框)
// flex-end: 右对齐(最后一个项目紧靠左边框)
// center: 居中
// space-between: 两端对齐(紧靠边框),项目之间的间隔都相等
// space-around: 每个项目两侧的间隔相等,所以项目之间的间隔比项目与边框的间隔大一倍
justify-content: flex-start(默认值)|flex-end|center|space-between|space-around;
// 定义了项目在侧轴上的对齐方式。
// flex-start: 侧轴起点对齐(所有项目上沿紧靠上边框)
// flex-end: 侧轴终点对齐(所有项目下沿紧靠下边框)
// center: 居中
// baseline: 以所有项目的第一行文字作为基线对齐
// stretch: 如果项目未设置高度或者设置为auto,则将占满整个容器的高度
align-items: flex-start|flex-end|center|baseline|stretch(默认值);
// 定义了多跟轴线的对齐方式。如果项目只有一根轴线,则该属性不起作用。
// flex-start: 与侧轴的起点对齐,最上面一根轴线紧靠上边框
// flex-end: 与侧轴的终点对齐,最下面一根轴线紧靠下边框
// center: 与侧轴的中点对齐
// space-between: 与侧轴两端对齐,轴线之间间隔均分
// space-around: 每根轴线两侧间隔都相等,所以,轴线之间的间隔比轴线与边框的间隔大一倍。
// stretch: 轴线占满整个交叉轴
align-content: flex-start|flex-end|center|space-between|space-around|stretch(默认)

项目的属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 定义了项目的排列顺序,数值越小,排列越靠前,默认为0
order: <integer>;
// 定义了项目的放大比例,默认0,即如果存在剩余空间,也不放大
// 如果所有项目的flex-grow的属性都为1,则他们将等分剩余空间(如果有的话)。
// 如果一个项目的flex-grow的属性都为2,其他项目都为1,则前者占据的剩余空间将比其他项目多1倍。
flex-grow: <number>;
// 定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小
// 如果所有项目的flex-shrink都为1,则当空间不足时,都将等比例缩小。
// 如果一个项目的flex-shrink属性为0,其他项目都为1,则空间不足时,前者不缩小。
flex-shrink: <number>; // 默认 1
// 定义了项目的宽度,如果同时定义了width, width属性无效,默认值是auto。
// 默认为auto时,width还是有效的。
flex-basic: <length> | auto;
// 是flex-grow,flex-shrink,flex-basis的简写,默认值是0 1 auto。后两个属性可选。
// 建议优化使用这个属性,而不是单独写三个分离的属性,因为浏览器会推断相关值
// 该属性有两个快捷值:auto(1 1 auto) 和 none(0 0 auto)
flex: number | [<flex-grow> <flex-shrink>? || <flex-basis>];
// 该属性允许定义单个项目有与其他项目不一样的对齐方式,可覆盖align-items属性。默认值为auto,表示继承父元素的align-items属性。
align-self: auto | flex-start | flex-end | center | baseline | stretch;

关于 flex 的几点理解:

  1. 容器设为 flex 后,项目可以设置宽高,无论项目是否是行内元素还是块级元素
  2. flex-basis 针对于主轴上的宽或者高,如果设置了值,则主轴对应的宽和高设置会失效
  3. 主轴是水平方向时,项目如果设置了高度,则容器设置 align-items: stretch 也不会生效;反之主轴是垂直方向也是类似的,项目如果设置了宽度,则容器设置了 align-items:stretch 也不会生效。

上一篇的姊妹篇-谈谈对象的offsetX,clientX,pageX,screenX,layerX,x的区别

今天来讲一下offsetX,clientX,pageX,screenX,layerX,x的区别,以及在chrome,firefox,edge浏览器中的相同和不同点

下面先上一段代码:

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
102
103
104
105
106
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>offsetX,clientX,pageX,screenX,layerX,x</title>
<style>
body {
height: 1600px;
margin: 20px;
padding: 20px;
border: 20px solid #f38;
background: #ccc;
overflow: auto;
}
.main {
width: 400px;
height: 330px;
position: relative;
margin: 250px auto 0;
background: #eee;
overflow: auto;
border: 10px solid #f38;
}
.box {
width: 220px;
height: 420px;
position: absolute;
left: 50px;
top: 50px;
background: orange;
margin: 20px;
padding: 20px;
border: 10px solid #ccc;
}
.show {
position: fixed;
right: 10px;
top: 10px;
}
.show > label,
.show > span {
display: table-cell;
}
.show > label {
width: 150px;
text-align: right;
padding-right: 10px;
}
.show > span {
width: 100px;
text-align: left;
}

.show2 {
top: 270px;
}

</style>
</head>
<body>
<div class="main">
<div class="box" id="box"></div>
</div>
<div class="show">
<label>offsetX:</label><span id="offsetx"></span><br>
<label>clientX:</label><span id="clientx"></span><br>
<label>pageX:</label><span id="pagex"></span><br>
<label>screenX:</label><span id="screenx"></span><br>
<label>layerX:</label><span id="layerx"></span><br>
<label>x:</label><span id="x"></span>
</div>

<div class="show show2">
<label>offsetY:</label><span id="offsety"></span><br>
<label>clientY:</label><span id="clienty"></span><br>
<label>pageY:</label><span id="pagey"></span><br>
<label>screenY:</label><span id="screeny"></span><br>
<label>layerY:</label><span id="layery"></span><br>
<label>y:</label><span id="y"></span>
</div>

<script type="text/javascript">
var $id = function (id) {
return document.getElementById(id)
}
window.onload = function () {
$id('box').onmousedown = function (event) {
var e = event || window.event
$id('offsetx').innerText = e.offsetX
$id('clientx').innerText = e.clientX
$id('pagex').innerText = e.pageX
$id('screenx').innerText = e.screenX
$id('layerx').innerText = e.layerX
$id('x').innerText = e.x

$id('offsety').innerText = e.offsetY
$id('clienty').innerText = e.clientY
$id('pagey').innerText = e.pageY
$id('screeny').innerText = e.screenY
$id('layery').innerText = e.layerY
$id('y').innerText = e.y
}
}
</script>
</body>
</html>

分析结果

Chrome

offsetX — 点击位置相对padding左上角的距离,因此如果存在border,点击在border上为负值,点击在padding内为正值
clientX — 相对于浏览器左侧可视区域的距离
pageX — 相对于浏览器左侧页面的滚动距离 = clientX + 滚动条滚动距离, 因此在没有滚动条的情况下等于clientX,有滚动条的时候也可能大于screenX
screenX — 相对于显示屏左侧的距离
layerX — 点击位置相对于border左上角的距离,因此如果存在border,其值等于offsetX + border-left
x — 等于clientX

Firefox

offsetX — 与chrome一致
clientX — 与chrome一致
pageX — 与chrome一致
screenX — 与chrome一致
layerX — 与chrome一致
x — 与chrome一致

Edge

offsetX — 与chrome一致
clientX — 与chrome一致
pageX — 与chrome一致
screenX — 与chrome一致
layerX — 这个值与chrome和firefox行为不一致,其值相对于父容器的border左上角的距离
x — 与chrome一致

注意

没有scrollX,scrollY属性

谈谈clientXXX, offsetXXX, scrollXXX的用法

今天我想来探索一下几个经常搞混淆的概念,比如:

  1. clientWidth, offsetWidth, scrollWidth有什么区别?

  2. clientHeight, offsetHeight, scrollHeight有什么区别?

  3. clientTop, offsetTop, scrollTop有什么区别?

  4. clientLeft, offsetLeft, scrollLeft有什么区别?

  5. 上面这些属性和style.width, style.height, style.left, style.top有什么区别?

  6. div.clientXXXdocument.body.clientXXX 有没有区别?

  7. 不同浏览器之间有没有什么不同?(主要是chrome,firefox,Edge)

要解答以上问题,最直接的方式是写一个demo测试一下,下面放出测试代码。

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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>clientTop, offsetTop, srcollTop各属性介绍</title>
<style type="text/css">
html, body {
/*width: 100%;
height: 100%;*/
}
html {
margin: 0px;
padding: 0px;
}
body {
border: 10px solid #cc9999;
padding: 10px;
margin: 10px;
background-color: #ffffcc;
}
.wrap {
position: relative;
margin: 10px;
padding: 10px;
border: 10px solid #333399;

}
.div {
position: relative;
width: 300px;
height: 200px;
overflow: auto;
top: 20px;
left: 20px;
border: 10px solid #ff6666;
margin: 10px;
padding: 10px;
background-color: #ccffff;
/*box-sizing: border-box;*/
}
.div > pre,
.div > p {
margin: 0px;
}
.divide {
margin-top: 50px;
margin-bottom: 20px;
}
.divide > div {
width: 90%;
height: 1px;
border: 0px;
margin: 0 auto;
border-bottom: 1px dashed #555;
}
.result {
display: inline-block;
width: 400px;
vertical-align: top;
}
</style>
<script type="text/javascript">
var $$ = function (id) {
return document.querySelector('#' + id)
}

var calc = function () {
var body = document.body
// ---------------------- body --------------------------
$$('bodyClientWidth').innerText = body.clientWidth
$$('bodyOffsetWidth').innerText = body.offsetWidth
$$('bodyScrollWidth').innerText = body.scrollWidth

$$('bodyClientHeight').innerText = body.clientHeight
$$('bodyOffsetHeight').innerText = body.offsetHeight
$$('bodyScrollHeight').innerText = body.scrollHeight

$$('bodyStyleTop').innerText = body.style.top // 必须定义在行内样式中,否则取不到这个值
$$('bodyStyleLeft').innerText = body.style.left // 必须定义在行内样式中,否则取不到这个值

$$('bodyClientLeft').innerText = body.clientLeft
$$('bodyClientTop').innerText = body.clientTop

$$('bodyOffsetLeft').innerText = body.offsetLeft
$$('bodyOffsetTop').innerText = body.offsetTop

$$('bodyScrollLeft').innerText = body.scrollLeft
$$('bodyScrollTop').innerText = body.scrollTop

$$('bodyStylePadding').innerText = body.style.padding // 必须定义在行内样式中,否则取不到这个值
$$('bodyStyleMargin').innerText = body.style.margin // 必须定义在行内样式中,否则取不到这个值
$$('bodyStyleWidth').innerText = body.style.width // 必须定义在行内样式中,否则取不到这个值
$$('bodyStyleHeight').innerText = body.style.height // 必须定义在行内样式中,否则取不到这个值



// ------------------- div --------------------------
$$('clientWidth').innerText = $$('div').clientWidth // Chrome: 内容可视宽度,值不等于style.width,包括内容区域和padding,不包括border; Firefox: 和chrome一致
$$('offsetWidth').innerText = $$('div').offsetWidth // Chrome: 在clientWidth的基础上 + border水平宽度;firefox和chrome一致
$$('scrollWidth').innerText = $$('div').scrollWidth // Chrome: 整个滚动内容区宽度 = 内容可视宽度 + scrollLeft(滚动条滑到最右边时); Firefox: 和chrome一致
$ && $('#jqWidth').text($('#div').width()) // 值是数值,等于style.width的值,
$ && $('#jqOuterWidth').text($('#div').outerWidth()) // 值是数值,等于div.offsetWidth的值

$$('clientHeight').innerText = $$('div').clientHeight // Chrome: 内容可视高度,值不等于style.height,包括内容区域和padding,不包括border; Firefox: 和chrome一致
$$('offsetHeight').innerText = $$('div').offsetHeight // Chrome: 在clientHeight的基础上 + border水平高度;firefox和chrome一致
$$('scrollHeight').innerText = $$('div').scrollHeight // Chrome: 整个滚动内容区宽度 = 内容可视高度 + scrollTop(滚动条滑到最底部时); Firefox: 和chrome一致
$ && $('#jqHeight').text($('#div').height()) // 值是数值,等于style.height的值,
$ && $('#jqOuterHeight').text($('#div').outerHeight()) // 值是数值,等于div.offsetHeight的值

$$('styleTop').innerText = $$('div').style.top // 必须定义在行内样式中,否则取不到这个值
$$('styleLeft').innerText = $$('div').style.left // 必须定义在行内样式中,否则取不到这个值

$$('clientLeft').innerText = $$('div').clientLeft // Chrome: 左侧border宽度; Firefox: 和chrome一致
$$('clientTop').innerText = $$('div').clientTop // Chrome: 上侧border宽度; Firefox: 和chrome一致
// 与当前节点的offsetParent有关
// Chrome: 当offsetParent 为 Body 时,此时值为 :
// Body的margin + border + padding + 当前节点的margin + left(当前节点如果定位了包括relative, absolute,否则,不需要这个值)
// 当offsetParent 为其它元素时,以这个元素为基准,此时值为:
// 当前offsetParent元素的padding + 当前节点的margin + left(当前节点如果定位了包括relative, absolute,否则,不需要这个值)
// Firefox: 当offsetPrent 为 Body 时,去掉Body的border,其它和Chrome一样
// 当offsetParent为其它元素时,和chrome行为一致
$$('offsetLeft').innerText = $$('div').offsetLeft
// 与offsetLeft算法一致,只不过需要把对应的left换成top
$$('offsetTop').innerText = $$('div').offsetTop

// 当元素作为容器时,随着滚动条移动值不断变化,chrome和firefox行为一致
// scrollLeft 表示滚动条右移时,表示右移动值,单位像素
// scrollLeft 和 scrollTop 是可写的
$$('scrollLeft').innerText = $$('div').scrollLeft
// scrollTop 表示滚动条下移时,表示下移动值,单位像素
$$('scrollTop').innerText = $$('div').scrollTop


$$('stylePadding').innerText = $$('div').style.padding // 必须定义在行内样式中,否则取不到这个值
$$('styleMargin').innerText = $$('div').style.margin // 必须定义在行内样式中,否则取不到这个值
$$('styleWidth').innerText = $$('div').style.width // 必须定义在行内样式中,否则取不到这个值
$$('styleHeight').innerText = $$('div').style.height // 必须定义在行内样式中,否则取不到这个值
}

var getOffsetParent = function (id) {
var offsetParent = $$(id).offsetParent
console.log(`offsetParent.tagName: $${offsetParent.tagName}`)
}
</script>
</head>
<body onscroll="calc()">
<div class="wrap">
<div class="div" id="div" onscroll="calc()">
<p>sdfsdfsadfsadfa<br>sdfasdfasdfasdf<br>sdfsdfsdfasdfsdfsdffffffffffffdsfasdfsadfsadfsdfsdfsadfsadfsadfsdfsd<br>ffffff士大夫撒旦法师fff<br>fffffff<br>ffffffffff<br>ffffsadf<br>sadfasdfsd<br>fsadsfa<br>sdfasdfasdf<br>asdfas<br>dfas<br>dfasdfsadfsd<br>afasdf<br>sadfsafsadf</p>

</div>
<div class="divide">
<div></div>
</div>
<div class="result">
<h2>body</h2>
<!-- xxxWidth -->
<code>body.clientWidth</code>: <span id="bodyClientWidth"></span><br>
<code>body.offsetWidth</code>: <span id="bodyOffsetWidth"></span><br>
<code>body.scrollWidth</code>: <span id="bodyScrollWidth"></span><br>
<br>
<!-- xxxHeight -->
<code>body.clientHeight</code>: <span id="bodyClientHeight"></span><br>
<code>body.offsetHeight</code>: <span id="bodyOffsetHeight"></span><br>
<code>body.scrollHeight</code>: <span id="bodyScrollHeight"></span><br>
<br>
<!-- left, top -->
<code>body.style.top</code>: <span id="bodyStyleTop"></span><br>
<code>body.style.left</code>: <span id="bodyStyleLeft"></span><br>
<br>
<!-- padding, margin, border, width, height -->
<code>body.style.padding</code>: <span id="bodyStylePadding"></span><br>
<code>body.style.margin</code>: <span id="bodyStyleMargin"></span><br>
<code>body.style.width</code>: <span id="bodyStyleWidth"></span><br>
<code>body.style.height</code>: <span id="bodyStyleHeight"></span><br>
<br>
<!-- xxxLeft, xxxTop -->
<code>body.clientLeft</code>: <span id="bodyClientLeft"></span><br>
<code>body.clientTop</code>: <span id="bodyClientTop"></span><br>
<code>body.offsetLeft</code>: <span id="bodyOffsetLeft"></span><br>
<code>body.offsetTop</code>: <span id="bodyOffsetTop"></span><br>
<code>body.scrollLeft</code>: <span id="bodyScrollLeft"></span><br>
<code>body.scrollTop</code>: <span id="bodyScrollTop"></span><br>
<br>
</div>
<div class="result">
<h2>div</h2>
<!-- xxxWidth -->
<code>div.clientWidth</code>: <span id="clientWidth"></span><br>
<code>div.offsetWidth</code>: <span id="offsetWidth"></span><br>
<code>div.scrollWidth</code>: <span id="scrollWidth"></span><br>
<code>jQuery width</code>: <span id="jqWidth"></span><br>
<code>jQuery outerWidth</code>: <span id="jqOuterWidth"></span><br>
<br>
<!-- xxxHeight -->
<code>div.clientHeight</code>: <span id="clientHeight"></span><br>
<code>div.offsetHeight</code>: <span id="offsetHeight"></span><br>
<code>div.scrollHeight</code>: <span id="scrollHeight"></span><br>
<code>jQuery height</code>: <span id="jqHeight"></span><br>
<code>jQuery outerHeight</code>: <span id="jqOuterHeight"></span><br>
<br>
<!-- left, top -->
<code>div.style.top</code>: <span id="styleTop"></span><br>
<code>div.style.left</code>: <span id="styleLeft"></span><br>
<br>
<!-- padding, margin, border, width, height -->
<code>div.style.padding</code>: <span id="stylePadding"></span><br>
<code>div.style.margin</code>: <span id="styleMargin"></span><br>
<code>div.style.width</code>: <span id="styleWidth"></span><br>
<code>div.style.height</code>: <span id="styleHeight"></span><br>
<br>
<!-- xxxLeft, xxxTop -->
<code>div.clientLeft</code>: <span id="clientLeft"></span><br>
<code>div.clientTop</code>: <span id="clientTop"></span><br>
<code>div.offsetLeft</code>: <span id="offsetLeft"></span><br>
<code>div.offsetTop</code>: <span id="offsetTop"></span><br>
<code>div.scrollLeft</code>: <span id="scrollLeft"></span><br>
<code>div.scrollTop</code>: <span id="scrollTop"></span><br>
<br>
</div>
</div>
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<script type="text/javascript">
window.onload = function () {
calc()
getOffsetParent('div')

}
</script>
</body>
</html>

额外说明

  1. 在所在容器的css属性是box-sizing: content-box(默认) 时,style.width实际上是:实际内容宽度 + 浏览器默认滚动条宽度(在chrome和firefox上测试了都等于17px),而clientWidth这时等于= 实际内容(style.width - 浏览器滚动条默认宽度) + padding(即paddingLeft + paddingRight),不包括滚动条宽度,因此此时clientWidth != style.width;在所在容器的css属性是box-sizing: border-box时,这时style.width等于offsetWidth,即实际内容宽度 + padding + border,而这时clientWidth等于 = 实际内容 + padding,但是这时实际内容时多少呢?实际内容等于 = (style.width - border - padding - 浏览器滚动条默认宽度)

  2. 在css属性是box-sizing:content-box(默认)时,jquery.width() = style.width , jquery.outerWidth() = dom.offsetWidth ; 在css属性是box-sizing:border-box时,jquery.width() = style.width - border - padding , jquery.outerWidth() = dom.offsetWidth

wordpress优化

wordpress优化主要从下面几个方面着手:

  1. wordpress头像。它自带的头像服务国内用户访问受限,因此需要一款安装插件解决这个问题:wp-user-avatar
  2. wordpress字体。自带访问google字体,由于国内被墙因此会造成很大的阻塞,影响访问速度。下载一款插件解决吧:Disable Google fonts;也可以通过代码设置的方式解决,参见这篇文章
  3. wordpress启用用户注册功能。 需要安装一款smtp的插件:easy-wp-smtp。安装完启用,设置配置项即可。需要注意的是:SMTP username是邮箱服务器的登陆名,SMTP password是开启smtp的授权码而不是你邮箱的登录密码

wordpress安装配置

今天记一下我自己使用wordpress建站的过程中遇到的一些问题。

  1. 首先下载wordpress,然后解压到wamp的www目录中,在浏览器中输入localhost/wordpress/进入wordpress安装

  2. 首先填写需要的数据库名称(事先在phpmyadmin中建一个数据库),数据库登陆名(默认root),数据库密码(默认为空),等等。

  3. 成功后,填写网站名称,管理员名称,密码,电子邮箱

  4. 安装成功后,在localhost/wordpress/wp-admin.php地址下登陆管理员用户名,密码,进入前后端预览界面。

  5. 设置头像。这里我们使用一款插件:wp-user-avatar。就要用到wordpress下载插件功能了。如果下载插件报错No working transports found,
    是因为php没有开启curl功能,修改方法:将php.ini中的;extension=php_curl.dll前的分号去掉,修改完成后,重启wamp。

  6. 插件下载完毕后启用插件,进行需要的设置即可。

  7. wordpress默认没有开启用户登录的功能,在设置中勾选允许任何人注册选项。

  8. 用户注册时,需要填写电子邮箱,然后wordpress会给该用户发送电子邮件来设置密码。所以,作为管理员,需要开启发送电子邮件的功能。这时我们需要下载一款插件:easy-wp-smtp。启用,进入设置。

  9. 首先我们得启用php的ssl功能:将php.ini中的;extension=php_openssl.dll前的分号去掉

  10. 设置插件填写用户名密码时要注意密码不是你邮箱的登陆密码,而是开启smtp的功能的授权码。不然会报错:535 authentication failed

  11. 填写设置成功后可以测试下,测试成功表明开启成功。

  12. 用户注册的时候,发送邮件给用户,用户点击邮件后提示“您的密码重设链接无效”,这其实不是wordpress的问题,邮箱收到邮件后,会将密码重置链接地址及其前后的“<>”一起当成链接地址生成超链接,点击此超链接后,由于传给wordpress的参数不对(多了个>),例如把鼠标移到下图的红色框的连接上,并看到浏览器左下角的URL提示连接,会发现多了一个“>”,所以wordpress提示密码重设链接无效。
    解决这个问题,打开wp根目录下的wp-login.php,找到如下代码:

    1
    $message .= '<' . network_site_url( "wp-login.php?action=rp&key=$key&login=" . rawurlencode( $user_login ), 'login' ) . ">\r\n";

    修改为:

    $message .= network_site_url( “wp-login.php?action=rp&key=$key&login=” . rawurlencode( $user_login ), ‘login’ ) . “\r\n”;

就是把<,>去掉。还要修改wp/wp-includes/pluggable.php文件:

$message .= '<' . network_site_url("wp-login.php?action=rp&key=$key&login=" . rawurlencode($user->user_login), 'login') . ">\r\n\r\n";

修改为:

$message .= network_site_url("wp-login.php?action=rp&key=$key&login=" . rawurlencode($user->user_login), 'login') . "\r\n\r\n";

也是把<,>去掉。

wampserver配置

** wamp = window + apache + mysql + php **

  1. wamp的默认端口是80,容易和已安装的ISS等其他服务冲突,导致wampserver无法启动,修改httpd.conf文件如下:
    搜索’80’找到Listen 80ServerName localhost:80,修改’80’为想要设置的端口重启即可。详细参见这篇文章
  2. 仅仅修改httpd.conf文件可能还不够,还需要修改wamp目录下wampmanager.tpl文件,找到Parameters: “http://localhost/”; Glyph: 5修改为Parameters: “http://localhost:8080/”; Glyph: 5(比如新端口为8080),详细参见这篇文章
  3. 修改phpmyadmin端口号:方法同2,打开wamp目录下wampmanager.tpl文件,找到Parameters: “http://localhost/phpmyadmin”; Glyph: 5修改为Parameters: “http://localhost:8080/phpmyadmin”; Glyph: 5(比如新端口为8080),详细参见这篇文章
  4. 如何修改mysql数据库的用户名密码呢?a. 通过WAMP打开mysql控制台;b. 输入use mysql;,意思是使用mysql这个数据库,提示“Database changed”就行;c. 输入要修改的密码的sql语句update user set password=PASSWORD('hooray') where user='root';; d. 输入flush privileges;; e. 输入quit;退出。详细参见这篇文章。做完了这些关闭wampserver,重新打开可能你还是不能进入phpmyadmin,因为你还需要把新密码手动加入到phpmyadmin配置文件中,位置在wamp/apps/phpadmin/config.inc.php,打开配置文件把密码写进去重启wamp就可以了。详细参见这篇文章
  5. wamp安装完成后,等图标变绿后,在浏览器输入127.0.0.1即可进入默认页面,输入localhost呢,如果不可以需要配置httpd.conf文件,如下图:
    允许all
    修改完后,重启wamp,在浏览器中输入localhost应该可以正常访问。访问localhost/phpmyadmin/时依然无法访问,但是访问127.0.0.1/phpmyadmin可以,需要配置wamp目录下的alias目录phpmyadmin.conf文件:
    允许all
  6. 一般情况下,我们把代码和程序分开,wamp默认的www目录在安装程序目录下,所以我们需要自定义www目录。还是修改httpd.conf目录,如下图:
    自定义www目录
    自定义www目录
    我把目录改为d盘下的www目录,重启后输入localhost,应该可以进入d:/www这个目录的index.php文件。但是点击wamp图标的www还是显示默认wamp下面的www目录,因此还需要修改两个文件wamp/wampmanager.iniwampmanager.tpl
    自定义www目录
    自定义www目录
    关闭wamp,重新打开,在点击www目录,应该就是修改后的目录了。在浏览器地址栏输入localhost也会进入修改后的目录的index.php

npm知识

设置npm的registry

  1. 国内访问官方registry非常慢,因此选择国内第三方镜像(淘宝镜像,良心)
    1
    npm config set registry https://registry.npm.taobao.org
    查看是否设置成功:
    1
    npm get registry
  2. 查找全局安装的包
    1
    npm ls -g
    或者
    1
    npm ls -g --depth 0
  3. 理解package.json文件中的~^
    ~ 表示匹配中间数字,比如:~1.2.3将会匹配所有1.2.x版本,但是不会匹配1.3.0
    ^ 表示匹配第一个数字,比如:^1.2.3将会匹配所有1.x.x版本,但是不会匹配2.0.0
    总结一下:
    ~1.2.3: >= 1.2.3 <** 1.3.0
    ^1.2.3: **>=
    1.2.3 < 2.0.0
    ^符号时npm安装插件的默认符号

git使用心得

平时经常使用github,clone别人的项目或者自己写项目上传到github都或多或少用到git,但是一段时间不用了又忘了命令,总要浪费些时间查资料,不胜其烦,好记性不如烂笔头,今天我想自己总结一下,加深记忆,方便快速查阅,下面不啰嗦直接上代码:

  1. 创建SSH Key。在用户主目录下,看看有没有.ssh目录,如果有,再看看这个目录下有没有id_rsaid_rsa.pub这两个文件,如果已经有了,不需要创建了。如果没有,执行此命令,创建SSH Key。

    1
    $ ssh-keygen -t rsa -C "youremail@example.com"
  2. git初始化,让当前目录处理git管理之下

    1
    $ git init
  3. 添加文件。file可以是具体文件名,也可以是文件夹,甚至可以是.(表示add当前所有文件),post/*(表示post目录下的所有文件),post/*.js(表示post目录下的所有js文件)

    1
    $ git add <file>
  4. 查看当前状态,一般系统会提示下一步的操作。比如是否需要commit , push 等操作

    1
    $ git status
  5. 提交文件到版本库,可以输入提交信息

    1
    $ git commit -m "message"
  6. 打印命令可以查看历史记录,git log命令查看的更详细,git log --pretty=oneline命令更直观

    1
    2
    3
    4
    $ git log
    $ git log --pretty=oneline
    $ git log --pretty=oneline --abbrev-commit
    $ git log --graph
  7. 有时候需要回到上一个版本或者指定版本需要用到这个命令。HEAD表示当前版本,HEAD^表示上一个版本,上上个版本就是HEAD^^,当前版本往上100个版本就是HEAD~100。当前这里也可以使用提交的id,eg. 3628164...882e1e0,一般取前6位就可以了

    1
    $ git reset --hard HEAD^
  8. 有时候版本切回之前的版本了,但是又想恢复到现在的版本,那如何知道现在的版本号呢?别着急,git reflog帮你排除忧虑,这个命令记录了你之前所有版本记录

    1
    $ git reflog
  9. 可以丢弃(discard)工作区的修改,或者已经使用过git add命令后又修改了工作区的内容但是想丢弃,回到git add时的状态。
    即回到上一次git add或者git commit的状态。如果你git add了一些资料,但是还没有git commmit,这时你想丢弃暂存区的内容,可以使用命令git reset HEAD <file>把内容打回到工作区,再使用命令git checkout -- <file>把工作区的内容丢弃。如果你已经使用了git commit提交了内容,但是又反悔了,可以使用git reset --hard HEAD^返回上一个版本

    1
    $ git checkout -- <file>
  10. 删除文件。如果删除了文件,又反悔了,因为此时没有存储到暂存区,所以可以使用git checkout -- <file>恢复。

    1
    $ rm <file>
  11. 删除文件。注意和rm <file>的区别,git rm <file>相当于rm <file> + git add <file>。因此若想恢复之前的状态,需要先执行git reset HEAD <file>打回到工作区,再执行git checkout -- <file>

    1
    $ git rm <file>
  12. 这条命令是把本地库和github上的远程库建立关联。这个是采用ssh的方式,所以github上的ssh配置里面需要有你本地的ssh公钥。后面的chenweiyi/gitTest是你的github账号名和项目名。

    1
    $ git remote add origin git@github.com:chenweiyi/gitTest.git
  13. 当你首次提交文件到github时,需要输入这条命令。push推,表示推送到github;origin远程仓库名;master分支名;-u表示把本地master和远程仓库的master分支关联起来,以后再推送或者拉取时可以简化命令git push origin master / git pull

    1
    $ git push -u origin master
  14. 从远程仓库中克隆项目到本地。当你执行这条命令时,git自动把本地master分支和远程库的master分支对应起来,远程库默认仓库名时origin

    1
    $ git clone git@github.com:chenweiyi/gitTest.git
  15. 查看当前项目分支

    1
    $ git branch
  16. 创建名为name的分支

    1
    $ git branch <name>
  17. 切换到名为name的分支

    1
    $ git checkout <name>
  18. 创建并切换到名为name的分支

    1
    $ git checkout -b <name>
  19. 合并名为name的分支到当前分支

    1
    $ git merge <name>
  20. 删除名为name的分支

    1
    $ git branch -d <name>
  21. 查看远程库的信息,$ git remote -v 查看更详细的信息

    1
    $ git remote
  22. 推送名为branchName分支到远程库

    1
    $ git push origin <branchName>
  23. 拉去远程分支到本地。当你从远程库克隆时,默认情况下只能看到本地master分支。可以用git branch命令查看下。现在要看到其他分支就必须使用这个命令创建。比如git checkout -b dev origin/dev就能把远程dev分支创建到本地

    1
    $ git checkout -b <branchName> origin/<branchName>
  24. 与远程库创建连接后,即可使用此命令拉取远程库分支内容到本地。如果拉取失败,则可根据提示使用git branch --set-upstream <branchName> origin/<branchName> 来指定本地分支branchName与远程分支的连接。

    1
    $ git pull
  25. 首先切换到需要打标签的分支,再打标签。默认标签是打在最新提交的commit上的。有时候,如果忘了打标签,比如,现在已经是周五了,但应该在周一打的标签没有打,怎么办?方法是找到历史提交的commit id,然后打上就可以了。eg. $ git tag v0.9 6224937

    1
    2
    $ git checkout <branchName>
    $ git tag <tagName>
  26. 查看所有的标签。注意,标签不是按时间顺序列出,而是按字母排序的。可以使用git show <tagName>查看标签信息。

    1
    $ git tag
  27. 可以创建带有说明的标签,-a 指定标签名 ,-m 指定标签信息 ,commitId 版本id

    1
    $ git tag -a <tagName> -m <message> [commitID]
  28. 标签打错了可以删除标签

    1
    git tag -d <tagName>
  29. 创建的标签都只存储在本地,不会自动推送到远程 ,使用此命令推送到远程。或者一次性推送本地尚未推送到远程的所有标签,命令:git push origin --tags

    1
    git push origin <tagName>
  30. 如果标签已经推送到远程,要删除远程标签就麻烦一点,先从本地删除:git tag -d v1.0。然后再使用该命令:git push origin :refs/tags/v1.0

    1
    git push origin :refs/tags/<tagName>
  31. 保存代码现场

    1
    git stash
  32. 查看所有的stash

    1
    git stash list
  33. 恢复指定的stash

    1
    2
    3
    git stash apply stash@{x}
    或者恢复最近的stash
    git stash pop
  34. 删除stash

    1
    git stash drop
  35. 有时候合并分支会出现界面被锁住的情况,这时处理方法: 先按 ESC,再输入 :wq,按一下 Enter 键即可退出

  36. 出现 warning: LF will be replaced by CRLF in ... 提示时,设置 git config --global core.autocrlf false 或者 git config --global core.safecrlf false 消除提示

  37. 有时.gitignore配置文件不生效,原因是某些文件已经被纳入了版本管理中,做法是删除本地缓存文件 git rm -r --cached ,然后重新添加文件到仓库中 git add xxx git commit -m 'xxx'

  38. 本地创建的项目,需要推送到github或者码云上去,首先,你要在github或者码云上创建一个项目,例如名称为projectX,然后使用git init初始化本地工程,再次使用(以码云为例)git remote add origin git@gitee.com:cwy7/projectX.git,然后拉去远程项目使用git pull --rebase origin master,然后检查下状态 git status 以继续下一步操作

  39. 查看某一个命令的使用说明

    1
    git <command> --help